Как получить токен доступа Azure B2C в приложении MVC с помощью Owin - PullRequest
3 голосов
/ 11 мая 2019

В моем последнем проекте я делаю MVC-приложение. Основные требования к приложению заключаются в том, что он позволяет пользователю выполнять вход с помощью Azure ADD B2C и позволяет пользователям манипулировать данными в базе данных службы приложений Azure. После этой статьи здесь мне удалось успешно войти в систему B2C. Однако у меня возникают проблемы при переходе ко второй части моего приложения, начиная с входа в службу приложений. Я знаю, что мне нужно получить токен доступа от B2C для передачи в службу приложений для проверки. Моя проблема в том, что я могу получить только токен ID, который служба приложения, как и должно, просто отправляет мне обратно 401. Мой класс запуска OWIN содержит следующее, как в статье:

public class Startup
{
    // App config settings
    private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
    private static string aadInstance = ConfigurationManager.AppSettings["ida:AadInstance"];
    private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
    private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];

    // B2C policy identifiers
    public static string SignUpPolicyId = ConfigurationManager.AppSettings["ida:SignUpPolicyId"];
    public static string SignInPolicyId = ConfigurationManager.AppSettings["ida:SignInPolicyId"];
    public static string ProfilePolicyId = ConfigurationManager.AppSettings["ida:UserProfilePolicyId"];

    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
    }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        // Configure OpenID Connect middleware for each policy
        app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
        app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(ProfilePolicyId));
        app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
    }

    // Used for avoiding yellow-screen-of-death
    private Task AuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
    {
        notification.HandleResponse();
        if (notification.Exception.Message == "access_denied")
        {
            notification.Response.Redirect("/");
        }
        else
        {
            notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
        }

        return Task.FromResult(0);
    }

    private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
    {
        return new OpenIdConnectAuthenticationOptions
        {
            // For each policy, give OWIN the policy-specific metadata address, and
            // set the authentication type to the id of the policy
            MetadataAddress = String.Format(aadInstance, tenant, policy),
            AuthenticationType = policy,

            // These are standard OpenID Connect parameters, with values pulled from web.config
            ClientId = clientId,
            RedirectUri = redirectUri,
            PostLogoutRedirectUri = redirectUri,
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthenticationFailed = AuthenticationFailed
            },
            Scope = "openid",
            ResponseType = "id_token",

            // This piece is optional - it is used for displaying the user's name in the navigation bar.
            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = "name",
                SaveSigninToken = true //important to save the token in boostrapcontext
            }
        };
    }
}

Вещи, которые я пробовал:

  1. Установка ResponceType на "access_token"
  2. Установка Scope для моей пользовательской опубликованной области действия "user_impersonation"
  3. Я был почти уверен, что это ничего не изменит в лучшую сторону, но я попытался установить NameClaimType в "access_token"
  4. Несколько других вещей, которые просто бросали исключения влево и вправо.

Это мое первое веб-приложение MVC, поэтому я все еще пытаюсь понять, как именно оно работает; особенно концепция куки (и даже как хранить токен доступа, который мне дали). Однако я нашел много полезных обучающих видео и статей, которые мне помогли. Я просто не нашел ничего, что касалось бы того, что я пытаюсь сделать. Любая помощь или руководство будет принята с благодарностью! Спасибо!

1 Ответ

2 голосов
/ 13 мая 2019

Здесь - очень хорошая статья, которая охватывает как веб-приложение Asp.Net, так и веб-приложение, использующее аутентификацию Azure AD B2c. Этот образец охватывает:

  • Вызов провайдера идентификации OpenID Connect (Azure AD B2C)
  • Получение токена из Azure AD B2C с использованием MSAL

    Я публикую Startup.cs для веб-API и проекта веб-приложения

    с использованием Microsoft.IdentityModel.Tokens; использование Microsoft.Owin.Security.Jwt; использование Microsoft.Owin.Security.OAuth; используя Овина; используя Систему; используя System.Configuration; using TaskService.App_Start;

    namespace TaskService { публичный частичный класс Startup { // Эти значения взяты из web.config открытая статическая строка AadInstance = ConfigurationManager.AppSettings ["ida: AadInstance"]; открытая статическая строка Tenant = ConfigurationManager.AppSettings ["ida: Tenant"]; открытая статическая строка ClientId = ConfigurationManager.AppSettings ["ida: ClientId"]; открытая статическая строка SignUpSignInPolicy = ConfigurationManager.AppSettings ["ida: SignUpSignInPolicyId"]; открытая статическая строка DefaultPolicy = SignUpSignInPolicy;

        /*
         * Configure the authorization OWIN middleware 
         */
        public void ConfigureAuth(IAppBuilder app)
        {
            TokenValidationParameters tvps = new TokenValidationParameters
            {
                // Accept only those tokens where the audience of the token is equal to the client ID of this app
                ValidAudience = ClientId,
                AuthenticationType = Startup.DefaultPolicy
            };
    
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
            {
                // This SecurityTokenProvider fetches the Azure AD B2C metadata & signing keys from the OpenIDConnect metadata endpoint
                AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(String.Format(AadInstance, Tenant, DefaultPolicy)))
            });
        }
    }
    

    }

Настройка веб-API-интерфейса

 <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="ida:AadInstance" value="https://login.microsoftonline.com/{0}/v2.0/.well-known/openid-configuration?p={1}" />
    <add key="ida:Tenant" value="fabrikamb2c.onmicrosoft.com" />
    <add key="ida:ClientId" value="90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6" />
    <add key="ida:SignUpSignInPolicyId" value="b2c_1_susi" />
    <!-- The following settings is used for requesting access tokens -->
    <add key="api:ReadScope" value="read" />
    <add key="api:WriteScope" value="write" />
  </appSettings>

