ASP.NET 身份(使用 IdentityServer4)获取外部资源 oauth 访问令牌
ASP.NET Identity (with IdentityServer4) get external resource oauth access token
我已经阅读了 identityServer4 的文档,并将其设置为使用 Microsoft Office 365 作为登录提供程序。当用户登录后,我想制作一个按钮,他可以让我的应用程序使用 graph.microsoft.com
的 webhooks api 订阅他的日历事件
代码在startup.cs
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountOptions
{
AuthenticationScheme = "Microsoft",
DisplayName = "Microsoft",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = "CLIENT ID",
ClientSecret = "CLIENT SECRET",
CallbackPath = new PathString("/signin-microsoft"),
Events = new OAuthEvents
{
OnCreatingTicket = context =>
{
redisCache.Set("AccessToken", context.AccessToken.GetBytes(), new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(3)
});
return Task.FromResult(context);
}
}
Scope =
{
"Calendars.Read",
"Calendars.Read.Shared",
},
SaveTokens = true
});
但这显然不是一条可行的道路。我这样做只是为了测试目的,并制作所需订阅的 PoC。
现在我想知道是否有更智能的方式与 identityServer 通信,允许我获取此外部访问令牌,以便我可以代表我登录使用 Microsoft api用户?
或者我唯一的选择是直接从此 OAuthEvent 获取 Microsoft AccessToken 并将其直接存储在数据库中,链接到登录用户吗?
我真的需要这个,因为我的大部分功能都基于来自第三方的数据。
好的,所以我终于开始工作了。我创建了一个使用 ASP.Net Identity
和 IdentityServer4
的新项目,它们都构建在 ASP.Net Core
.
之上
问题是我没有完全意识到外部登录过程中使用的流程。
如果您使用两个系统的样板,您将有一个 AccountController
,其中将存在以下方法:
//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
这里的重要部分是:
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
这会将您的外部凭据保存在与您的 ASP.Net identity
关联的数据库 table 中。在 table AspNetUserTokens
中,您现在将有 3 个条目,名称如下:
access_token
、expires_at
和 token_type
。
这些是我们感兴趣的令牌,我们可以使用它们来访问我们应用程序中其他地方的用户凭据。
要在登录用户的上下文中获取这些令牌:
var externalAccessToken = await _userManager.GetAuthenticationTokenAsync(User, "Microsoft", "access_token");
要为我们从数据库中获取的用户获取它们,我们可以使用:
var user = _userManager.Users.SingleOrDefault(x => x.Id == "myId");
if (user == null)
return;
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
var externalAccessToken = await _userManager.GetAuthenticationTokenAsync(claimsPrincipal, "Microsoft", "access_token");
我已经阅读了 identityServer4 的文档,并将其设置为使用 Microsoft Office 365 作为登录提供程序。当用户登录后,我想制作一个按钮,他可以让我的应用程序使用 graph.microsoft.com
的 webhooks api 订阅他的日历事件代码在startup.cs
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountOptions
{
AuthenticationScheme = "Microsoft",
DisplayName = "Microsoft",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
ClientId = "CLIENT ID",
ClientSecret = "CLIENT SECRET",
CallbackPath = new PathString("/signin-microsoft"),
Events = new OAuthEvents
{
OnCreatingTicket = context =>
{
redisCache.Set("AccessToken", context.AccessToken.GetBytes(), new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(3)
});
return Task.FromResult(context);
}
}
Scope =
{
"Calendars.Read",
"Calendars.Read.Shared",
},
SaveTokens = true
});
但这显然不是一条可行的道路。我这样做只是为了测试目的,并制作所需订阅的 PoC。
现在我想知道是否有更智能的方式与 identityServer 通信,允许我获取此外部访问令牌,以便我可以代表我登录使用 Microsoft api用户?
或者我唯一的选择是直接从此 OAuthEvent 获取 Microsoft AccessToken 并将其直接存储在数据库中,链接到登录用户吗?
我真的需要这个,因为我的大部分功能都基于来自第三方的数据。
好的,所以我终于开始工作了。我创建了一个使用 ASP.Net Identity
和 IdentityServer4
的新项目,它们都构建在 ASP.Net Core
.
问题是我没有完全意识到外部登录过程中使用的流程。
如果您使用两个系统的样板,您将有一个 AccountController
,其中将存在以下方法:
//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
这里的重要部分是:
await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
这会将您的外部凭据保存在与您的 ASP.Net identity
关联的数据库 table 中。在 table AspNetUserTokens
中,您现在将有 3 个条目,名称如下:
access_token
、expires_at
和 token_type
。
这些是我们感兴趣的令牌,我们可以使用它们来访问我们应用程序中其他地方的用户凭据。
要在登录用户的上下文中获取这些令牌:
var externalAccessToken = await _userManager.GetAuthenticationTokenAsync(User, "Microsoft", "access_token");
要为我们从数据库中获取的用户获取它们,我们可以使用:
var user = _userManager.Users.SingleOrDefault(x => x.Id == "myId");
if (user == null)
return;
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
var externalAccessToken = await _userManager.GetAuthenticationTokenAsync(claimsPrincipal, "Microsoft", "access_token");