Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass through authentication with ASP Core MVC, Web API and IdentityServer4?

I have been working on migrating a monolithic ASP Core MVC application to use an service architecture design. The MVC front-end website uses an HttpClient to load necessary data from the ASP Core Web API. A small portion of the front-end MVC app also requires authentication which is in place using IdentityServer4 (integrated with the back-end API). This all works great, until I put an Authorize attribute on a controller or method on the Web API. I know I need to somehow pass the user authorization from the front-end to the back-end in order for this to work, but I'm not sure how. I have tried getting the access_token: User.FindFirst("access_token") but it returns null. I then tried this method and I am able to get the token:

var client = new HttpClient("url.com");
var token = HttpContext.Authentication.GetTokenAsync("access_token")?.Result;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

This method gets the token but still doesn't authentication with the back-end API. I'm pretty new to this OpenId/IdentityServer concepts and any help would be appreciated!

Here is the relevant code from the MVC Client Startup class:

    private void ConfigureAuthentication(IApplicationBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies",
            AutomaticAuthenticate = true,
            ExpireTimeSpan = TimeSpan.FromMinutes(60)
        });
        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = "https://localhost:44348/",
            RequireHttpsMetadata = false,

            ClientId = "clientid",
            ClientSecret = "secret",

            ResponseType = "code id_token",
            Scope = { "openid", "profile" },
            GetClaimsFromUserInfoEndpoint = true,
            AutomaticChallenge = true, // Required to 302 redirect to login
            SaveTokens = true,

            TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
            {
                NameClaimType = "Name",
                RoleClaimType = "Role",
                SaveSigninToken = true
            },


        });
    }

and the StartUp class of the API:

        // Add authentication
        services.AddIdentity<ExtranetUser, IdentityRole>(options =>
        {
            // Password settings
            options.Password.RequireDigit = true;
            options.Password.RequiredLength = 8;
            options.Password.RequireNonAlphanumeric = true;
            options.Password.RequireUppercase = true;
            options.Password.RequireLowercase = true;

            // Lockout settings
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
            options.Lockout.MaxFailedAccessAttempts = 10;

            // User settings
            options.User.RequireUniqueEmail = true;
        })
            .AddDefaultTokenProviders();
        services.AddScoped<IUserStore<ExtranetUser>, ExtranetUserStore>();
        services.AddScoped<IRoleStore<IdentityRole>, ExtranetRoleStore>();
        services.AddSingleton<IAuthorizationHandler, AllRolesRequirement.Handler>();
        services.AddSingleton<IAuthorizationHandler, OneRoleRequirement.Handler>();
        services.AddSingleton<IAuthorizationHandler, EditQuestionAuthorizationHandler>();
        services.AddSingleton<IAuthorizationHandler, EditExamAuthorizationHandler>();
        services.AddAuthorization(options =>
        {
            /* ... etc .... */
        });
        var serviceProvider = services.BuildServiceProvider();
        var serviceSettings = serviceProvider.GetService<IOptions<ServiceSettings>>().Value;
        services.AddIdentityServer() // Configures OAuth/IdentityServer framework
            .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
            .AddInMemoryClients(IdentityServerConfig.GetClients(serviceSettings))
            .AddAspNetIdentity<ExtranetUser>()
            .AddTemporarySigningCredential(); // ToDo: Add permanent SigningCredential for IdentityServer
like image 747
Lin Meyer Avatar asked May 19 '17 19:05

Lin Meyer


People also ask

How will you implement authentication and authorization in asp net web API?

Web API assumes that authentication happens in the host. For web-hosting, the host is IIS, which uses HTTP modules for authentication. You can configure your project to use any of the authentication modules built in to IIS or ASP.NET, or write your own HTTP module to perform custom authentication.

How does authentication and authorization work in Web API?

The authentication and authorization mechanism in such a site is simple. After the user logs into the website, a single database holding user information verifies their identity. A session is created on the server, and all subsequent requests use the session to identify the user without another login required.


1 Answers

Added the nuget package here and the following code to fix:

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
   Authority = "https://localhost:44348/",
   ApiName = "api"
});

This allows the API to host IdentityServer4 and use itself as authentication. Then in the MvcClient the bearer token can be passed to the API.

like image 60
Lin Meyer Avatar answered Sep 17 '22 20:09

Lin Meyer