ASP.NET Core 2.0 AzureAD Authentication not working

I have an ASP.NET Core 2.0 application setup that I want to use AzureAd for the authentication with my company’s directory. I have setup the classes and startup method and have the authentication piece working, the problem that I’m having is that I’m trying to setup and event handler to the OnAuthorizationCodeReceived event, so that I can request a user token that will then be used for Microsoft graph calls.

In my Startup.cs I have the following code

public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddAzureAd(options => Configuration.Bind("AzureAd", options))
            .AddCookie();

            services.AddMvc();

            services.AddSingleton(Configuration);
            services.AddSingleton<IGraphAuthProvider, GraphAuthProvider>();
            services.AddTransient<IGraphSDKHelper, GraphSDKHelper>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseAuthentication();

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

Then in the AzureAdAuthenticationBuilderExtensions.cs I have the following code.
public static class AzureAdAuthenticationBuilderExtensions
{        
    public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, IConfiguration configuration)
        => builder.AddAzureAd(_ => { }, configuration);

    public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions, 
        IConfiguration configuration)
    {
        builder.Services.Configure(configureOptions);
        builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, ConfigureAzureOptions>();
        builder.AddOpenIdConnect(opts =>
        {
            opts.ResponseType = "code id_token";

            opts.ClientId = configuration["AzureAd:ClientId"];
            opts.Authority = $"{configuration["AzureAd:Instance"]}{configuration["AzureAd:TenantId"]}";
            opts.UseTokenLifetime = true;
            opts.CallbackPath = configuration["AzureAd:CallbackPath"];
            opts.ClientSecret = configuration["AzureAd:ClientSecret"];
            opts.RequireHttpsMetadata = false;

            opts.Events = new OpenIdConnectEvents
            {
                OnAuthorizationCodeReceived = async context =>
                {
                    var credential = new ClientCredential(context.Options.ClientId, context.Options.ClientSecret);

                    var distributedCache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
                    var userId = context.Principal
                        .FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
                        .Value;
                    var cache = new AdalDistributedTokenCache(distributedCache, userId);
                    var authContext = new AuthenticationContext(context.Options.Authority, cache);
                    await authContext.AcquireTokenByAuthorizationCodeAsync(context.TokenEndpointRequest.Code,
                        new Uri(context.TokenEndpointRequest.RedirectUri, UriKind.RelativeOrAbsolute), credential, context.Options.Resource);
                    context.HandleCodeRedemption();
                }
            };
        });
        return builder;
    }

    private class ConfigureAzureOptions: IConfigureNamedOptions<OpenIdConnectOptions>
    {
        private readonly AzureAdOptions _azureOptions;

        public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
        {
            if (azureOptions != null)
            {
                _azureOptions = azureOptions.Value;
            }
        }

        public void Configure(string name, OpenIdConnectOptions options)
        {
            options.ClientId = _azureOptions.ClientId;
            options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
            options.UseTokenLifetime = true;
            options.CallbackPath = _azureOptions.CallbackPath;
            options.RequireHttpsMetadata = false;
            options.ClientSecret = _azureOptions.ClientSecret;
        }

        public void Configure(OpenIdConnectOptions options)
        {
            Configure(Options.DefaultName, options);
        }
    }
}

Then AddAzureAd method is being called and I can see it walk through all of the code in this method, but when I put a breakpoint in the OnAuthorizationCodeReceived method that breakpoint never gets hit. I’ve done a bunch of reading and it looks like what I have is right, so I’m guessing that I must be missing something simple here, but can’t find the problem.

Editted
I’m now hitting the OnAuthorizationCodeReceived event, but now the application is failing to continue to log in getting the following error

SecurityTokenException: Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: ''."
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler+<HandleRequestAsync>d__12.MoveNext()

Stack Query Cookies Headers
SecurityTokenException: Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: ''."
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler+<HandleRequestAsync>d__12.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+<Invoke>d__6.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The OpenIdConnect component for Asp.net core 2.0 uses implicit flow(the value of response_type is id_token).

To fire the OnAuthorizationCodeReceived the event, we should use the hybrid flow which’s ‘response_type’ parameter contains code value.(eg. id_token code). And we need set it through the OpenIdConnectOptions like code below:

.AddOpenIdConnect(options =>
{
    options.Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAd:Tenant"]);
    options.ClientId = Configuration["AzureAd:ClientId"];
    options.ResponseType = "code id_token";     
});

options.Events = new OpenIdConnectEvents
{
    OnAuthorizationCodeReceived = async context =>
    {
        var credential = new ClientCredential(context.Options.ClientId, context.Options.ClientSecret);

        var authContext = new AuthenticationContext(context.Options.Authority);
        var authResult=await authContext.AcquireTokenByAuthorizationCodeAsync(context.TokenEndpointRequest.Code,
            new Uri(context.TokenEndpointRequest.RedirectUri, UriKind.RelativeOrAbsolute), credential, context.Options.Resource);
        context.HandleCodeRedemption(authResult.AccessToken, context.ProtocolMessage.IdToken);
    },
};


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x