private async Task <string> SaveStateAsync(string oidcDiscoveryUri, string clientId, string callBackUri, string redirectUri, string codeVerifier = null, string nonce = null) { var state = RandomGenerator.GenerateNonce(32); var openidClientPkceState = new OpenidConnectPkceState { OidcDiscoveryUri = oidcDiscoveryUri, ClientId = clientId, CallBackUri = callBackUri, RedirectUri = redirectUri, CodeVerifier = codeVerifier, Nonce = nonce }; await sessionStorage.SetItemAsync(state, openidClientPkceState); return(state); }
private async Task <(ClaimsPrincipal idTokenPrincipal, TokenResponse tokenResponse)> AcquireTokensAsync(OpenidConnectPkceState openidClientPkceState, string code) { var tokenRequest = new TokenRequest { GrantType = IdentityConstants.GrantTypes.AuthorizationCode, Code = code, ClientId = openidClientPkceState.ClientId, RedirectUri = openidClientPkceState.CallBackUri, }; var codeVerifierSecret = new CodeVerifierSecret { CodeVerifier = openidClientPkceState.CodeVerifier, }; var oidcDiscovery = await GetOidcDiscoveryAsync(openidClientPkceState.OidcDiscoveryUri); var requestDictionary = tokenRequest.ToDictionary().AddToDictionary(codeVerifierSecret); if (openidClientPkceState.Resources?.Count() > 0) { var resourceRequest = new ResourceRequest { Resources = openidClientPkceState.Resources }; requestDictionary = requestDictionary.AddToDictionary(resourceRequest); } var request = new HttpRequestMessage(HttpMethod.Post, oidcDiscovery.TokenEndpoint); request.Content = new FormUrlEncodedContent(requestDictionary); var response = await GetHttpClient().SendAsync(request); switch (response.StatusCode) { case HttpStatusCode.OK: var result = await response.Content.ReadAsStringAsync(); var tokenResponse = result.ToObject <TokenResponse>(); tokenResponse.Validate(true); if (tokenResponse.AccessToken.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(tokenResponse.AccessToken), tokenResponse.GetTypeName()); } if (tokenResponse.ExpiresIn <= 0) { throw new ArgumentNullException(nameof(tokenResponse.ExpiresIn), tokenResponse.GetTypeName()); } // .NET 5.0 error, System.Security.Cryptography.RSA.Create() - System.PlatformNotSupportedException: System.Security.Cryptography.Algorithms is not supported on this platform. // https://github.com/dotnet/aspnetcore/issues/26123 // https://github.com/dotnet/runtime/issues/40074 // .NET 7 // https://github.com/dotnet/designs/blob/main/accepted/2021/blazor-wasm-crypto.md#net-7-plan #if !NET50 && !NET60 var oidcDiscoveryKeySet = await GetOidcDiscoveryKeysAsync(openidClientPkceState.OidcDiscoveryUri); (var idTokenPrincipal, _) = JwtHandler.ValidateToken(tokenResponse.IdToken, oidcDiscovery.Issuer, oidcDiscoveryKeySet.Keys.ToMSJsonWebKeys(), openidClientPkceState.ClientId, nameClaimType: globalOpenidClientPkceSettings.NameClaimType, roleClaimType: globalOpenidClientPkceSettings.RoleClaimType); #else var idTokenPrincipal = JwtHandler.ReadTokenClaims(tokenResponse.IdToken); #endif var nonce = idTokenPrincipal.Claims.Where(c => c.Type == JwtClaimTypes.Nonce).Select(c => c.Value).FirstOrDefault(); if (!openidClientPkceState.Nonce.Equals(nonce, StringComparison.Ordinal)) { throw new SecurityException("Nonce do not match."); } return(idTokenPrincipal, tokenResponse); case HttpStatusCode.BadRequest: var resultBadRequest = await response.Content.ReadAsStringAsync(); var tokenResponseBadRequest = resultBadRequest.ToObject <TokenResponse>(); tokenResponseBadRequest.Validate(true); throw new Exception($"Error login call back, Bad request. StatusCode={response.StatusCode}"); default: throw new Exception($"Error login call back, Status Code not expected. StatusCode={response.StatusCode}"); } }
private async Task <(int, ClaimsPrincipal, string, string)> AcquireTokensAsync(OpenidConnectPkceState openidClientPkceState, string code) { var tokenRequest = new TokenRequest { GrantType = IdentityConstants.GrantTypes.AuthorizationCode, Code = code, ClientId = openidClientPkceState.ClientId, RedirectUri = openidClientPkceState.CallBackUri, }; var codeVerifierSecret = new CodeVerifierSecret { CodeVerifier = openidClientPkceState.CodeVerifier, }; var oidcDiscovery = await GetOidcDiscoveryAsync(openidClientPkceState.OidcDiscoveryUri); var request = new HttpRequestMessage(HttpMethod.Post, oidcDiscovery.TokenEndpoint); request.Content = new FormUrlEncodedContent(tokenRequest.ToDictionary().AddToDictionary(codeVerifierSecret)); var httpClient = serviceProvider.GetService <HttpClient>(); var response = await httpClient.SendAsync(request); switch (response.StatusCode) { case HttpStatusCode.OK: var result = await response.Content.ReadAsStringAsync(); var tokenResponse = result.ToObject <TokenResponse>(); tokenResponse.Validate(true); if (tokenResponse.AccessToken.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(tokenResponse.AccessToken), tokenResponse.GetTypeName()); } if (tokenResponse.ExpiresIn <= 0) { throw new ArgumentNullException(nameof(tokenResponse.ExpiresIn), tokenResponse.GetTypeName()); } var oidcDiscoveryKeySet = await GetOidcDiscoveryKeysAsync(openidClientPkceState.OidcDiscoveryUri); (var idTokenPrincipal, _) = JwtHandler.ValidateToken(tokenResponse.IdToken, oidcDiscovery.Issuer, oidcDiscoveryKeySet.Keys, openidClientPkceState.ClientId, nameClaimType: globalOpenidClientPkceSettings.NameClaimType, roleClaimType: globalOpenidClientPkceSettings.RoleClaimType); var nonce = idTokenPrincipal.Claims.Where(c => c.Type == JwtClaimTypes.Nonce).Select(c => c.Value).SingleOrDefault(); if (!openidClientPkceState.Nonce.Equals(nonce, StringComparison.Ordinal)) { throw new SecurityException("Nonce do not match."); } return(tokenResponse.ExpiresIn, idTokenPrincipal, tokenResponse.IdToken, tokenResponse.AccessToken); case HttpStatusCode.BadRequest: var resultBadRequest = await response.Content.ReadAsStringAsync(); var tokenResponseBadRequest = resultBadRequest.ToObject <TokenResponse>(); tokenResponseBadRequest.Validate(true); throw new Exception($"Error login call back, Bad request. StatusCode={response.StatusCode}"); default: throw new Exception($"Error login call back, Status Code not expected. StatusCode={response.StatusCode}"); } }
public Task <OidcUserSession> CreateSessionAsync(DateTimeOffset validUntil, ClaimsPrincipal claimsPrincipal, TokenResponse tokenResponse, string sessionState, OpenidConnectPkceState openidClientPkceState) { return(CreateUpdateSessionAsync(validUntil, claimsPrincipal, tokenResponse, sessionState, openidClientPkceState.OidcDiscoveryUri, openidClientPkceState.ClientId)); }