如何为使用外部提供商登录的用户生成 AccessToken
How to Generate AccessToken for user who is logged in with External Providers
我有一个由 asp.net 内核实现的 API。
我已经使用 OpenIddict 为通过电子邮件和密码注册到我的 api 的用户生成访问令牌和刷新令牌。
我已经将 Google 中间件 (.UseGoogleAuthentication ... ) 添加到我的 API 并且我可以使用 Google 成功登录用户。
我的客户端是 UWP,在向 localhost/Account/ExternalLogin/Google 发送请求后,我使用 WebAuthenticationBroker 重定向到 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
现在如何为用户生成这些令牌?
now how do I generate these Tokens for the user?
OpenIddict 故意阻止您 return 来自非 OIDC 端点的 OIDC 响应(出于明显的安全原因)。
要使您的方案正常运行,您必须使用所有 OpenID Connect 参数将用户重定向回授权端点。
具体来说,您应该还原添加到 ExternalLoginConfirmation()
的所有更改(应该 return RedirectToLocal(returnUrl);
)并将 SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
部分移回您的授权控制器。
我有一个由 asp.net 内核实现的 API。 我已经使用 OpenIddict 为通过电子邮件和密码注册到我的 api 的用户生成访问令牌和刷新令牌。 我已经将 Google 中间件 (.UseGoogleAuthentication ... ) 添加到我的 API 并且我可以使用 Google 成功登录用户。 我的客户端是 UWP,在向 localhost/Account/ExternalLogin/Google 发送请求后,我使用 WebAuthenticationBroker 重定向到 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
现在如何为用户生成这些令牌?
now how do I generate these Tokens for the user?
OpenIddict 故意阻止您 return 来自非 OIDC 端点的 OIDC 响应(出于明显的安全原因)。
要使您的方案正常运行,您必须使用所有 OpenID Connect 参数将用户重定向回授权端点。
具体来说,您应该还原添加到 ExternalLoginConfirmation()
的所有更改(应该 return RedirectToLocal(returnUrl);
)并将 SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
部分移回您的授权控制器。