Retrieving Name in OnTicketReceived is Null when using Embedded Lock on .NET CORE

I’m trying to use the tutorial from for the Embedded lock mixed with the tutorial to get the users Profile info in the Nav Bar.

It seems the options for the OpenIdConnectOptions in the Embedded tutorial are in the ConfigureService function, but the OpenIdConnectOptions for User Profile are in the Configure function (inside of Startup.cs).

When I try to port over the code into the existing ConfigureService code from the first tutorial, the OnTicketReceived fires, but context.Properties.Items doesn’t ContainsKey(“.TokenNames”)), its just null.

If I do the opposite, and use the code from the ConfigureServices to the Configure, what happens is the Auth0Extensions from the Embedded code throws a null error when trying to set the NONCE to the cookie.

  httpContext.Response.Cookies.Append(
            OpenIdConnectDefaults.CookieNoncePrefix + options.StringDataFormat.Protect(nonce),
            NonceProperty,
            new CookieOptions
            {
                HttpOnly = true,
                Secure = httpContext.Request.IsHttps,
                Expires = DateTime.UtcNow + options.ProtocolValidator.NonceLifetime
            });

Specifically, options.StringDataFormat is null.

What is the proper way to get the user profile information with an embedded login?

Maybe a better question to ask is - why are the samples different? Why do we have to configure options inside of ConfigureService for using the Embedded function, and why do we have to use it inside of just Configure when not?

Anyone have an idea? I can’t be the only one to want to do this …

SMALL UPDATE:

Update:

Using your EmbeddedLock code, and pasting in the OnTicketReceived event from the example on UserProfile only returns 5 Claims.

Imgur

Where as on the Example with the external Lock, the claim comes back as expected …

Imgur

No one wants to Embed the Lock into their own .NET project?

Well, I figured out the solution.

Basically the example for EmbeddedLock doesn’t include the “SaveTokens” attribute inside of the OpenIdConnectOptions connection options and the tutorials don’t tell you that you need to do this.

You will also have to add the Scope options in order to get the server to send you them.

      options.Scope.Clear();
        options.Scope.Add("openid");
        options.Scope.Add("name");
        options.Scope.Add("email");
        options.Scope.Add("picture");

Here is my final Startup.cs file in case you waste 2 days of waiting for support on this like I did…

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Security.Claims;
using System.Threading.Tasks;

namespace SampleMvcApp
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add authentication services
            services.AddAuthentication(
                options => options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);

            // Configure OIDC
            services.Configure<OpenIdConnectOptions>(options =>
            {
                // Specify Authentication Scheme
                options.AuthenticationScheme = "Auth0";

                // Set the authority to your Auth0 domain
                options.Authority = $"https://{Configuration"auth0:domain"]}";

                // Configure the Auth0 Client ID and Client Secret
                options.ClientId = Configuration"auth0:clientId"];
                options.ClientSecret = Configuration"auth0:clientSecret"];

                // Do not automatically authenticate and challenge
                options.AutomaticAuthenticate = false;
                options.AutomaticChallenge = false;

                // Set response type to code
                options.ResponseType = "code";

                // Set the callback path, so Auth0 will call back to http://localhost:5000/signin-auth0 
                // Also ensure that you have added the URL as an Allowed Callback URL in your Auth0 dashboard 
                options.CallbackPath = new PathString("/signin-auth0");

                // Configure the Claims Issuer to be Auth0
                options.ClaimsIssuer = "Auth0";

                options.SaveTokens = true;

                options.Events = new OpenIdConnectEvents
                {
                    // handle the logout redirection 
                    OnRedirectToIdentityProviderForSignOut = HandleRedirectToIdentityProviderForSignOut,
                    OnTicketReceived = OnTicketReceived
                };

                options.Scope.Clear();
                options.Scope.Add("openid");
                options.Scope.Add("name");
                options.Scope.Add("email");
                options.Scope.Add("picture");

            });

            // Add framework services.
            services.AddMvc();

            // Add functionality to inject IOptions<T>
            services.AddOptions();

            // Add the Auth0 Settings object so it can be injected
            services.Configure<Auth0Settings>(Configuration.GetSection("Auth0"));
        }

        public Task OnTicketReceived(TicketReceivedContext context)
        {
            // Get the ClaimsIdentity
            var identity = context.Principal.Identity as ClaimsIdentity;
            if (identity != null)
            {
                // Add the Name ClaimType. This is required if we want User.Identity.Name to actually return something!
                if (!context.Principal.HasClaim(c => c.Type == ClaimTypes.Name) &&
                    identity.HasClaim(c => c.Type == "name"))
                    identity.AddClaim(new Claim(ClaimTypes.Name, identity.FindFirst("name").Value));

                // Check if token names are stored in Properties
                if (context.Properties.Items.ContainsKey(".TokenNames"))
                {
                    // Token names a semicolon separated
                    string] tokenNames = context.Properties.Items".TokenNames"].Split(';');

                    // Add each token value as Claim
                    foreach (var tokenName in tokenNames)
                    {
                        // Tokens are stored in a Dictionary with the Key ".Token.<token name>"
                        string tokenValue = context.Properties.Items$".Token.{tokenName}"];

                        identity.AddClaim(new Claim(tokenName, tokenValue));
                    }
                }
            }

            return Task.CompletedTask;
        }

        public Task HandleRedirectToIdentityProviderForSignOut(RedirectContext context)
        {
            var logoutUri = $"https://{Configuration"auth0:domain"]}/v2/logout?client_id={Configuration"auth0: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;
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<OpenIdConnectOptions> oidcOptions)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            // Add the cookie middleware
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AutomaticAuthenticate = true,
                AutomaticChallenge = true
            });
            
            // Add the OIDC middleware
            app.UseOpenIdConnectAuthentication(oidcOptions.Value);

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

The other thing you should know is that the “Scope” is hard coded in the Embedded Lock example, instead of it being passed by the model. That means regardless if you set your scope in Code on the model options, you will also have to set it in the Javascript embed code.

            <script src="https://cdn.auth0.com/js/lock/10.12.1/lock.min.js"></script>
            <script>

                  var lock = new Auth0Lock('@Model.ClientId', '@Model.Domain', {
                    container: 'root',
                    auth: {
                      redirectUrl: '@Model.CallbackUrl',
                      responseType: 'code',
                      params: {
                        **scope: 'openid name picture email',**
                        state: '@Model.State' ,
                        nonce: '@Model.Nonce'
                      }
                    }
                  });

                lock.show();
            </script>