Switch between passwordless and password in universal login from ASP.Net Core Open Id Connect

Hi,

We are currently using the universal login, which is configured to be password based using lock js.

What we want to do is also be able to configure this also to switch to passwordless based on some parameter or configuration setting coming in, to support a free trial customer flow.

We currently have openid connect set up in asp.net web api which takes care of things under the covers, but I want to be able to tell universal login from open id connect within the web api to switch to passwordless, based on some kind of parameter, e.g. to tell which auth0 connection to use.

Is there a way to do this? I included relevant code samples below, if i’m missing something let me know.
Any help you can give will be appreciated.

More detail

  1. we call into our web api auth controller, from a vue based link:
[AllowAnonymous]
        [Route("login")]
        public async Task Login(string returnUrl = "/home")
        {
            await HttpContext.ChallengeAsync("Auth0", new AuthenticationProperties() { RedirectUri = "/auth/success" });
        }
  1. The universal login is triggered via the open id connect mechanism, as configured below in startup.cs for our api layer in ASP.Net core, v2.2. at present.
    (names changed to protect the innocent):
        
public void ConfigureServices(IServiceCollection services)
        {
          //... detail omitted for brevity.
   services.Configure<CookiePolicyOptions>(options =>
           {
               options.CheckConsentNeeded = context => true;
               options.MinimumSameSitePolicy = SameSiteMode.None;
           });

            services.AddAuthentication(options =>
               {
                   options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                   options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                   options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               })
                .AddCookie(options =>
               {
                   options.LoginPath = new PathString("/auth/login");
                   options.AccessDeniedPath = new PathString("/auth/login");
                   options.LogoutPath = new PathString("/auth/logout");

               })
                .AddOpenIdConnect("Auth0",
                    options =>
                    {
                        options.Authority = $"https://{auth0Configuration.Domain}";
                        options.ClientId = auth0Configuration.ClientId;
                        options.ClientSecret = auth0Configuration.ClientSecret;

                        options.ResponseType = "code";
                        options.Scope.Clear();
                        options.Scope.Add("openid");

                        options.CallbackPath = new PathString("/auth/callback");

                        options.ClaimsIssuer = "Auth0";

                        options.TokenValidationParameters = new TokenValidationParameters
                        {
                            NameClaimType = "name"
                        };

                        options.Events = new OpenIdConnectEvents
                        {
                            OnRedirectToIdentityProviderForSignOut =
                            (ctx) => ProcessSignOutRedirect(ctx, auth0Configuration),
                            OnRedirectToIdentityProvider = CustomiseAuthenticationRequest,
                            OnTokenValidated = AddCustomClaims,
                            OnRemoteFailure = HandleRemoteFailure
                        };
                    });
//... etc
}

I’ve included the events we currently work with.

ProcessSignoutRedirect:

private Task ProcessSignOutRedirect(RedirectContext context, Auth0Configuration configuration)
        {
            var logoutUri =
                $"https://{configuration.Domain}/v2/logout?client_id={configuration.ClientId}";

            var postLogoutUri = context.Properties.RedirectUri;
            if (!string.IsNullOrEmpty(postLogoutUri))
            {
                if (postLogoutUri.StartsWith("/"))
                {
                    // transform to absolute
                    var request = context.Request;
                    postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
                }

                logoutUri += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}";
            }

            context.Response.Redirect(logoutUri);
            context.HandleResponse();

            return Task.CompletedTask;
        }

CustomiseAuthenticationRequest:

        private Task CustomiseAuthenticationRequest(RedirectContext context)
        {
            return Task.FromResult(0);
        }

AddCustomClaims:

 private static Task AddCustomClaims(TokenValidatedContext ctx)
 {
            //see example:
            // https://joonasw.net/view/adding-custom-claims-aspnet-core-2
            var claimId = ctx.Principal.Claims.First(x => x.Type == ClaimTypes.NameIdentifier);
           // our services needed for the custom claims
            var service = ctx.HttpContext.RequestServices.GetService<IUserRegistrationService>();
            var lookupService = ctx.HttpContext.RequestServices.GetService<IUserLookUpService>();
     
            try
            {
                var claims = new List<Claim>();

                if (!service.UserExists(claimId.Value))
                {
                    OrganisationType organisationType;

                    
                    var claimOrganisationType = ctx.Principal.Claims.FirstOrDefault(x => x.Type == ORGANISATION_TYPE);

                    if (claimOrganisationType == null || !Enum.TryParse(claimOrganisationType.Value, out organisationType))
                    {
                        organisationType = OrganisationType.TypeA;
                    }

                    service.RegisterNewUser(claimId.Value, organisationType);
                }

                var userId = lookupService.GetInternalUserId(claimId.Value);
                claims.Add(new Claim(CustomClaimTypes.INTERNAL_USER_ID, userId.ToString(), "Guid"));


                var passwordLess = ctx.Principal.Claims.FirstOrDefault(x => x.Type == IS_PASSWORDLESS);
                claims.Add(new Claim("IsPasswordLess",passwordLess?.Value??""));
                var appIdentity = new ClaimsIdentity(claims);
                ctx.Principal.AddIdentity(appIdentity);
            }
            catch (Exception e)
            {
                ctx.Fail($"An error occurred on token validated: {e}");
            }

            return Task.CompletedTask;
 }

HandleRemoteFailure:

private Task HandleRemoteFailure(RemoteFailureContext context)
{
            context.HandleResponse();

            context.Response.Redirect("/");

            return Task.FromResult(0);
}
  1. Universal login code - note this is not exactly how it will look when finished:
///CSS removed for brevity, and example 

<script src="https://cdn.auth0.com/js/lock/11.15/lock.min.js"></script>
  <script>
    // Decode utf8 characters properly
    var config = 
    JSON.parse(decodeURIComponent(escape(window.atob('@@config@@'))));
    config.extraParams = config.extraParams || {};
    var connection = config.connection;
    var prompt = config.prompt;
    var languageDictionary;
    var language;

    if (config.dict && config.dict.signin && config.dict.signin.title) {
      languageDictionary = { title: config.dict.signin.title };
    } else if (typeof config.dict === 'string') {
      language = config.dict;
    }
    var loginHint = config.extraParams.login_hint;
    var colors = config.colors || {};

    // this is to add passwordless
    var passwordLessLock = new Auth0LockPasswordless(config.clientID, config.auth0Domain, {
    passwordlessMethod: "link",              // Sets Lock to use magic link
    responseType: 'token id_token',
    auth: {
      redirectUrl: config.callbackURL,    // If not specified, defaults to the current page 
      params: {
        scope: 'openid email'                // Learn about scopes: https://auth0.com/docs/scopes
      }          
    }
  });
    // Available Lock configuration options: https://auth0.com/docs/libraries/lock/v11/configuration
    var lock = new Auth0Lock(config.clientID, config.auth0Domain, {
      /// configuration omitted for brevity
    }
	//want to wrap in an if statement, e.g. 
	if (connection === 'email') 
          passwordLessLock.show(); 
    else
          lock.show();
    
  </script>
</body>
</html>
1 Like