public override async Task <CompleteRegistrationRequestValidationResult> Validate(NameValueCollection parameters, string accessToken = null) { Logger.LogDebug($"[{nameof(CompleteRegistrationRequestValidator)}] Started trusted device registration request validation."); // The access token needs to be valid and have at least the openid scope. var tokenValidationResult = await TokenValidator.ValidateAccessTokenAsync(accessToken, IdentityServerConstants.StandardScopes.OpenId); if (tokenValidationResult.IsError) { return(Error(tokenValidationResult.Error, "Provided access token is not valid.")); } // The access token must have at lease a 'sub' and 'client_id' claim. var claimsToValidate = new[] { JwtClaimTypes.Subject, JwtClaimTypes.ClientId }; foreach (var claim in claimsToValidate) { var claimValue = tokenValidationResult.Claims.SingleOrDefault(x => x.Type == claim)?.Value; if (string.IsNullOrWhiteSpace(claimValue)) { return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, $"Access token must contain the '{claim}' claim.")); } } // Get 'code' parameter and retrieve it from the store. var code = parameters.Get(RegistrationRequestParameters.Code); if (string.IsNullOrWhiteSpace(code)) { return(Error(OidcConstants.TokenErrors.InvalidGrant, $"Parameter '{nameof(RegistrationRequestParameters.Code)}' is not specified.")); } var authorizationCode = await CodeChallengeStore.GetAuthorizationCode(code); if (authorizationCode == null) { return(Error(OidcConstants.TokenErrors.InvalidGrant, "Authorization code is invalid.")); } // Validate that the consumer specified all required parameters. var parametersToValidate = new List <string> { RegistrationRequestParameters.CodeSignature, RegistrationRequestParameters.CodeVerifier, RegistrationRequestParameters.DeviceId, RegistrationRequestParameters.DevicePlatform }; if (authorizationCode.InteractionMode == InteractionMode.Fingerprint) { parametersToValidate.Add(RegistrationRequestParameters.PublicKey); } if (authorizationCode.InteractionMode == InteractionMode.Pin) { parametersToValidate.Add(RegistrationRequestParameters.Pin); } var otpAuthenticatedValue = tokenValidationResult.Claims.FirstOrDefault(x => x.Type == BasicClaimTypes.OtpAuthenticated)?.Value; var otpAuthenticated = !string.IsNullOrWhiteSpace(otpAuthenticatedValue) && bool.Parse(otpAuthenticatedValue); if (!otpAuthenticated) { parametersToValidate.Add(RegistrationRequestParameters.OtpCode); } foreach (var parameter in parametersToValidate) { var parameterValue = parameters.Get(parameter); if (string.IsNullOrWhiteSpace(parameterValue)) { return(Error(OidcConstants.TokenErrors.InvalidGrant, $"Parameter '{parameter}' is not specified.")); } } var isValidPlatform = Enum.TryParse(typeof(DevicePlatform), parameters.Get(RegistrationRequestParameters.DevicePlatform), out var platform); if (!isValidPlatform) { return(Error(OidcConstants.TokenErrors.InvalidRequest, $"Parameter '{nameof(RegistrationRequestParameters.DevicePlatform)}' does not have a valid value.")); } // Load client and validate that it allows the 'password' flow. var client = await LoadClient(tokenValidationResult); if (client == null) { return(Error(OidcConstants.AuthorizeErrors.UnauthorizedClient, $"Client is unknown or not enabled.")); } if (client.AllowedGrantTypes.Except(Constants.RequiredGrantTypes).Any()) { return(Error(OidcConstants.AuthorizeErrors.UnauthorizedClient, $"Client not authorized any of the following grant types: {string.Join(", ", Constants.RequiredGrantTypes)}")); } // Validate authorization code against code verifier given by the client. var codeVerifier = parameters.Get(RegistrationRequestParameters.CodeVerifier); var authorizationCodeValidationResult = await ValidateAuthorizationCode(code, authorizationCode, codeVerifier, parameters.Get(RegistrationRequestParameters.DeviceId), client); if (authorizationCodeValidationResult.IsError) { return(Error(authorizationCodeValidationResult.Error, authorizationCodeValidationResult.ErrorDescription)); } // Validate given public key against signature for fingerprint. string publicKey = null; if (authorizationCode.InteractionMode == InteractionMode.Fingerprint) { publicKey = parameters.Get(RegistrationRequestParameters.PublicKey); var codeSignature = parameters.Get(RegistrationRequestParameters.CodeSignature); var publicKeyValidationResult = ValidateSignature(publicKey, code, codeSignature); if (publicKeyValidationResult.IsError) { return(Error(publicKeyValidationResult.Error, publicKeyValidationResult.ErrorDescription)); } } // Find requested scopes. var requestedScopes = tokenValidationResult.Claims.Where(claim => claim.Type == JwtClaimTypes.Scope).Select(claim => claim.Value).ToList(); // Create principal from incoming access token excluding protocol claims. var claims = tokenValidationResult.Claims.Where(x => !Constants.ProtocolClaimsFilter.Contains(x.Type)); var principal = Principal.Create("TrustedDevice", claims.ToArray()); var userId = tokenValidationResult.Claims.Single(x => x.Type == JwtClaimTypes.Subject).Value; // Validate OTP code, if needed. if (!otpAuthenticated) { var totpResult = await TotpService.Verify( principal : principal, code : parameters.Get(RegistrationRequestParameters.OtpCode), purpose : Constants.TrustedDeviceOtpPurpose(userId, authorizationCode.DeviceId) ); if (!totpResult.Success) { return(Error(totpResult.Error)); } } // Finally return result. return(new CompleteRegistrationRequestValidationResult { IsError = false, Client = client, DeviceId = authorizationCode.DeviceId, DeviceName = parameters.Get(RegistrationRequestParameters.DeviceName), DevicePlatform = (DevicePlatform)platform, InteractionMode = authorizationCode.InteractionMode, Pin = parameters.Get(RegistrationRequestParameters.Pin), Principal = principal, PublicKey = publicKey, RequestedScopes = requestedScopes, UserId = userId }); }
public async Task ValidateAsync(ExtensionGrantValidationContext context) { var parameters = context.Request.Raw; var deviceId = parameters.Get(RegistrationRequestParameters.DeviceId); if (string.IsNullOrWhiteSpace(deviceId)) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, $"Parameter '{RegistrationRequestParameters.DeviceId}' is not specified."); return; } // Load device. var device = await UserDeviceStore.GetByDeviceId(deviceId); if (device == null) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Device is unknown."); return; } // If a code is present we are heading towards fingerprint login. var code = parameters.Get(RegistrationRequestParameters.Code); if (!string.IsNullOrWhiteSpace(code)) { // Retrieve authorization code from the store. var authorizationCode = await CodeChallengeStore.GetAuthorizationCode(code); if (authorizationCode == null) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Authorization code is invalid."); return; } // Validate that the consumer specified all required parameters. var parametersToValidate = new List <string> { RegistrationRequestParameters.CodeSignature, RegistrationRequestParameters.CodeVerifier }; foreach (var parameter in parametersToValidate) { var parameterValue = parameters.Get(parameter); if (string.IsNullOrWhiteSpace(parameterValue)) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, $"Parameter '{parameter}' is not specified."); return; } } // Validate authorization code against code verifier given by the client. var codeVerifier = parameters.Get(RegistrationRequestParameters.CodeVerifier); var authorizationCodeValidationResult = await ValidateAuthorizationCode(code, authorizationCode, codeVerifier, deviceId, context.Request.Client); if (authorizationCodeValidationResult.IsError) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, authorizationCodeValidationResult.ErrorDescription); return; } // Validate given public key against signature for fingerprint. var publicKey = parameters.Get(RegistrationRequestParameters.PublicKey); var codeSignature = parameters.Get(RegistrationRequestParameters.CodeSignature); var publicKeyValidationResult = ValidateSignature(publicKey, code, codeSignature); if (publicKeyValidationResult.IsError) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, publicKeyValidationResult.ErrorDescription); return; } await UserDeviceStore.UpdateDevicePublicKey(device, publicKey); // Grant access token. context.Result = new GrantValidationResult(authorizationCode.Subject.GetSubjectId(), GrantType); await UserDeviceStore.UpdateLastSignInDate(device); } var pin = parameters.Get(RegistrationRequestParameters.Pin); if (!string.IsNullOrWhiteSpace(pin)) { var result = DevicePasswordHasher.VerifyHashedPassword(device, device.Password, pin); if (result == PasswordVerificationResult.Failed) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Wrong pin."); return; } context.Result = new GrantValidationResult(device.UserId, GrantType); await UserDeviceStore.UpdateLastSignInDate(device); } }