/// <summary>
        /// Is called whenever B2C retrieves a new auth token.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
        {
            // Use MSAL to swap the code for an access token
            // Extract the authorization code from the response notification
            var code              = context.ProtocolMessage.Code;
            var signedInUserId    = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;
            var userTokenCache    = new MsalSessionCache(signedInUserId, context.HttpContext).GetMsalCacheInstance();
            var clientApplication = new ConfidentialClientApplication(
                AzureAdB2COptions.ClientId,
                AzureAdB2COptions.Authority,
                AzureAdB2COptions.RedirectUri,
                new ClientCredential(AzureAdB2COptions.ClientSecret),
                userTokenCache,
                null);

            // try to retrieve the baerer token
            try
            {
                var result = await clientApplication.AcquireTokenByAuthorizationCodeAsync(code, AzureAdB2COptions.ApiScopes.Split(' '));

                context.HandleCodeRedemption(result.AccessToken, result.IdToken);
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.Message);
                throw;
            }
        }
        public AzureAdB2CSecuredApiConnector(IOptions <AzureAdB2COptions> options, IHttpContextAccessor httpContextAccessor)
        {
            _azureAdB2COptions = options.Value;
            ValidateOptions(options);
            _requiredScopes = _azureAdB2COptions.ApiScopes.Split(' ').ToList();
            var signedInUserId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
            var userTokenCache = new MsalSessionCache(signedInUserId, httpContextAccessor.HttpContext).GetMsalCacheInstance();

            _confidentialClientApplication = new ConfidentialClientApplication(_azureAdB2COptions.ClientId, _azureAdB2COptions.Authority, _azureAdB2COptions.RedirectUri, new ClientCredential(_azureAdB2COptions.ClientSecret), userTokenCache, null);
        }
        public async Task <IActionResult> Api()
        {
            var responseString = "";

            try
            {
                // Retrieve the token with the specified scopes
                var scope             = _azureAdB2COptions.ApiScopes.Split(' ');
                var signedInUserId    = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
                var userTokenCache    = new MsalSessionCache(signedInUserId, HttpContext).GetMsalCacheInstance();
                var clientApplication = new ConfidentialClientApplication(
                    _azureAdB2COptions.ClientId,
                    _azureAdB2COptions.Authority,
                    _azureAdB2COptions.RedirectUri,
                    new ClientCredential(_azureAdB2COptions.ClientSecret),
                    userTokenCache,
                    null);
                var result = await clientApplication.AcquireTokenSilentAsync(scope, clientApplication.Users.FirstOrDefault(), _azureAdB2COptions.Authority, false);

                using (var client = new HttpClient())
                {
                    var request = new HttpRequestMessage(HttpMethod.Get, _azureAdB2COptions.ApiUrl);
                    // Add token to the Authorization header and make the request
                    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
                    var response = await client.SendAsync(request);

                    // Handle the response
                    switch (response.StatusCode)
                    {
                    case HttpStatusCode.OK:
                        responseString = await response.Content.ReadAsStringAsync();

                        break;

                    case HttpStatusCode.Unauthorized:
                        responseString = $"Please sign in again. {response.ReasonPhrase}";
                        break;

                    default:
                        responseString = $"Error calling API. StatusCode=${response.StatusCode}";
                        break;
                    }
                }
            }
            catch (MsalUiRequiredException ex)
            {
                responseString = $"Session has expired. Please sign in again. {ex.Message}";
            }
            catch (Exception ex)
            {
                responseString = $"Error calling API: {ex.Message}";
            }
            ViewData["Payload"] = $"{responseString}";
            return(View());
        }
        private async Task <AuthenticationResult> AcquireTokenSilentAsync(AzureAdB2COptions azureAdB2COptions)
        {
            // Retrieve the token with the specified scopes
            var scope          = azureAdB2COptions.ApiScopes.Split(' ');
            var signedInUserId = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
            var userTokenCache = new MsalSessionCache(signedInUserId, HttpContext).GetMsalCacheInstance();
            var cca            = new ConfidentialClientApplication(
                azureAdB2COptions.ApplicationId,
                azureAdB2COptions.Authority,
                azureAdB2COptions.RedirectUri,
                new ClientCredential(azureAdB2COptions.ClientSecret), userTokenCache, null);

            var result = await cca.AcquireTokenSilentAsync(scope, cca.Users.FirstOrDefault(), azureAdB2COptions.Authority, false);

            return(result);
        }
