ASP.NET Identity(使用 IdentityServer4)获取外部资源 oauth 访问令牌

Posted

技术标签:

【中文标题】ASP.NET Identity(使用 IdentityServer4)获取外部资源 oauth 访问令牌【英文标题】:ASP.NET Identity (with IdentityServer4) get external resource oauth access token 【发布时间】:2017-04-23 18:26:28 【问题描述】:

我已经阅读了 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 并将其直接存储在与登录用户相关联的数据库中?

我真的需要这个,因为我的大部分功能都是基于来自第三方的数据。

【问题讨论】:

【参考方案1】:

好的,所以我终于得到了这个工作。我创建了一个使用ASP.Net IdentityIdentityServer4 的新项目,它们都建立在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 关联的数据库表中。在AspNetUserTokens 表中,您现在将有 3 个条目,称为: access_tokenexpires_attoken_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");

【讨论】:

嗨,Kristian,您是在一个 asp.net 项目中使用所有部分,还是为 IdentityServer 使用不同的端点? @AmrElsehemy 我将身份服务器托管在一个单独的项目中。我们正在使用微服务,我们的其他 API 通过访问令牌验证中间件与身份服务器进行通信。希望这能回答您的问题。 谢谢,另外一个问题,身份服务器是托管在同一个端点还是外部,哪个更好用? @AmrElsehemy 我不确定您要问的是什么。我的 IdentityServer 仅托管在我们域的子域上。是专门针对外部登录的吗?如果是这样,我建议您遵循标准设置,该设置显示在身份服务器 readthedocs 页面上。 @KristianBarrett,你如何将此访问令牌检索到 web api 中?

以上是关于ASP.NET Identity(使用 IdentityServer4)获取外部资源 oauth 访问令牌的主要内容,如果未能解决你的问题,请参考以下文章

Scope_Identity()、Identity()、@@Identity 和 Ident_Current() 有啥区别?

使用 SCOPE_IDENTITY/IDENT_CURRENT 时出现“直接执行 SQL;没有游标”错误

sql IDENTITY,@@ IDENTITY,SCOPE_IDENTITY(),IDENT_CURRENT(),CREATE SEQUENCE

使用 Identity Server 4 和 ASP.NET Identity 添加外部登录

ASP.NET Identity教程ASP.NET Identity入门

asp.net identity 3.0.0 在MVC下的基本使用 序言