Beispiel #1
0
        public async Task <DeviceAuthorizationResponse> Generate(DeviceAuthorizationRequestValidationResult validationResult)
        {
            var authorizationCode = new TrustedDeviceAuthorizationCode {
                ClientId        = validationResult.Client.ClientId,
                CodeChallenge   = validationResult.CodeChallenge.Sha256(),
                CreationTime    = SystemClock.UtcNow.UtcDateTime,
                DeviceId        = validationResult.Device.DeviceId,
                InteractionMode = validationResult.InteractionMode,
                Lifetime        = validationResult.Client.AuthorizationCodeLifetime,
                RequestedScopes = validationResult.RequestedScopes,
                Subject         = Principal.Create("TrustedDevice", new Claim(JwtClaimTypes.Subject, validationResult.UserId))
            };
            var challenge = await CodeChallengeStore.GenerateChallenge(authorizationCode);

            return(new DeviceAuthorizationResponse {
                Challenge = challenge
            });
        }
Beispiel #2
0
        public async Task <InitRegistrationResponse> Generate(InitRegistrationRequestValidationResult validationResult)
        {
            var authorizationCode = new TrustedDeviceAuthorizationCode {
                ClientId        = validationResult.Client.ClientId,
                DeviceId        = validationResult.DeviceId,
                InteractionMode = validationResult.InteractionMode,
                CodeChallenge   = validationResult.CodeChallenge.Sha256(),
                CreationTime    = SystemClock.UtcNow.UtcDateTime,
                Lifetime        = validationResult.Client.AuthorizationCodeLifetime,
                RequestedScopes = validationResult.RequestedScopes,
                Subject         = validationResult.Principal
            };
            var challenge = await CodeChallengeStore.GenerateChallenge(authorizationCode);

            return(new InitRegistrationResponse {
                Challenge = challenge
            });
        }
Beispiel #3
0
        private async Task <ValidationResult> ValidateAuthorizationCode(string code, TrustedDeviceAuthorizationCode authorizationCode, string codeVerifier, string deviceId, Client client)
        {
            // Validate that the current client is not trying to use an authorization code of a different client.
            if (authorizationCode.ClientId != client.ClientId)
            {
                return(Error(OidcConstants.TokenErrors.InvalidGrant, "Authorization code is invalid."));
            }
            // Validate that the current device is not trying to use an authorization code of a different device.
            if (authorizationCode.DeviceId != deviceId)
            {
                return(Error(OidcConstants.TokenErrors.InvalidGrant, "Authorization code is invalid."));
            }
            // Remove authorization code.
            await CodeChallengeStore.RemoveAuthorizationCode(code);

            // Validate code expiration.
            if (authorizationCode.CreationTime.HasExceeded(authorizationCode.Lifetime, SystemClock.UtcNow.UtcDateTime))
            {
                return(Error(OidcConstants.TokenErrors.InvalidGrant, "Authorization code is invalid."));
            }
            if (authorizationCode.CreationTime.HasExceeded(client.AuthorizationCodeLifetime, SystemClock.UtcNow.UtcDateTime))
            {
                return(Error(OidcConstants.TokenErrors.InvalidGrant, "Authorization code is invalid."));
            }
            if (authorizationCode.RequestedScopes == null || !authorizationCode.RequestedScopes.Any())
            {
                return(Error(OidcConstants.TokenErrors.InvalidGrant, "Authorization code is invalid."));
            }
            var proofKeyParametersValidationResult = ValidateAuthorizationCodeWithProofKeyParameters(codeVerifier, authorizationCode);

            if (proofKeyParametersValidationResult.IsError)
            {
                return(Error(proofKeyParametersValidationResult.Error, proofKeyParametersValidationResult.ErrorDescription));
            }
            return(Success());
        }
Beispiel #4
0
        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
            });
        }
Beispiel #5
0
        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);
            }
        }