Receiving HS256 signing algorithm instead of RS256

I have a web app and web API both written in ASP .Net Core 1.1 MVC web app using Auth0 authentication server. After logging in (vis the Auth0 login form), when I try retrieve the access token in my controller before calling a web API, the token is incorrect. Here is how I retrieve the token:

So this is the code

public async Task<IActionResult> Index()
{
    string accessToken = User.Claims.FirstOrDefault(c => c.Type == "access_token")?.Value;
    if (accessToken == null || accessToken == "")
                    return View("Error");
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    HttpResponseMessage responseMessage = await client.GetAsync(inspectionsUrl);
    if (responseMessage.IsSuccessStatusCode)
    {
        var responseData = responseMessage.Content.ReadAsStringAsync().Result;
        List<Inspection> inspections = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Inspection>>(responseData);
        return View(inspections);
    }
    return View("Error");

}

The “access_token” is fLnLKtdjsP4QYwm7 which is too short.
However, I also see there is an “id_token” which looks more correct, but still wrong. You can view both in the screenshot below:


http://mysql1.propworx.co.za/propworxsetup/screenshot.png

Now, if I copy and paste the id_token into https://jwt.io/ it tell me the algorithm is HS256. But this is strange as I have both my web app and web API set to RS256 on the Auth0 settings page.

I’ve been adding things to my Startup.cs file to try and figure out what is going on, so it’s become quite long, but here it is in case…

public class Startup
    {
        public IConfigurationRoot Configuration { get; }
        public IHostingEnvironment HostingEnvironment { get; }

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

            if (env.IsDevelopment())
                builder.AddUserSecrets<Startup>();
            builder.AddEnvironmentVariables();
            Configuration = builder.Build();
            HostingEnvironment = env;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            // Add authentication services
            services.AddAuthentication(
                options => options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
            services.AddMvc();
            services.AddOptions();
            services.Configure<Auth0Settings>(Configuration.GetSection("Auth0"));
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<Auth0Settings> auth0Settings)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

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

            app.UseStaticFiles();

            //Set up JWT Bearer authentication first
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                Audience = auth0Settings.Value.ApiIdentifier,
                Authority = $"https://{auth0Settings.Value.Domain}"
            });

            // Add the cookie middleware
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,

                Events = new CookieAuthenticationEvents()
                {
                    OnRedirectToLogin = ctx =>
                    {
                        // if it is an ajax / api request, don't redirect to login page.
                        if (!(IsAjaxRequest(ctx.Request) || IsApiRequest(ctx.Request)))
                        {
                            ctx.Response.Redirect(ctx.RedirectUri);
                            return Task.CompletedTask;
                        }
                        ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        return ctx.Response.WriteAsync("Unauthorized");
                    }
                }
            });

            // Get the client secret used for signing the tokens
            var keyAsBytes = Encoding.UTF8.GetBytes(auth0Settings.Value.ClientSecret);

            // if using non-base64 encoded key, just use:
            //var keyAsBase64 = auth0Settings.Value.ClientSecret.Replace('_', '/').Replace('-', '+');
            //var keyAsBytes = Convert.FromBase64String(keyAsBase64);

            var issuerSigningKey = new SymmetricSecurityKey(keyAsBytes);

            // Add the OIDC middleware
            var options = new OpenIdConnectOptions("Auth0")
            {
                // Set the authority to your Auth0 domain
                Authority = $"https://{auth0Settings.Value.Domain}",

                // Configure the Auth0 Client ID and Client Secret
                ClientId = auth0Settings.Value.ClientId,
                ClientSecret = auth0Settings.Value.ClientSecret,

                // Do not automatically authenticate and challenge
                AutomaticAuthenticate = false,
                AutomaticChallenge = false,

                // Set response type to code
                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 
                CallbackPath = new PathString("/signin-auth0"),

                // Configure the Claims Issuer to be Auth0
                ClaimsIssuer = "Auth0",

                // The UserInfo endpoint does not really return any extra claims which were not returned in the original auth response, so
                // we can save ourselves from making an extra request
                GetClaimsFromUserInfoEndpoint = false,

                // Saves tokens to the AuthenticationProperties
                SaveTokens = true,

                Events = new OpenIdConnectEvents
                {
                    OnTicketReceived = 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;
                    },

                    // handle the logout redirection 
                    OnRedirectToIdentityProviderForSignOut = (context) =>
                    {
                        var logoutUri = $"https://{auth0Settings.Value.Domain}/v2/logout?client_id={auth0Settings.Value.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;
                    }
                },

                // manually setup the signature validation key
                TokenValidationParameters = new TokenValidationParameters
                {
                    IssuerSigningKey = issuerSigningKey
                }
            };

            options.Scope.Clear();
            options.Scope.Add("openid");
            options.Scope.Add("name");
            options.Scope.Add("email");
            options.Scope.Add("picture");
            options.Scope.Add("country");
            options.Scope.Add("roles");
            options.Scope.Add("read:inspections");
            app.UseOpenIdConnectAuthentication(options);

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

        private static bool IsAjaxRequest(HttpRequest request)
        {
            var query = request.Query;
            if ((query != null) && (query"X-Requested-With"] == "XMLHttpRequest"))
            {
                return true;
            }
            IHeaderDictionary headers = request.Headers;
            return ((headers != null) && (headers"X-Requested-With"] == "XMLHttpRequest"));
        }

        private static bool IsApiRequest(HttpRequest request)
        {

            return request.Path.StartsWithSegments(new PathString("/api"));
        }
    }

