在同一个 asp.net Web 应用程序中使用两个 Owin 身份

Use two Owin identities in the same asp.net web application

是否可以同时使用 OWIN 两种不同的身份验证,例如MicrosoftGoogle?

在我的 ASP.NET Web 应用程序中,用户最初使用 Azure OpenIdConnect 进行身份验证以使用该应用程序。

在某些时候,用户需要使用 Google 进行身份验证才能使用 Google 执行少量查询(不会覆盖将继续使用的 Microsoft 身份)。

我注意到每当我使用 Context.GetOwinContext().Authentication.Challenge(properties, "Google") 时,身份验证都会成功,我可以调用 Google 的 API,但是与 Microsoft 相关的声明、令牌和整个身份都丢失了并替换为 Google 一个,我不能再调用 Microsoft API 除非我要求用户再次登录。

有什么方法可以保留这两个身份,以便我可以根据需要使用它们吗?

由于没有答案,我想出了解决方案,感谢OWIN团队在GitHub上的支持,以下是解决方案:

Objective:向多个提供者进行身份验证,并维护两个声明,因此应用程序可以随时调用两个提供者 API。 在我的例子中,用户必须首先使用 Azure Active Directory (OpenIdConnect) 进行身份验证才能进入我的应用程序,并调用 Microsoft Graph API。用户还需要使用 Google 进行身份验证,才能调用 Google API.

For OpenIdConnect, I'm using the default creation by visual studio without any changes, and this is not the topic here.

如何添加第二个供应商?

  1. 告诉 OWIN 您在启动期间使用 Google 身份验证。
  2. 当用户尝试调用 Google API 时,检查它是否有 Google 相关声明。如果是,使用访问令牌并简单地调用 Google API,如果不是,这意味着这是对 Google 的第一次调用,因此要求应用程序使用 [=61= 进行身份验证].
  3. 一旦通过 Google 验证,保存声明(不覆盖 OpenIdConnect 的声明),以便下次调用 Google API.
  4. 时可以使用它

现在,让我们看看详情:

  1. 在主要身份验证 (OpenIDConnect) 下方,告诉 OWIN 您正在使用 Google(顺便说一下,这适用于任何其他提供商)。最重要的部分是告诉 OWIN 为 Google 使用不同的 cookie,以将 Google 的声明保存在单独的 cookie 中。如果错过这一步,Google 声明将覆盖 OpenIdConnect 声明,您将无法再调用 Microsoft Graph。

    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    
        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
    
        });
        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = AuthenticationConfig.ClientId,
                Authority = AuthenticationConfig.Authority,
                PostLogoutRedirectUri = AuthenticationConfig.PostLogoutRedirectUri,
                RedirectUri = AuthenticationConfig.RedirectUri,
                Scope = $"{AuthenticationConfig.BasicSignInScopes} User.Read",
                SaveTokens=true,
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,
                },
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = OnAuthenticationFailedAsync,
                    AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync,
                    SecurityTokenValidated = OnSecurityTokenValidatedAsync,
                },
                CookieManager = new Utils.SameSiteCookieManager(
                                 new SystemWebCookieManager())
    
            }
            );
        // Define New Cookies for Google
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Google",
            AuthenticationMode = AuthenticationMode.Passive,
            CookieName = CookieAuthenticationDefaults.CookiePrefix + "External.DocuSign",
        });
        // Tell OWIN to use Google with the special Cookies type
        app.UseGoogleAuthentication(new Microsoft.Owin.Security.Google.GoogleOAuth2AuthenticationOptions()
        {
            ClientId = "xxxxxxxxxxxxxxxxxxxxxxx",
            ClientSecret = "xxxxxxxxxxxxxxxxxx",
            SignInAsAuthenticationType = "Google",
            Provider = new Microsoft.Owin.Security.Google.GoogleOAuth2AuthenticationProvider() { OnAuthenticated = OnGoogleAuthenticated }
        });
        // This makes any middleware defined above this line run before the Authorization rule is applied in web.config
        app.UseStageMarker(PipelineStage.Authenticate);
    }
    
  2. 在调用 Google API 之前,检查 Google 声明是否已经存在。如果是,提取访问令牌并调用Google API。如果不是,则表示这是您第一次尝试调用 Google,因此请先进行身份验证,保存声明,然后再调用 API.

        var result = await Request.GetOwinContext().Authentication.AuthenticateAsync("Google");
        if (result == null) // No Claims found for Google
        {
            // Redirect to Google for authentication
            var properties = new AuthenticationProperties() { RedirectUri = "/" };
            Context.GetOwinContext().Authentication.Challenge(properties, "Google");
        }
        else
        {
            // Get the Access Token from the google Claims
            var accessToken = result.Identity.Claims.FirstOrDefault(a => a.Type == "google_access_token").Value;
            // Now CALL Google API
        }
    
  3. 在 Google 验证后保存 Google 声明。这又是在 StartupAuth.cs 中延续到 app.UseGoogeAuthentication 的地方,我们覆盖了获得 google 响应的事件,并将令牌保存到声明中。

        private static Task OnGoogleAuthenticated(Microsoft.Owin.Security.Google.GoogleOAuth2AuthenticatedContext context)
    {
        // Save the access token to Google Claims, to be used in Google API calls
        context.Identity.AddClaim(new Claim("google_access_token", context.AccessToken));
    
        if (context.RefreshToken != null)
        {
            context.Identity.AddClaim(new Claim("google_refresh_token", context.RefreshToken));
        }
        var expiresInSec = (long)(context.ExpiresIn.Value.TotalSeconds);
        context.Identity.AddClaim(new Claim("google_expires_in", expiresInSec.ToString()));
        return Task.FromResult(0);
    }