Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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()
like image 484
Paul Cavacas Avatar asked Mar 09 '23 05:03

Paul Cavacas


1 Answers

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);
    },
};
like image 191
Fei Xue - MSFT Avatar answered Apr 02 '23 21:04

Fei Xue - MSFT