Beispiel #5
0
        public async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context, string clientId, string authority, string clientSecret, string redirectUri, string apiScopes)
        {
            // Use MSAL to swap the code for an access token
            // Extract the code from the response notification
            var code = context.ProtocolMessage.Code;

            var signedInUserId = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;
            var userTokenCache = new MsalSessionCache(signedInUserId, context.HttpContext).GetMsalCacheInstance();
            var cca            = new ConfidentialClientApplication(clientId, authority, redirectUri, new ClientCredential(clientSecret), userTokenCache, null);

            try
            {
                var result =
                    await cca.AcquireTokenByAuthorizationCodeAsync(code, apiScopes.Split(' '));


                context.HandleCodeRedemption(result.AccessToken, result.IdToken);
            }
            catch (Exception ex)
            {
                //TODO: Handle
                throw;
            }
        }
        public static AuthenticationBuilder AddAzureB2CCookieAuthentication(this IServiceCollection services, AzureAdB2COptions azureAdB2CSettings, string resetPasswordUrl, bool requestAccessToken, bool loadMemberGroupsAsRoles = false)
        {
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromMinutes(20);
                // no javascript calls to cookie
                options.Cookie.HttpOnly = true;
            });
            return(services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
                   .AddCookie()
                   .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
            {
                options.ClientId = azureAdB2CSettings.ClientId;
                // Set the authority to your Azure domain
                options.Authority = azureAdB2CSettings.Authority;

                options.UseTokenLifetime = true;
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    NameClaimType = "name"
                };

                options.Events = new OpenIdConnectEvents
                {
                    OnRedirectToIdentityProvider = (context) =>
                    {
                        // pass language (adds ui_locales to query string)
                        var requestCulture = context.HttpContext.Features.Get <IRequestCultureFeature>();
                        var lang = requestCulture?.RequestCulture.Culture.TextInfo.ToTitleCase(
                            requestCulture.RequestCulture.Culture.TwoLetterISOLanguageName);
                        if (lang != null)
                        {
                            context.ProtocolMessage.UiLocales = lang;
                        }

                        // no explict policy or default policy passed - just continue
                        var defaultPolicy = azureAdB2CSettings.DefaultPolicy;
                        if (!context.Properties.Items.TryGetValue(AzureAdB2COptions.POLICY_AUTHENTICATION_PROPERTY, out var policy) || policy.Equals(defaultPolicy))
                        {
                            if (requestAccessToken)
                            {
                                context.ProtocolMessage.Scope += $" offline_access {String.Join(" ", azureAdB2CSettings.Scopes)}";
                                context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.CodeIdToken;
                            }
                        }
                        else
                        {
                            // explict policy set in context => see AuthenticationProperties.Items.Add("Policy", desired policy_name)
                            // example --> custom policy has been set to reset password
                            context.ProtocolMessage.Scope = OpenIdConnectScope.OpenIdProfile;
                            context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
                            context.ProtocolMessage.IssuerAddress = context.ProtocolMessage.IssuerAddress.ToLower().Replace($"/{defaultPolicy.ToLower()}/", $"/{policy.ToLower()}/");
                            context.Properties.Items.Remove(AzureAdB2COptions.POLICY_AUTHENTICATION_PROPERTY);
                        }
                        return Task.CompletedTask;
                    },
                    OnRemoteFailure = (context) =>
                    {
                        context.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 (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("AADB2C90118"))
                        {
                            // If the user clicked the reset password link, redirect to the reset password route
                            context.Response.Redirect(resetPasswordUrl);
                        }
                        else if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("access_denied"))
                        {
                            context.Response.Redirect("/");
                        }
                        else
                        {
                            throw context.Failure;
                        }
                        return Task.FromResult(0);
                    },
                    OnAuthorizationCodeReceived = async(context) =>
                    {
                        // Use MSAL to swap the code for an access token
                        // Extract the code from the response notification
                        var auhtorizationCode = context.ProtocolMessage.Code;

                        string signedInUserId = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;
                        var userTokenCache = new MsalSessionCache(signedInUserId, context.HttpContext).GetMsalCacheInstance();
                        var clientApplication = new ConfidentialClientApplication(azureAdB2CSettings.ClientId, azureAdB2CSettings.Authority, azureAdB2CSettings.RedirectUri, new ClientCredential(azureAdB2CSettings.ClientSecret), userTokenCache, null);

                        try
                        {
                            if (requestAccessToken)
                            {
                                var result = await clientApplication.AcquireTokenByAuthorizationCodeAsync(auhtorizationCode, azureAdB2CSettings.Scopes);
                                context.HandleCodeRedemption(result.AccessToken, result.IdToken);
                            }
                        }
                        catch (Exception e)
                        {
                            throw new Exception("AcquireTokenByAuthorizationCodeAsync failed", e);
                        }
                    },
                    // handle the logout redirection
                    OnRedirectToIdentityProviderForSignOut = (context) =>
                    {
                        var logoutUri = $"{azureAdB2CSettings.Domain}/v2/logout?client_id={azureAdB2CSettings.ClientId}";

                        var postLogoutUri = context.Properties.RedirectUri;
                        if (!string.IsNullOrEmpty(postLogoutUri))
                        {
                            if (postLogoutUri.StartsWith("/"))
                            {
                                // transform to absolute
                                var request = context.Request;
                                postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase +
                                                postLogoutUri;
                            }
                            logoutUri += $"&post_logout_redirect_uri={Uri.EscapeDataString(postLogoutUri)}";
                        }
                        // set the audience parameter to get also an access token back after login to be able to call APIs of this application
                        context.ProtocolMessage.SetParameter("audience", azureAdB2CSettings.ClientId);
                        context.Response.Redirect(logoutUri);
                        context.HandleResponse();

                        return Task.CompletedTask;
                    }
                };
                if (loadMemberGroupsAsRoles)
                {
                    //Check via Azure Graph API if user is in correct group
                    options.Events.OnTokenValidated = context =>
                    {
                        var serviceProvider = services.BuildServiceProvider();
                        // resolve GraphApiConnector
                        var graphApiConnector = serviceProvider.GetService <IGraphApiConnector>();
                        if (graphApiConnector == null)
                        {
                            throw new Exception("No implementation has been registered for IGraphApiConnector");
                        }
                        // Get membergroups for user from AzureAd
                        var signedInUserId = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;
                        var memberGroups = graphApiConnector.GetMemberGroupsForUser(signedInUserId).GetAwaiter()
                                           .GetResult();
                        // create roleclaim
                        var roleClaims = memberGroups.Select(x => new Claim(ClaimTypes.Role, x));
                        // Add RoleClaim to useridentity
                        ((ClaimsIdentity)context.Principal.Identity).AddClaims(roleClaims);

                        return Task.FromResult(0);
                    };
                }
            }));