首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从Web应用程序访问使用Azure AD B2C保护的两个单独的web

如何从Web应用程序访问使用Azure AD B2C保护的两个单独的web
EN

Stack Overflow用户
提问于 2020-11-25 11:26:10
回答 1查看 467关注 0票数 0

我们有两个独立的dotnet核心API2(API1和API2),它们使用azure ad b2c进行保护。这两个apis都在b2c租户上注册,并公开了它们的作用域。我们有一个客户端web应用程序,即访问上述受保护的apis。此web应用程序已在b2c租户中注册为应用程序,并为上述具有适当作用域的api设置了api权限。

我们使用带有签名策略的MSAL.net将用户注册到web应用程序。身份验证调用需要提到作用域。因此,我们在呼叫中添加API1 1的作用域。(注意:单个资源的一个作用域可以在下面所示的auth调用中添加)

代码语言:javascript
复制
public void ConfigureAuth(IAppBuilder app)
    {
        // Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            // ASP.NET web host compatible cookie manager
            CookieManager = new SystemWebChunkingCookieManager()
        });

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                // Generate the metadata address using the tenant and policy information
                MetadataAddress = String.Format(Globals.WellKnownMetadata, Globals.Tenant, Globals.DefaultPolicy),

                // These are standard OpenID Connect parameters, with values pulled from web.config
                ClientId = Globals.ClientId,
                RedirectUri = Globals.RedirectUri,
                PostLogoutRedirectUri = Globals.RedirectUri,

                // Specify the callbacks for each type of notifications
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = OnRedirectToIdentityProvider,
                    AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                    AuthenticationFailed = OnAuthenticationFailed,
                },

                // Specify the claim type that specifies the Name property.
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    ValidateIssuer = false
                },

                // Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
                Scope = $"openid profile offline_access {Globals.ReadTasksScope} {Globals.WriteTasksScope}",

                // ASP.NET web host compatible cookie manager
                CookieManager = new SystemWebCookieManager()
            }
        );
    }

OnAuthorizationCodeRecieved方法在Startup.Auth.cs中接收由于上述auth调用而接收的代码,并使用它根据所提供的作用域获取访问令牌,并将其存储在缓存中。如下所示

代码语言:javascript
复制
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
    {
        try
        {
            /*
             The `MSALPerUserMemoryTokenCache` is created and hooked in the `UserTokenCache` used by `IConfidentialClientApplication`.
             At this point, if you inspect `ClaimsPrinciple.Current` you will notice that the Identity is still unauthenticated and it has no claims,
             but `MSALPerUserMemoryTokenCache` needs the claims to work properly. Because of this sync problem, we are using the constructor that
             receives `ClaimsPrincipal` as argument and we are getting the claims from the object `AuthorizationCodeReceivedNotification context`.
             This object contains the property `AuthenticationTicket.Identity`, which is a `ClaimsIdentity`, created from the token received from
             Azure AD and has a full set of claims.
             */
            IConfidentialClientApplication confidentialClient = MsalAppBuilder.BuildConfidentialClientApplication(new ClaimsPrincipal(notification.AuthenticationTicket.Identity));

            // Upon successful sign in, get & cache a token using MSAL
            AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();
            

        }
        catch (Exception ex)
        {
            throw new HttpResponseException(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.BadRequest,
                ReasonPhrase = $"Unable to get authorization code {ex.Message}.".Replace("\n", "").Replace("\r", "")
            });
        }
    }

然后在TasksController中使用该访问令牌来调用AcquireTokenSilent,后者从缓存中检索访问令牌,然后在api调用中使用该访问令牌。

代码语言:javascript
复制
public async Task<ActionResult> Index()
    {
        try
        {
            // Retrieve the token with the specified scopes
            var scope = new string[] { Globals.ReadTasksScope };
            
            IConfidentialClientApplication cca = MsalAppBuilder.BuildConfidentialClientApplication();
            var accounts = await cca.GetAccountsAsync();
            AuthenticationResult result = await cca.AcquireTokenSilent(scope, accounts.FirstOrDefault()).ExecuteAsync();
            
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, apiEndpoint);

            // Add token to the Authorization header and make the request
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
            HttpResponseMessage response = await client.SendAsync(request);

            // Handle the response
            switch (response.StatusCode)
            {
                case HttpStatusCode.OK:
                    String responseString = await response.Content.ReadAsStringAsync();
                    JArray tasks = JArray.Parse(responseString);
                    ViewBag.Tasks = tasks;
                    return View();
                case HttpStatusCode.Unauthorized:
                    return ErrorAction("Please sign in again. " + response.ReasonPhrase);
                default:
                    return ErrorAction("Error. Status code = " + response.StatusCode + ": " + response.ReasonPhrase);
            }
        }
        catch (MsalUiRequiredException ex)
        {
            /*
                If the tokens have expired or become invalid for any reason, ask the user to sign in again.
                Another cause of this exception is when you restart the app using InMemory cache.
                It will get wiped out while the user will be authenticated still because of their cookies, requiring the TokenCache to be initialized again
                through the sign in flow.
            */
            return new RedirectResult("/Account/SignUpSignIn?redirectUrl=/Tasks");
        }
        catch (Exception ex)
        {
            return ErrorAction("Error reading to do list: " + ex.Message);
        }
    }

问题是OnAuthorizationCodeRecieved方法接收的代码只能用于获取API1的访问令牌,因为其作用域是在auth调用中提到的。当试图获取API2的访问令牌时,它返回null。

问:如何配置web应用程序,使其能够访问多个受保护的apis?

请建议一下。

代码可以从示例https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi中找到。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-11-25 13:47:31

单个访问令牌只能包含单个受众的作用域。

你有两个选择:

scopes.

  • Request

  • 将这两个服务合并到一个应用程序注册中,并公开不同的多个令牌--每个服务一个。如果您的单点登录策略在B2C中配置正确,那么用户.就会悄无声息地发生这种情况。

如果您拥有这两种服务,我建议使用选项1(听起来您这样做)。与此选项相关的一些提示。

在组合应用程序注册中声明作用域时,使用点语法{}.{

  • }.如果这样做,作用域将按Azure门户中的逻辑服务分组。
  • 确保您正在验证服务中的作用域。只验证观众是不够好的,并且允许攻击者使用标记绑定到另一个服务进行横向移动。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65003748

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档