public static async Task ValidateAsync_WrongAudience()
        {
            AuthenticationSettings authenticationSettings = new()
            {
                Google = new GoogleAuthenticationSettings
                {
                    ClientId = ClientId,
                },
            };
            IOptions <AuthenticationSettings> options = Options.Create(authenticationSettings);

            using (HttpClientTestingFactory http = new())
            {
                GoogleAssertionGrantHandler handler    = new(options, http.HttpClient);
                Task <AssertionGrantResult> resultTask = handler.ValidateAsync(Assertion);

                http.Expect(ValidationEndpoint).Respond(JsonConvert.SerializeObject(new JsonWebToken
                {
                    Aud = "SomeOtherClientId",
                    Sub = ExternalUserId,
                }));

                AssertionGrantResult result = await resultTask;
                Assert.NotNull(result);
                Assert.False(result.IsSuccessful);

                http.EnsureNoOutstandingRequests();
            }
        }
Example #2
0
        public void IsSuccessful(bool isSuccessful, string externalUserId, string externalUserEmail, string error)
        {
            var result = new AssertionGrantResult
            {
                ExternalUserId    = externalUserId,
                ExternalUserEmail = externalUserEmail,
                Error             = error,
            };

            Assert.Equal(isSuccessful, result.IsSuccessful);
        }
        public static async Task ValidateAsync_HttpError()
        {
            AuthenticationSettings            authenticationSettings = new();
            IOptions <AuthenticationSettings> options = Options.Create(authenticationSettings);

            using (HttpClientTestingFactory http = new())
            {
                GoogleAssertionGrantHandler handler    = new(options, http.HttpClient);
                Task <AssertionGrantResult> resultTask = handler.ValidateAsync(Assertion);

                http.Expect(ValidationEndpoint).Respond(HttpStatusCode.BadRequest);

                AssertionGrantResult result = await resultTask;
                Assert.NotNull(result);
                Assert.False(result.IsSuccessful);

                http.EnsureNoOutstandingRequests();
            }
        }
        public async Task <ActionResult> ExchangeAsync()
        {
            OpenIddictRequest request = HttpContext.GetOpenIddictServerRequest();

            if (request.IsPasswordGrantType())
            {
                // Allow the user to log in with their email address too.
                // We already check that usernames are only "word" chars (\w+), so this check is sufficient.
                ApplicationUser user = request.Username.Contains("@", StringComparison.Ordinal)
                    ? await _userManager.FindByEmailAsync(request.Username)
                    : await _userManager.FindByNameAsync(request.Username);

                if (user == null)
                {
                    AuthenticationProperties properties = new(new Dictionary <string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid.",
                    });

                    return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                }

                // Validate the username/password parameters and ensure the account is not locked out.
                SignInResult result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure : false);

                if (!result.Succeeded)
                {
                    AuthenticationProperties properties = new(new Dictionary <string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid.",
                    });

                    return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                }

                return(await SignInAsync(request, user));
            }

            if (request.IsRefreshTokenGrantType())
            {
                // Retrieve the claims principal stored in the refresh token.
                AuthenticateResult result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

                // Retrieve the user profile corresponding to the refresh token.
                ApplicationUser user = await _signInManager.ValidateSecurityStampAsync(result.Principal);

                if (user == null)
                {
                    AuthenticationProperties properties = new(new Dictionary <string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The refresh token is no longer valid.",
                    });

                    return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                }

                // Ensure the user is still allowed to sign in.
                if (!await _signInManager.CanSignInAsync(user))
                {
                    AuthenticationProperties properties = new(new Dictionary <string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in.",
                    });

                    return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                }

                // Reuse the properties stored in the refresh token, including the scopes originally granted.
                return(await SignInAsync(request, user));
            }

            IAssertionGrantHandler assertionGrantHandler = _assertionGrantHandlerProvider.GetHandler(request.GrantType);

            if (assertionGrantHandler != null)
            {
                // Reject the request if the "assertion" parameter is missing.
                if (string.IsNullOrEmpty(request.Assertion))
                {
                    AuthenticationProperties properties = new(new Dictionary <string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The mandatory 'assertion' parameter was missing.",
                    });

                    return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                }

                AssertionGrantResult validationResult = await assertionGrantHandler.ValidateAsync(request.Assertion);

                if (!validationResult.IsSuccessful)
                {
                    AuthenticationProperties properties = new(new Dictionary <string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = validationResult.Error,
                    });

                    return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                }

                // Find the user associated with this external log in
                ApplicationUser user = await _userManager.FindByLoginAsync(assertionGrantHandler.Name, validationResult.ExternalUserId);

                if (user == null)
                {
                    if (!string.IsNullOrEmpty(request.Username))
                    {
                        // They provided a user name, so try to implicitly create an account for them
                        user = new ApplicationUser {
                            UserName = request.Username, Email = validationResult.ExternalUserEmail
                        };
                        IdentityResult creationResult = await _userManager.CreateAsync(user);

                        if (!creationResult.Succeeded)
                        {
                            AuthenticationProperties properties = new(new Dictionary <string, string>
                            {
                                [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = string.Join(" ", creationResult.Errors.Select(error => error.Description)),
                            });

                            return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                        }
                    }

                    if (user == null)
                    {
                        // If the user is already logged in, use the current user
                        user = await _userManager.GetUserAsync(User);
                    }

                    // Add the login if we found a user
                    if (user != null)
                    {
                        UserLoginInfo  login          = new(assertionGrantHandler.Name, validationResult.ExternalUserId, assertionGrantHandler.Name);
                        IdentityResult addLoginResult = await _userManager.AddLoginAsync(user, login);

                        if (!addLoginResult.Succeeded)
                        {
                            AuthenticationProperties properties = new(new Dictionary <string, string>
                            {
                                [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = string.Join(" ", addLoginResult.Errors.Select(error => error.Description)),
                            });

                            return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                        }
                    }
                    else
                    {
                        // Ask the user to create an account.
                        AuthenticationProperties properties = new(new Dictionary <string, string>
                        {
                            [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.AccountSelectionRequired,
                        });

                        return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                    }
                }

                // Ensure the user is allowed to sign in.
                if (!await _signInManager.CanSignInAsync(user))
                {
                    AuthenticationProperties properties = new(new Dictionary <string, string>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in.",
                    });

                    return(Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
                }

                return(await SignInAsync(request, user));
            }

            throw new NotImplementedException("The specified grant type is not implemented.");
        }