简体   繁体   中英

Asp Net Core with multiple authentication schemes. Integrate Azure AD into Indentity

I have a web application that allow users to login and register using local authentication (with JWT Authentication scheme), and also allow users to login in their azure active directory account (using OpenIdConnect Scheme). I'm treating the logging with azure active directory as an external login, however, I keep getting null response when I'm trying to get the GetExternalLoginInfoAsync()

Below is my setup:

services.AddAuthentication(options =>
            {
                options.DefaultScheme = "JWT_OR_COOKIE";
                options.DefaultChallengeScheme = "JWT_OR_COOKIE";
            })            
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
            // for azure active directory
            .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
            {
                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;                    
                options.Authority = config["authority"];
                options.ClientId = config["clientId"];
                options.ClientSecret = config["clientSecret"];
                options.ResponseType = "code";
                options.SaveTokens = true;
            })
            // for local login
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["TokenKey"])),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    NameClaimType = "name",
                    RoleClaimType = "role"
                };
                options.RequireHttpsMetadata = env.IsProduction();
            })                
            .AddPolicyScheme("JWT_OR_COOKIE", "JWT_OR_COOKIE", options =>
            {
                // runs on each request
                options.ForwardDefaultSelector = context =>
                {
                    // filter by auth type
                    string authorization = context.Request.Headers[HeaderNames.Authorization];
                    if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
                        return JwtBearerDefaults.AuthenticationScheme;

                    // otherwise always check for cookie auth                        
                    return OpenIdConnectDefaults.AuthenticationScheme;
                };
            });

            services.AddAuthorization(options =>
            {
                options.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin"));
            });

My controller:

[HttpGet("login")]
        [AllowAnonymous]
        public async Task<IActionResult> ExternalLogin([FromQuery] string returnUrl)
        {            
            var scheme = OpenIdConnectDefaults.AuthenticationScheme;            

            var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });

            return Challenge(new AuthenticationProperties
            {
                RedirectUri = redirectUrl
            }, scheme);

        }

        [HttpGet]
        [AllowAnonymous]
        public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
        {
            var acccessToken = await HttpContext.GetTokenAsync("access_token"); // <== this works

            var info = await _signInManager.GetExternalLoginInfoAsync(); // <=== this is null, and I don't know what I did wrong in here !!!

            var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);

            if (result.Succeeded)
            {
                // update any authentication process
                await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
                return LocalRedirect(returnUrl);
            }
            else
            {
                // do not have account yet
                return Ok();
            }


        }
  1. Please try by setting SignIn Scheme to IdentityConstants.ExternalScheme :

     services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options => options.SignInScheme = IdentityConstants.ExternalScheme);
  2. And define cookie scheme to null :

services.AddAuthentication()
   .AddMicrosoftIdentityWebApp(Configuration, cookieScheme: null);

Something like:-

 options => {
    ...
    options.ClientId = config.ClientId;
    options.TenantId = config.TenantId;
    options.CallbackPath = config.CallbackPath;
    options.SignInScheme = IdentityConstants.ExternalScheme;
    options.SignOutScheme = IdentityConstants.ExternalScheme;
   ...
  },
  openIdConnectScheme: idp.LoginProvider,
  cookieScheme: null 
);

In some cases also try by upgrading asp.net core to latest version or version campatible and check again.

References:

  1. signInManager.GetExternalLoginInfoAsync() always returns null.GitHub
  2. asp.net core - _signInManager.GetExternalLoginInfoAsync() always returns null with open id to azure ad - Stack Overflow

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM