public async Task<AuthorizeResult> AuthorizeAsync(bool trySilent = false, object extraParameters = null) { InvokeResult wviResult; AuthorizeResult result = new AuthorizeResult { IsError = true, }; // todo: replace with CryptoRandom result.Nonce = Guid.NewGuid().ToString("N"); result.RedirectUri = _options.RedirectUri; string codeChallenge = CreateCodeChallenge(result); var url = await CreateUrlAsync(result, codeChallenge, extraParameters); var webViewOptions = new InvokeOptions(url, _options.RedirectUri); if (trySilent) { webViewOptions.InitialDisplayMode = DisplayMode.Hidden; } if (_options.UseFormPost) { webViewOptions.ResponseMode = ResponseMode.FormPost; } // try silent mode if requested wviResult = await _options.WebView.InvokeAsync(webViewOptions); if (wviResult.ResultType == InvokeResultType.Success) { return await ParseResponse(wviResult.Response, result); } result.Error = wviResult.ResultType.ToString(); return result; }
private async Task<LoginResult> ValidateAsync(AuthorizeResult result) { // validate identity token var principal = await ValidateIdentityTokenAsync(result.IdentityToken); if (principal == null) { return new LoginResult { Success = false, Error = "identity token validation error" }; } // validate nonce var tokenNonce = principal.FindFirst("nonce")?.Value ?? ""; if (!string.Equals(result.Nonce, tokenNonce)) { return new LoginResult { Success = false, Error = "invalid nonce" }; } // validate audience var audience = principal.FindFirst("aud")?.Value ?? ""; if (!string.Equals(_options.ClientId, audience)) { return new LoginResult { Success = false, Error = "invalid audience" }; } // validate c_hash var cHash = principal.FindFirst("c_hash")?.Value ?? ""; var sha256 = HashAlgorithmProvider.OpenAlgorithm("SHA256"); var codeHash = sha256.HashData( CryptographicBuffer.CreateFromByteArray( Encoding.ASCII.GetBytes(result.Code))); byte[] codeHashArray; CryptographicBuffer.CopyToByteArray(codeHash, out codeHashArray); byte[] leftPart = new byte[16]; Array.Copy(codeHashArray, leftPart, 16); var leftPartB64 = Base64Url.Encode(leftPart); if (!leftPartB64.Equals(cHash)) { return new LoginResult { Success = false, Error = "invalid code" }; } var endpoints = await _options.GetEndpointsAsync(); // get access token var tokenClient = new TokenClient(endpoints.Token, _options.ClientId, _options.ClientSecret); var tokenResult = await tokenClient.RequestAuthorizationCodeAsync( result.Code, result.RedirectUri, codeVerifier: result.Verifier); if (tokenResult.IsError || tokenResult.IsHttpError) { return new LoginResult { Success = false, Error = tokenResult.Error }; } // get profile if enabled if (_options.LoadProfile) { var userInfoClient = new UserInfoClient(new Uri(endpoints.UserInfo), tokenResult.AccessToken); var userInfoResponse = await userInfoClient.GetAsync(); var primaryClaimTypes = principal.Claims.Select(c => c.Type).Distinct(); foreach (var claim in userInfoResponse.Claims.Where(c => !primaryClaimTypes.Contains(c.Item1))) { principal.Identities.First().AddClaim(new Claim(claim.Item1, claim.Item2)); } } // success return new LoginResult { Success = true, Principal = FilterProtocolClaims(principal), AccessToken = tokenResult.AccessToken, RefreshToken = tokenResult.RefreshToken, AccessTokenExpiration = DateTime.Now.AddSeconds(tokenResult.ExpiresIn), IdentityToken = result.IdentityToken, AuthenticationTime = DateTime.Now }; }
private string CreateCodeChallenge(AuthorizeResult result) { if (_options.UseProofKeys) { // todo: replace with CryptoRandom result.Verifier = Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N"); var sha256 = HashAlgorithmProvider.OpenAlgorithm("SHA256"); var challengeBuffer = sha256.HashData( CryptographicBuffer.CreateFromByteArray( Encoding.ASCII.GetBytes(result.Verifier))); byte[] challengeBytes; CryptographicBuffer.CopyToByteArray(challengeBuffer, out challengeBytes); return Base64Url.Encode(challengeBytes); } else { return null; } }
private Task<AuthorizeResult> ParseResponse(string webViewResponse, AuthorizeResult result) { var response = new AuthorizeResponse(webViewResponse); if (response.IsError) { result.Error = response.Error; return Task.FromResult(result); } if (string.IsNullOrEmpty(response.Code)) { result.Error = "Missing authorization code"; return Task.FromResult(result); } if (string.IsNullOrEmpty(response.IdentityToken)) { result.Error = "Missing identity token"; return Task.FromResult(result); } result.IdentityToken = response.IdentityToken; result.Code = response.Code; result.IsError = false; return Task.FromResult(result); }
private async Task<string> CreateUrlAsync(AuthorizeResult result, string codeChallenge, object extraParameters) { var request = new AuthorizeRequest((await _options.GetEndpointsAsync()).Authorize); var url = request.CreateAuthorizeUrl( clientId: _options.ClientId, responseType: OidcConstants.ResponseTypes.CodeIdToken, scope: _options.Scope, redirectUri: result.RedirectUri, responseMode: _options.UseFormPost ? OidcConstants.ResponseModes.FormPost : null, nonce: result.Nonce, codeChallenge: codeChallenge, codeChallengeMethod: _options.UseProofKeys ? OidcConstants.CodeChallengeMethods.Sha256 : null, extra: extraParameters); return url; }