ASP.NET Core 2.0 combining Cookies and Bearer Authorization for the same endpoint

后端 未结 5 474
梦如初夏
梦如初夏 2021-01-30 13:46

I\'ve created a new ASP.NET Core Web Application project in VS17 using the \"Web Application (Model-View-Controller)\" template and \".Net Framework\" + \"ASP.NET Core 2\" as th

5条回答
  •  日久生厌
    2021-01-30 14:26

    If I understand the question correctly then I believe that there is a solution. In the following example I am using cookie AND bearer authentication in a single app. The [Authorize] attribute can be used without specifying the scheme, and the app will react dynamically, depending on the method of authorization being used.

    services.AddAuthentication is called twice to register the 2 authentication schemes. The key to the solution is the call to services.AddAuthorization at the end of the code snippet, which tells ASP.NET to use BOTH schemes.

    I've tested this and it seems to work well.

    (Based on Microsoft docs.)

    services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.Authority = "https://localhost:4991";
            options.RequireHttpsMetadata = false;
    
            options.ClientId = "WebApp";
            options.ClientSecret = "secret";
    
            options.ResponseType = "code id_token";
            options.Scope.Add("api");
            options.SaveTokens = true;
        });
    
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Authority = "https://localhost:4991";
            options.RequireHttpsMetadata = false;
            // name of the API resource
            options.Audience = "api";
        });
    
    services.AddAuthorization(options =>
    {
        var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
            CookieAuthenticationDefaults.AuthenticationScheme,
            JwtBearerDefaults.AuthenticationScheme);
        defaultAuthorizationPolicyBuilder =
            defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
        options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
    });
    

    EDIT

    This works for authenticated users, but simply returns a 401 (unauthorized) if a user has not yet logged in.

    To ensure that unauthorized users are redirected to the login page, add the following code to the Configure method in your Startup class. Note: it's essential that the new middleware is placed after the call the app.UseAuthentication().

    app.UseAuthentication();
    app.Use(async (context, next) =>
    {
        await next();
        var bearerAuth = context.Request.Headers["Authorization"]
            .FirstOrDefault()?.StartsWith("Bearer ") ?? false;
        if (context.Response.StatusCode == 401
            && !context.User.Identity.IsAuthenticated
            && !bearerAuth)
        {
            await context.ChallengeAsync("oidc");
        }
    });
    

    If you know a cleaner way to achieve this redirect, please post a comment!

提交回复
热议问题