The short access token means that this authentication response is for an authentication request that did not specified that it wanted to obtain an access token one of your configured API’s, more specifically, the request did not include an audience parameter and a default audience is also not configured.

In these situation, the access token is not usable to call your own API’s and as such does not need to be a JWT. In relation to the ID token being signed as HS256 although you say that you changed that setting. The most simple explanation would be that (bear with me, as this also happened to me a couple of time) you did not save the changes. Can you ensure that the settings were indeed saved and try to obtain a new ID token performing a new authentication process?

Thanks for the reply jmangelo.

I unfortunately only saw your reply now, after I posted another question. I thought perhaps that short access token was in fact not an access token at all, but an authorization code, to be later exchanged for an access token. That is, after all, what I am trying to achieve, as I believe the correct flow for a regular web app (mine is written in ASP .Net Core 1.1 MVC) is the “Authorization Code Grant Flow”?

Regarding not specifying the audience, if you look at my Startup.cs code above, on line 25:

//Set up JWT Bearer authentication first
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    Audience = auth0Settings.Value.ApiIdentifier,
    Authority = $"https://{auth0Settings.Value.Domain}"
});

I’m not very clued up with all of this, but would that not do the trick?

I wish Auth0 had sample code for an ASP .Net Core app calling a web API :frowning:

I double checked, and the “auth0Settings.Value.ApiIdentifier” does have the correct value (http://inspections.propworx.co.za/api/) and Authority is https://codexcreations.eu.auth0.com

I think the problem is that in my web app controller, when I read the access_token, I should (I think) be reading the authorization code? Which I will in turn exchange for an access token? But if I look at my “User.Claims” object, it has “access_token”, “id_token”, etc. but not “code”. Unless the authorization has already been exchanged for an access token in the background (by some middleware)? That’s the problem with not fully understanding what I’m doing :frowning: For example, at which point does User.Claims get populated? Never thought I’d struggle so much with this… :confused:

In my Configure() method in Startup.cs, I do tell it to use “authentication code” response:

        // Add the OIDC middleware
        var options = new OpenIdConnectOptions("Auth0")
        {
            // Set the authority to your Auth0 domain
            Authority = $"https://{auth0Settings.Value.Domain}",

            // Configure the Auth0 Client ID and Client Secret
            ClientId = auth0Settings.Value.ClientId,
            ClientSecret = auth0Settings.Value.ClientSecret,

            // Do not automatically authenticate and challenge
            AutomaticAuthenticate = false,
            AutomaticChallenge = false,

            // Set response type to code
            ResponseType = OpenIdConnectResponseType.Code,
            .....

So I guess maybe that is swapping the code for a token in the background? But why is the token so short? Probably like you mentioned, it’s because I’m not specifying an audience (i.e. my API) but I am… unless I’m doing it incorrectly?

        //Set up JWT Bearer authentication first
        app.UseJwtBearerAuthentication(new JwtBearerOptions
        {
            Audience = auth0Settings.Value.ApiIdentifier,
            Authority = $"https://{auth0Settings.Value.Domain}/"
        });