public async Task LoginAsync(OpenidConnectPkceSettings openidClientPkceSettings = null) { try { openidClientPkceSettings = openidClientPkceSettings ?? globalOpenidClientPkceSettings; var nonce = RandomGenerator.GenerateNonce(); var codeVerifier = RandomGenerator.Generate(64); ValidateResponseMode(openidClientPkceSettings.ResponseMode); var loginCallBackUri = new Uri(new Uri(navigationManager.BaseUri), openidClientPkceSettings.LoginCallBackPath).OriginalString; var state = await SaveStateAsync(openidClientPkceSettings, loginCallBackUri, navigationManager.Uri, codeVerifier : codeVerifier, nonce : nonce); var authenticationRequest = new AuthenticationRequest { ClientId = openidClientPkceSettings.ClientId, ResponseMode = openidClientPkceSettings.ResponseMode, ResponseType = openidClientPkceSettings.ResponseType, RedirectUri = loginCallBackUri, Scope = openidClientPkceSettings.AllScope.ToSpaceList(), Nonce = nonce, State = state }; var codeChallengeRequest = new CodeChallengeSecret { CodeChallenge = await codeVerifier.Sha256HashBase64urlEncodedAsync(), CodeChallengeMethod = IdentityConstants.CodeChallengeMethods.S256, }; var requestDictionary = authenticationRequest.ToDictionary().AddToDictionary(codeChallengeRequest); if (openidClientPkceSettings.Resources?.Count() > 0) { var resourceRequest = new ResourceRequest { Resources = openidClientPkceSettings.Resources }; requestDictionary = requestDictionary.AddToDictionary(resourceRequest); } var oidcDiscovery = await GetOidcDiscoveryAsync(openidClientPkceSettings.OidcDiscoveryUri); var authorizationUri = QueryHelpers.AddQueryString(oidcDiscovery.AuthorizationEndpoint, requestDictionary); navigationManager.NavigateTo(authorizationUri, true); } catch (Exception ex) { throw new SecurityException($"Failed to login, Authority '{openidClientPkceSettings.Authority}'.", ex); } }
/// <summary> /// Is Valid OAuth 2.0 Code Challenge Secret. /// </summary> public static void Validate(this CodeChallengeSecret codeChallengeSecret) { if (codeChallengeSecret == null) { new ArgumentNullException(nameof(codeChallengeSecret)); } if (codeChallengeSecret.CodeChallenge.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(codeChallengeSecret.CodeChallenge), codeChallengeSecret.GetTypeName()); } codeChallengeSecret.CodeChallenge.ValidateMaxLength(IdentityConstants.MessageLength.CodeChallengeMax, nameof(codeChallengeSecret.CodeChallenge), codeChallengeSecret.GetTypeName()); codeChallengeSecret.CodeChallengeMethod.ValidateMaxLength(IdentityConstants.MessageLength.CodeChallengeMethodMax, nameof(codeChallengeSecret.CodeChallengeMethod), codeChallengeSecret.GetTypeName()); }
public async Task <IActionResult> AuthenticationRequestAsync(string partyId) { logger.ScopeTrace(() => "Up, OIDC Authentication request."); var oidcUpSequenceData = await sequenceLogic.GetSequenceDataAsync <OidcUpSequenceData>(remove : false); if (!oidcUpSequenceData.UpPartyId.Equals(partyId, StringComparison.Ordinal)) { throw new Exception("Invalid up-party id."); } logger.SetScopeProperty(Constants.Logs.UpPartyId, oidcUpSequenceData.UpPartyId); var party = await tenantRepository.GetAsync <TParty>(oidcUpSequenceData.UpPartyId); logger.SetScopeProperty(Constants.Logs.UpPartyClientId, party.Client.ClientId); await oidcDiscoveryReadUpLogic.CheckOidcDiscoveryAndUpdatePartyAsync(party); var nonce = RandomGenerator.GenerateNonce(); var loginCallBackUrl = HttpContext.GetUpPartyUrl(party.Name, Constants.Routes.OAuthController, Constants.Endpoints.AuthorizationResponse, partyBindingPattern: party.PartyBindingPattern); oidcUpSequenceData.ClientId = !party.Client.SpClientId.IsNullOrWhiteSpace() ? party.Client.SpClientId : party.Client.ClientId; oidcUpSequenceData.RedirectUri = loginCallBackUrl; oidcUpSequenceData.Nonce = nonce; if (party.Client.EnablePkce) { var codeVerifier = RandomGenerator.Generate(64); oidcUpSequenceData.CodeVerifier = codeVerifier; } await sequenceLogic.SaveSequenceDataAsync(oidcUpSequenceData); var authenticationRequest = new AuthenticationRequest { ClientId = oidcUpSequenceData.ClientId, ResponseMode = party.Client.ResponseMode, ResponseType = party.Client.ResponseType, RedirectUri = loginCallBackUrl, Nonce = nonce, State = await sequenceLogic.CreateExternalSequenceIdAsync() }; switch (oidcUpSequenceData.LoginAction) { case LoginAction.ReadSession: authenticationRequest.Prompt = IdentityConstants.AuthorizationServerPrompt.None; break; case LoginAction.RequireLogin: authenticationRequest.Prompt = IdentityConstants.AuthorizationServerPrompt.Login; break; default: break; } if (oidcUpSequenceData.MaxAge.HasValue) { authenticationRequest.MaxAge = oidcUpSequenceData.MaxAge; } if (!oidcUpSequenceData.UserId.IsNullOrEmpty()) { authenticationRequest.LoginHint = oidcUpSequenceData.UserId; } authenticationRequest.Scope = new[] { IdentityConstants.DefaultOidcScopes.OpenId }.ConcatOnce(party.Client.Scopes).ToSpaceList(); //TODO add AcrValues //authenticationRequest.AcrValues = "urn:federation:authentication:windows"; logger.ScopeTrace(() => $"Up, Authentication request '{authenticationRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message); var nameValueCollection = authenticationRequest.ToDictionary(); if (party.Client.EnablePkce) { var codeChallengeRequest = new CodeChallengeSecret { CodeChallenge = await oidcUpSequenceData.CodeVerifier.Sha256HashBase64urlEncodedAsync(), CodeChallengeMethod = IdentityConstants.CodeChallengeMethods.S256, }; logger.ScopeTrace(() => $"Up, CodeChallengeSecret request '{codeChallengeRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message); nameValueCollection = nameValueCollection.AddToDictionary(codeChallengeRequest); } securityHeaderLogic.AddFormActionAllowAll(); logger.ScopeTrace(() => $"Up, Authentication request URL '{party.Client.AuthorizeUrl}'."); logger.ScopeTrace(() => "Up, Sending OIDC Authentication request.", triggerEvent: true); return(await nameValueCollection.ToRedirectResultAsync(party.Client.AuthorizeUrl)); }
private void ValidateAuthenticationRequest(OidcDownClient client, AuthenticationRequest authenticationRequest, CodeChallengeSecret codeChallengeSecret) { try { var responseTypes = authenticationRequest.ResponseType.ToSpaceList(); bool isImplicitFlow = !responseTypes.Where(rt => rt.Contains(IdentityConstants.ResponseTypes.Code)).Any(); authenticationRequest.Validate(isImplicitFlow); if (client.RequirePkce) { if (responseTypes.Where(rt => !rt.Equals(IdentityConstants.ResponseTypes.Code)).Any()) { throw new OAuthRequestException($"Require '{IdentityConstants.ResponseTypes.Code}' flow with PKCE.") { RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidRequest }; } } if (!client.RedirectUris.Any(u => u.Equals(authenticationRequest.RedirectUri, StringComparison.InvariantCultureIgnoreCase))) { throw new OAuthRequestException($"Invalid redirect Uri '{authenticationRequest.RedirectUri}'.") { RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidRequest }; } if (!client.ClientId.Equals(authenticationRequest.ClientId, StringComparison.InvariantCultureIgnoreCase)) { throw new OAuthRequestException($"Invalid client id '{authenticationRequest.ClientId}'.") { RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidClient }; } if (!authenticationRequest.Scope.Contains(IdentityConstants.DefaultOidcScopes.OpenId)) { throw new OAuthRequestException($"Require '{IdentityConstants.DefaultOidcScopes.OpenId}' scope.") { RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidScope }; } var resourceScopes = oauthResourceScopeDownLogic.GetResourceScopes(client as TClient); var invalidScope = authenticationRequest.Scope.ToSpaceList().Where(s => !(resourceScopes.Select(rs => rs).Contains(s) || (client.Scopes != null && client.Scopes.Select(ps => ps.Scope).Contains(s))) && IdentityConstants.DefaultOidcScopes.OpenId != s); if (invalidScope.Count() > 0) { throw new OAuthRequestException($"Invalid scope '{authenticationRequest.Scope}'.") { RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidScope }; } ValidateResponseType(client, authenticationRequest, responseTypes); if (!authenticationRequest.ResponseMode.IsNullOrEmpty()) { var invalidResponseMode = !(new[] { IdentityConstants.ResponseModes.Fragment, IdentityConstants.ResponseModes.Query, IdentityConstants.ResponseModes.FormPost }.Contains(authenticationRequest.ResponseMode)); if (invalidResponseMode) { throw new OAuthRequestException($"Invalid response mode '{authenticationRequest.ResponseMode}'.") { RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidRequest }; } } if (client.RequirePkce) { codeChallengeSecret.Validate(); } } catch (ArgumentException ex) { throw new OAuthRequestException(ex.Message, ex) { RouteBinding = RouteBinding, Error = IdentityConstants.ResponseErrors.InvalidRequest }; } }