Веб-приложение Startup.cs

using Microsoft.Identity.Client;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Notifications;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Configuration;
using System.IdentityModel.Claims;
using System.Threading.Tasks;
using System.Web;
using TaskWebApp.Models;
using System.Net;

namespace TaskWebApp
{
    public partial class Startup
    {
        // App config settings
        public static string ClientId = ConfigurationManager.AppSettings["ida:ClientId"];
        public static string ClientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
        public static string AadInstance = ConfigurationManager.AppSettings["ida:AadInstance"];
        public static string Tenant = ConfigurationManager.AppSettings["ida:Tenant"];
        public static string RedirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
        public static string ServiceUrl = ConfigurationManager.AppSettings["api:TaskServiceUrl"];

        // B2C policy identifiers
        public static string SignUpSignInPolicyId = ConfigurationManager.AppSettings["ida:SignUpSignInPolicyId"];
        public static string EditProfilePolicyId = ConfigurationManager.AppSettings["ida:EditProfilePolicyId"];
        public static string ResetPasswordPolicyId = ConfigurationManager.AppSettings["ida:ResetPasswordPolicyId"];

        public static string DefaultPolicy = SignUpSignInPolicyId;

        // API Scopes
        public static string ApiIdentifier = ConfigurationManager.AppSettings["api:ApiIdentifier"];
        public static string ReadTasksScope = ApiIdentifier + ConfigurationManager.AppSettings["api:ReadScope"];
        public static string WriteTasksScope = ApiIdentifier + ConfigurationManager.AppSettings["api:WriteScope"];
        public static string[] Scopes = new string[] { ReadTasksScope, WriteTasksScope };

        // OWIN auth middleware constants
        public const string ObjectIdElement = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";

        // Authorities
        public static string Authority = String.Format(AadInstance, Tenant, DefaultPolicy);

        /*
        * Configure the OWIN middleware 
        */
        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());

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

                    // These are standard OpenID Connect parameters, with values pulled from web.config
                    ClientId = ClientId,
                    RedirectUri = RedirectUri,
                    PostLogoutRedirectUri = 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"
                    },

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

        /*
         *  On each call to Azure AD B2C, check if a policy (e.g. the profile edit or password reset policy) has been specified in the OWIN context.
         *  If so, use that policy when making the call. Also, don't request a code (since it won't be needed).
         */
        private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            var policy = notification.OwinContext.Get<string>("Policy");

            if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
            {
                notification.ProtocolMessage.Scope = OpenIdConnectScope.OpenId;
                notification.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
                notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
            }

            return Task.FromResult(0);
        }

        /*
         * Catch any failures received by the authentication middleware and handle appropriately
         */
        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            notification.HandleResponse();

            // Handle the error code that Azure AD B2C throws when trying to reset a password from the login page 
            // because password reset is not supported by a "sign-up or sign-in policy"
            if (notification.ProtocolMessage.ErrorDescription != null && notification.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
            {
                // If the user clicked the reset password link, redirect to the reset password route
                notification.Response.Redirect("/Account/ResetPassword");
            }
            else if (notification.Exception.Message == "access_denied")
            {
                notification.Response.Redirect("/");
            }
            else
            {
                notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
            }

            return Task.FromResult(0);
        }


        /*
         * Callback function when an authorization code is received 
         */
        private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
        {
            // Extract the code from the response notification
            var code = notification.Code;

            string signedInUserID = notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
            TokenCache userTokenCache = new MSALSessionCache(signedInUserID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
            ConfidentialClientApplication cca = new ConfidentialClientApplication(ClientId, Authority, RedirectUri, new ClientCredential(ClientSecret), userTokenCache, null);
            try
            {
                AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, Scopes);
            }
            catch (Exception ex)
            {
                //TODO: Handle
                throw;
            }
        }
    }
}

Веб-конфигурация для веб-приложения

<appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="ida:Tenant" value="fabrikamb2c.onmicrosoft.com" />
    <add key="ida:ClientId" value="fdb91ff5-5ce6-41f3-bdbd-8267c817015d" />
    <add key="ida:ClientSecret" value="X330F3#92!z614M4" />
    <add key="ida:AadInstance" value="https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/.well-known/openid-configuration" />
    <add key="ida:RedirectUri" value="https://localhost:44316/" />
    <add key="ida:SignUpSignInPolicyId" value="b2c_1_susi" />
    <add key="ida:EditProfilePolicyId" value="b2c_1_edit_profile" />
    <add key="ida:ResetPasswordPolicyId" value="b2c_1_reset" />
    <!-- Uncomment the localhost url if you want to run the API locally -->
    <add key="api:TaskServiceUrl" value="https://aadb2cplayground.azurewebsites.net/" />
    <!--add key="api:TaskServiceUrl" value="https://localhost:44332/" /-->
    <!-- The following settings is used for requesting access tokens -->
    <add key="api:ApiIdentifier" value="https://fabrikamb2c.onmicrosoft.com/tasks/" />
    <add key="api:ReadScope" value="read" />
    <add key="api:WriteScope" valu

«Microsoft.Owin.Security.Cookies» будет использоваться для создания сеанса на основе файлов cookie после получения действительного токена от нашего клиента Azure AD B2C. Этот файл cookie отправляется из браузера на сервер с каждым последующим запросом и проверяется промежуточным программным обеспечением файла cookie.

Обратитесь ко мне на случай, если вам понадобится дополнительная помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...