/// <summary> /// Builds the an authorization URI. /// </summary> /// <param name="buildAuthorizedUrlOptions">A <see cref="AuthorizeOptions"/> param.</param> /// <returns>An <see cref="string"/> representing an authorization URI.</returns> public static string BuildAuthorizeUrl(AuthorizeOptions buildAuthorizedUrlOptions) { if (buildAuthorizedUrlOptions is null) { throw new ArgumentNullException(nameof(buildAuthorizedUrlOptions)); } Utils.ValidateObject(buildAuthorizedUrlOptions); var responseType = CommonAuthentication.ParseResponseType(buildAuthorizedUrlOptions.ResponseType); var responseMode = CommonAuthentication.ParseResponseMode(buildAuthorizedUrlOptions.ResponseMode); var path = new PathString("/authorize"); var query = new QueryString(); query = query.Add("response_type", responseType); query = query.Add("state", buildAuthorizedUrlOptions.State); query = query.Add("nonce", buildAuthorizedUrlOptions.Nonce); query = query.Add("client_id", buildAuthorizedUrlOptions.ClientID); query = query.Add("scope", buildAuthorizedUrlOptions.Scope); if (buildAuthorizedUrlOptions.CodeChallengeMethod != CodeChallengeMethods.None) { string codechallengeMethod = CommonAuthentication.ParseCodeChallengeMethod(buildAuthorizedUrlOptions.CodeChallengeMethod); query = query.Add("code_challenge_method", codechallengeMethod); query = query.Add("code_challenge", buildAuthorizedUrlOptions.CodeChallenge); } if (!string.IsNullOrEmpty(buildAuthorizedUrlOptions.Connection)) { query = query.Add("connection", buildAuthorizedUrlOptions.Connection); } if (!string.IsNullOrEmpty(buildAuthorizedUrlOptions.Audience)) { query = query.Add("audience", buildAuthorizedUrlOptions.Audience); } if (!string.IsNullOrEmpty(responseMode)) { query = query.Add("response_mode", responseMode); } query = query.Add("redirect_uri", buildAuthorizedUrlOptions.RedirectUri); UriBuilder uriBuilder = new UriBuilder { Scheme = "https", Host = buildAuthorizedUrlOptions.Domain, Path = path, Query = query.ToUriComponent(), }; return(uriBuilder.Uri.AbsoluteUri); }
/// <summary> /// Add default Blazor.Auth0 authentication. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> instance.</param> public static void AddDefaultBlazorAuth0Authentication(this IServiceCollection services) { // TODO: This method is too convulted, it should be separeted into smaller pieces if (services is null) { throw new ArgumentNullException(nameof(services)); } ClientOptions clientOptions = services.BuildServiceProvider().GetRequiredService <ClientOptions>(); Utils.ValidateObject(clientOptions); services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { options.ExpireTimeSpan = TimeSpan.FromMinutes(60); options.SlidingExpiration = clientOptions.SlidingExpiration; }) .AddBlazorAuth0() .AddOpenIdConnect(clientOptions.ClaimsIssuer, options => { // Set the authority to your Auth0 domain options.Authority = $"https://{clientOptions.Domain}"; options.RequireHttpsMetadata = false; // Configure the Auth0 Client ID and Client Secret options.ClientId = clientOptions.ClientId; options.ClientSecret = clientOptions.ClientSecret; options.ResponseType = CommonAuthentication.ParseResponseType(clientOptions.ResponseType); string[] scopes = clientOptions.Scope.Trim().ToLowerInvariant().Split(","); options.Scope.Clear(); foreach (string scope in scopes) { options.Scope.Add(scope); } if (clientOptions.ResponseType == Models.Enumerations.ResponseTypes.Code && !string.IsNullOrEmpty(clientOptions.ClientSecret)) { options.Scope.Add("offline_access"); } options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash"); options.GetClaimsFromUserInfoEndpoint = true; options.SaveTokens = true; options.CallbackPath = new PathString(clientOptions.CallbackPath); options.RemoteSignOutPath = new PathString(clientOptions.RemoteSignOutPath); options.SignedOutRedirectUri = clientOptions.RedirectUri; options.ClaimsIssuer = clientOptions.ClaimsIssuer; options.UseTokenLifetime = !clientOptions.SlidingExpiration; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", RequireSignedTokens = true, RequireExpirationTime = true, ValidateTokenReplay = true, ValidIssuers = new string[] { options.Authority, }, ValidateLifetime = true, ValidateIssuerSigningKey = true, }; if (!string.IsNullOrEmpty(clientOptions.Audience)) { options.TokenValidationParameters.ValidAudiences = new string[] { clientOptions.Audience, $"{options.Authority}/userinfo", }; } options.Events = new OpenIdConnectEvents { OnRedirectToIdentityProvider = context => { if (!string.IsNullOrEmpty(clientOptions.Audience)) { context.ProtocolMessage.SetParameter("audience", clientOptions.Audience); HttpRequest request = context.Request; var errorUri = request.Scheme + "://" + request.Host + request.PathBase; context.ProtocolMessage.SetParameter("error_uri", errorUri); } return(Task.FromResult(0)); }, OnRemoteSignOut = (context) => { Authentication.ClearAspNetCookies(context.HttpContext); string redirectUri = context.Request.Query .Where(x => x.Key.ToLowerInvariant() == "redirect_uri") .Select(x => x.Value) .FirstOrDefault(); if (string.IsNullOrEmpty(redirectUri)) { HttpRequest request = context.Request; redirectUri = request.Scheme + "://" + request.Host + request.PathBase; } string logoutUri = CommonAuthentication.BuildLogoutUrl(clientOptions.Domain, clientOptions.ClientId, redirectUri); context.Response.Redirect(logoutUri); context.HandleResponse(); return(Task.CompletedTask); }, // handle the logout redirection OnRedirectToIdentityProviderForSignOut = (context) => { string logoutUri = CommonAuthentication.BuildLogoutUrl(clientOptions.Domain, clientOptions.ClientId); string postLogoutUri = context.Properties.RedirectUri; if (!string.IsNullOrEmpty(postLogoutUri)) { if (postLogoutUri.StartsWith("/")) { // transform to absolute HttpRequest request = context.Request; postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri; } logoutUri += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}"; } context.Response.Redirect(logoutUri); context.HandleResponse(); return(Task.CompletedTask); }, OnTokenValidated = (u) => { if (!string.IsNullOrEmpty(clientOptions.Audience)) { string accessToken = u.TokenEndpointResponse.AccessToken; AccessTokenPayload parsedAccessToken = !string.IsNullOrEmpty(accessToken) ? CommonAuthentication.DecodeTokenPayload <AccessTokenPayload>(accessToken) : default; List <string> permissions = parsedAccessToken?.Permissions ?? new List <string>(); if (permissions.Any()) { string name = u.Principal.Claims.Where(x => x.Type == "name").FirstOrDefault()?.Value; name ??= u.Principal.Claims.Where(x => x.Type == ClaimTypes.GivenName).FirstOrDefault()?.Value; name ??= u.Principal.Claims.Where(x => x.Type == ClaimTypes.Name).FirstOrDefault()?.Value; name ??= u.Principal.Claims.Where(x => x.Type == "nickname").FirstOrDefault()?.Value; name ??= u.Principal.Claims.Where(x => x.Type == ClaimTypes.Email).FirstOrDefault()?.Value; GenericIdentity identity = new GenericIdentity(name, "JWT"); identity.AddClaims(u.Principal.Claims); identity.AddClaims(permissions.Select(permission => new Claim("permissions", permission, "permissions"))); identity.AddClaim(new Claim("exp", parsedAccessToken.Exp.ToString(), ClaimValueTypes.Integer64)); ClaimsPrincipal user = new ClaimsPrincipal(identity); u.Principal = user; } } return(Task.CompletedTask); }, OnAccessDenied = (u) => { return(Task.CompletedTask); }, OnAuthenticationFailed = (u) => { return(Task.CompletedTask); }, OnRemoteFailure = (context) => { context.Response.Redirect("/"); context.HandleResponse(); return(Task.CompletedTask); }, }; });