Implementing a Redirect with Actions and Passing Data Back to Auth0

Problem statement

How can data be passed back to Auth0 when implementing a Redirect with Actions? The documentation on Pass information back to the Action includes the following:

A signed session token should be used to send sensitive information back to Auth0.
The token will be validated to ensure that:

  • The signature is valid.
  • The token is not expired.
  • The state claim within the token matches the state parameter used as part of the redirect.

To avoid replay attacks, the token should be sent back to Auth0 by making a POST request to the /continue endpoint. The tokenParameterName option in the code allows you to specify the name of the field that contains your token.

Is there an example of how to implement this on the application side?

Steps to reproduce

Create a post-login Action in your Auth0 tenant. You can use the following code as a guide:

exports.onExecutePostLogin = async (event, api) => {

      // Craft a signed session token
  const session_token  = api.redirect.encodeToken({
    secret: "RANDOM-256-BITS-SECRET",
    expiresInSeconds: 500,
    payload: {
      sub: event.user.user_id,
      continue_uri: `https://YOUR-AUTH0-DOMAIN/continue`     
    },
  });

  // Send the user to your application along
  // with a `session_token` query string param including
  // the email.

  console.log("Redirecting outside of Actions");

  // For the Action to work with the ASP.NET Core MVC app, you need to change the redirection to "http://localhost:3000/Auth/Redirect" instead
  api.redirect.sendUserTo("http://localhost:3000/redirect"", {
    query: { session_token }
  });

};


/**
* Handler that will be invoked when this action is resuming after an external redirect. If your
* onExecutePostLogin function does not perform a redirect, this function can be safely ignored.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
 exports.onContinuePostLogin = async (event, api) => {  
    console.log("validating token" );
    try{    
      const payload = api.redirect.validateToken({
      secret: "RANDOM-256-BITS-SECRET",
      tokenParameterName: 'session_token'
    });
    console.log("payload", payload);
    }catch(e){
      console.log("error --->", e);
    }
 };

Solution

The following is an example of how to implement this within an application.

Regular Web App

Parting from the ASP.NET Core MVC quickstart, the following changes were made to get the Action redirection working in a test app:

  1. Create a Controller: Create a controller, for example, AuthController, and add an action named Redirect
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using SampleMvcApp.ViewModels;
using System.Security.Claims;

namespace SampleMvcApp.Controllers
{
    public class AuthController : Controller
    {
        private readonly IConfiguration _configuration;

        public AuthController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public IActionResult Redirect(string state, string session_token)
        {
            try
            {
                var tokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = _configuration["Jwt:Issuer"],
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"])),
                    ClockSkew = TimeSpan.Zero
                };

                var handler = new JwtSecurityTokenHandler();
                var jsonToken = handler.ReadToken(session_token) as JwtSecurityToken;

                if (jsonToken == null)
                {
                    throw new SecurityTokenException("Invalid JWT token");
                }

                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Issuer = jsonToken.Issuer,
                    Expires = jsonToken.ValidTo,
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"])), SecurityAlgorithms.HmacSha256),
                    Subject = new ClaimsIdentity(new[]
                    {
                        new Claim("state", state),
                        new Claim("sub", jsonToken.Subject)
                    })
                };

                var token = handler.CreateToken(tokenDescriptor);

                return View("Redirect", new RedirectViewModel
                {
                    State = state,
                    SessionToken = handler.WriteToken(token)
                });
            }
            catch (Exception ex)
            {
                // Handle exception
                Console.WriteLine(ex);
                throw new Exception("Token could not be decoded", ex);
            }
        }
    }
}
  1. Create a ViewModel: Create a ViewModel class to hold the data to be passed to the view.
using System;
namespace SampleMvcApp.ViewModels
{
    public class RedirectViewModel
    {
        public string State { get; set; }
        public string SessionToken { get; set; }
    }

}
  1. Create a View: Create a view file named Redirect.cshtml in the Views/Auth folder (or any other folder you prefer).
@using SampleMvcApp.ViewModels
@model RedirectViewModel

<h1>Loading...</h1>

<form id="redirectForm" method="post" action="https://YOUR-AUTH0-DOMAIN/continue?state=@Model.State">;
    <input type="hidden" name="state" value="@Model.State" />
    <input type="hidden" name="session_token" value="@Model.SessionToken" />
</form>

<script>document.getElementById('redirectForm').submit();</script>
  1. Configure JWT Settings in appsettings.json: Ensure you have the JWT settings configured in your appsettings.json file.
{
...
"Jwt": {
    "Issuer": "YOUR-AUTH0-DOMAIN",
    "SecretKey": "RANDOM-256-BITS-SECRET"
  }
}

That should be enough to get the Action Redirect working.