Как создать AccessToken для пользователя, который вошел в систему с внешними провайдерами

У меня есть API, реализованный ядром asp.net. Я использовал OpenIddict для создания токена доступа и обновления токена для пользователей, которые зарегистрировались в моем API по электронной почте и паролю. Я добавил промежуточное программное обеспечение Google (.UseGoogleAuthentication...) в свой API, и я могу успешно войти в систему с помощью Google. Мой клиент - UWP, и я использую WebAuthenticationBroker для перенаправления в Google после отправки запроса на localhost/Account/ExternalLogin/Google. когда пользователь входит в систему с помощью Google, он перенаправляется в Account/ExternalLoginConfirmation, что является тривиальным до этого момента, прежде чем он завершается с ExternalLoginConfirmation. Я хочу сгенерировать и отправить обратно токен доступа и токен обновления для пользователя, если закрывается WebAuthenticationBroker У меня нет другого способа получить токены для этого пользователя (потому что у него нет пароля и имя пользователя будет мне неизвестно). Это так:

//
    // POST: /Account/ 
    [HttpPost("ExternalLoginConfirmation")]
    [AllowAnonymous]
    //[ValidateAntiForgeryToken]
    public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model,
        string returnUrl = null)
    {
        if (ModelState.IsValid)
        {
            // Get the information about the user from the external login provider
            var info = await SignInManager.GetExternalLoginInfoAsync();
            if (info == null)
                return View("ExternalLoginFailure");
            var user = new UserInfo { UserName = model.Email, Email = model.Email };
            var result = await UserManager.CreateAsync(user);
            if (result.Succeeded)
            {
                result = await UserManager.AddLoginAsync(user, info);
                if (result.Succeeded)
                {
                    await SignInManager.SignInAsync(user, false);
                    Logger.LogInformation(6, "User created an account using {Name} provider.", info.LoginProvider);
                    var identity = new ClaimsIdentity(
                OpenIdConnectServerDefaults.AuthenticationScheme,
                OpenIdConnectConstants.Claims.Name, null);

                    // Add a "sub" claim containing the user identifier, and attach
                    // the "access_token" destination to allow OpenIddict to store it
                    // in the access token, so it can be retrieved from your controllers.
                    identity.AddClaim(OpenIdConnectConstants.Claims.Subject,
                        user.Id,
                        OpenIdConnectConstants.Destinations.AccessToken);

                    identity.AddClaim(OpenIdConnectConstants.Claims.Name, user.UserName,
                        OpenIdConnectConstants.Destinations.AccessToken);

                    // ... add other claims, if necessary.

                    var principal = new ClaimsPrincipal(identity);

                    var authenticateInfo = await HttpContext.Authentication.GetAuthenticateInfoAsync(info.LoginProvider);
                    var ticket = CreateTicketAsync(principal, authenticateInfo.Properties);

                    // Ask OpenIddict to generate a new token and return an OAuth2 token response.
                    return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
                    //return RedirectToLocal(returnUrl);
                }
            }
            AddErrors(result);
        }

        //ViewData["ReturnUrl"] = returnUrl;
        return BadRequest();
    }

    #region _helpers

    private AuthenticationTicket CreateTicketAsync(ClaimsPrincipal principal,
        AuthenticationProperties properties = null)
    {
        // Create a new authentication ticket holding the user identity.
        var ticket = new AuthenticationTicket(principal, properties,
            OpenIdConnectServerDefaults.AuthenticationScheme);

       ticket.SetScopes(new[]
       {
           /* openid: */ OpenIdConnectConstants.Scopes.OpenId,
           /* email: */ OpenIdConnectConstants.Scopes.Email,
           /* profile: */ OpenIdConnectConstants.Scopes.Profile,
           /* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess,
           /* roles: */ OpenIddictConstants.Scopes.Roles
       });

        ticket.SetAudiences(Configuration["Authentication:OpenIddict:Audience"]);

        return ticket;
    }

    private void AddErrors(IdentityResult result)
    {
        foreach (var error in result.Errors)
            ModelState.AddModelError(string.Empty, error.Description);
    }

    private IActionResult RedirectToLocal(string returnUrl)
    {
        if (Url.IsLocalUrl(returnUrl))
            return Redirect(returnUrl);
        return BadRequest();
    }

    #endregion

но это терпит неудачу и выдает исключение:an authorization or token response cannot be returned from this controller

Теперь, как я могу сгенерировать эти токены для пользователя?

1 ответ

Решение

Теперь, как я могу сгенерировать эти токены для пользователя?

OpenIddict намеренно запрещает вам возвращать ответы OIDC с конечных точек, не относящихся к OIDC (по очевидным причинам безопасности).

Чтобы ваш сценарий работал, вы должны перенаправить пользователей обратно в конечную точку авторизации со всеми параметрами OpenID Connect.

Конкретно, вы должны отменить все изменения, добавленные в ExternalLoginConfirmation() (это должно вернуться RedirectToLocal(returnUrl);) и переместить SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); часть обратно к вашему контроллеру авторизации.

Другие вопросы по тегам