// Based on https://stackoverflow.com/a/44322425/1249506
        public async Task <IdentityServerToken> GetIdentityServerTokenForUserAsync(User user)
        {
            var request          = new TokenCreationRequest();
            var identityPricipal = await PrincipalFactory.CreateAsync(user);

            var identityUser =
                new IdentityServerUser(user.Id.ToString())
            {
                AdditionalClaims   = identityPricipal.Claims.ToArray(),
                DisplayName        = user.UserName,
                AuthenticationTime = DateTime.UtcNow,
                IdentityProvider   = IdentityServerConstants.LocalIdentityProvider
            };

            request.Subject = identityUser.CreatePrincipal();
            request.IncludeAllIdentityClaims = true;
            request.ValidatedRequest         = new ValidatedRequest
            {
                Subject = request.Subject,
            };

            var client = await ClientStore.FindClientByIdAsync(ClientId);

            request.ValidatedRequest.SetClient(client);
            request.Resources =
                new Resources(await ResourceStore.FindEnabledIdentityResourcesByScopeAsync(client.AllowedScopes),
                              await ResourceStore.FindApiResourcesByScopeAsync(client.AllowedScopes))
            {
                OfflineAccess = client.AllowOfflineAccess
            };

            request.ValidatedRequest.Options      = Options;
            request.ValidatedRequest.ClientClaims = identityUser.AdditionalClaims;

            var token = await TokenService.CreateAccessTokenAsync(request);

            var accessToken = await TokenService.CreateSecurityTokenAsync(token);

            var refreshToken = await RefreshTokenService.CreateRefreshTokenAsync(request.Subject, token, client);

            return(new IdentityServerToken(token, accessToken, refreshToken));
        }
Exemple #2
0
        public async Task <IEndpointResult> ProcessAsync(HttpContext httpContext)
        {
            Logger.LogInformation($"[{nameof(InitRegistrationEndpoint)}] Started processing trusted device registration initiation endpoint.");
            var isPostRequest = HttpMethods.IsPost(httpContext.Request.Method);
            var isApplicationFormContentType = httpContext.Request.HasApplicationFormContentType();

            // Validate HTTP request type and method.
            if (!isPostRequest || !isApplicationFormContentType)
            {
                return(Error(OidcConstants.TokenErrors.InvalidRequest, "Request must be of type 'POST' and have a Content-Type equal to 'application/x-www-form-urlencoded'."));
            }
            // Ensure that a valid 'Authorization' header exists.
            var tokenUsageResult = await Token.Validate(httpContext);

            if (!tokenUsageResult.TokenFound)
            {
                return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "No access token is present in the request."));
            }
            // Validate request data and access token.
            var parameters = (await httpContext.Request.ReadFormAsync()).AsNameValueCollection();
            var requestValidationResult = await Request.Validate(parameters, tokenUsageResult.Token);

            if (requestValidationResult.IsError)
            {
                return(Error(requestValidationResult.Error, requestValidationResult.ErrorDescription));
            }
            // Ensure device is not already registered or belongs to any other user.
            var existingDevice = await UserDeviceStore.GetByDeviceId(requestValidationResult.DeviceId);

            var isNewDeviceOrOwnedByUser = existingDevice == null || existingDevice.UserId.Equals(requestValidationResult.UserId, StringComparison.OrdinalIgnoreCase);

            if (!isNewDeviceOrOwnedByUser)
            {
                return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "Device does not belong to the this user."));
            }
            // Ensure that the principal has declared a phone number which is also confirmed.
            // We will get these 2 claims by retrieving the identity resources from the store (using the requested scopes existing in the access token) and then calling the profile service.
            // This will help us make sure that the 'phone' scope was requested and finally allowed in the token endpoint.
            var identityResources = await ResourceStore.FindEnabledIdentityResourcesByScopeAsync(requestValidationResult.RequestedScopes);

            var resources          = new Resources(identityResources, Enumerable.Empty <ApiResource>(), Enumerable.Empty <ApiScope>());
            var validatedResources = new ResourceValidationResult(resources);

            if (!validatedResources.Succeeded)
            {
                return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "Identity resources could be validated."));
            }
            var requestedClaimTypes       = resources.IdentityResources.SelectMany(x => x.UserClaims).Distinct();
            var profileDataRequestContext = new ProfileDataRequestContext(requestValidationResult.Principal, requestValidationResult.Client, IdentityServerConstants.ProfileDataCallers.UserInfoEndpoint, requestedClaimTypes)
            {
                RequestedResources = validatedResources
            };
            await ProfileService.GetProfileDataAsync(profileDataRequestContext);

            var profileClaims            = profileDataRequestContext.IssuedClaims;
            var phoneNumberClaim         = profileClaims.FirstOrDefault(x => x.Type == JwtClaimTypes.PhoneNumber);
            var phoneNumberVerifiedClaim = profileClaims.FirstOrDefault(x => x.Type == JwtClaimTypes.PhoneNumberVerified);

            if (string.IsNullOrWhiteSpace(phoneNumberClaim?.Value) || phoneNumberVerifiedClaim == null || (bool.TryParse(phoneNumberVerifiedClaim.Value, out var phoneNumberVerified) && !phoneNumberVerified))
            {
                return(Error(OidcConstants.ProtectedResourceErrors.InvalidToken, "User does not have a phone number or the phone number is not verified."));
            }
            var otpAuthenticatedValue = profileClaims.FirstOrDefault(x => x.Type == BasicClaimTypes.OtpAuthenticated)?.Value;
            var otpAuthenticated      = !string.IsNullOrWhiteSpace(otpAuthenticatedValue) && bool.Parse(otpAuthenticatedValue);

            if (!otpAuthenticated)
            {
                // Send OTP code.
                void messageBuilder(TotpMessageBuilder message)
                {
                    var builder = message.UsePrincipal(requestValidationResult.Principal).WithMessage(IdentityMessageDescriber.DeviceRegistrationCodeMessage(existingDevice?.Name, requestValidationResult.InteractionMode));

                    if (requestValidationResult.DeliveryChannel == TotpDeliveryChannel.Sms)
                    {
                        builder.UsingSms();
                    }
                    else
                    {
                        builder.UsingViber();
                    }
                    builder.WithPurpose(Constants.TrustedDeviceOtpPurpose(requestValidationResult.UserId, requestValidationResult.DeviceId));
                }

                var totpResult = await TotpService.Send(messageBuilder);

                if (!totpResult.Success)
                {
                    return(Error(totpResult.Error));
                }
            }
            // Create endpoint response.
            var response = await Response.Generate(requestValidationResult);

            Logger.LogInformation($"[{nameof(InitRegistrationEndpoint)}] Trusted device registration initiation endpoint success.");
            return(new InitRegistrationResult(response));
        }