asp.net core identity extract and save external lo

2019-02-05 05:46发布

I am a stackoverflow noob so please go easy if I am doing this wrong.

I am using asp.net core with the default core identity template (local accounts).

I have accertained how to add claims to user principal when they login locally like so

[HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginInputModel model)
    {
        if (ModelState.IsValid)
        {
            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, set lockoutOnFailure: true

            var user = await _userManager.FindByNameAsync(model.Email);

            await _userManager.AddClaimAsync(user, new Claim("your-claim", "your-value"));

And I have figured out how to get claims returned from the external login but I cannot figure out how I would add these before the user principal gets created in the ExternalLoginCallback function

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));
        }
        else {
            // extract claims from external token here
        }

        // assume add claims to user here before cookie gets created??

        // 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)

I am assuming the the _signInManager.ExternalLoginSignInAsync function works similar to the local login _signInManager.PasswordSignInAsync in the sense that once it is called, the cookie will be created. But I am just not sure.

Essentially what I am hoping to achieve, is understanding of how to add custom claims into the cookie that gets created regardless of how to user logins in (local or external), and how to persist these claims to the database if required.

I am planning on doing some work where if I have a user login using say google auth, I need to save that access_token from google, because I wish to call into the Google APIs later with it. So I need to be able to include this access_token in with the User Principal that gets created, and I would hope the cookie would have a claim on it I could use at the front end as well.

This might be out of scope on this question but I would also like when the google token expires, for some-how it to use the refresh token and go get a new one, or force the user to relogin.

Any help on this would be super appreciated, I have really tried hard to understand this without posting this question to stackoverflow. I have read many articles with lots of useful info, but does not provide the answers this specific question is asking. So Thank you very much in advance.

cheers

1条回答
beautiful°
2楼-- · 2019-02-05 05:56

When you use await _userManager.AddClaimAsync(user, new Claim("your-claim", "your-value")); that actually updates the Identity's aspnetuserclaims table.

Whenever you sign in (by using _signInManager.PasswordSignIn or _signInManager.ExternalLoginSignInAsync) the claims from that table are read and added to the cookie that on every request becomes the Principal.

So you probably don't want to be calling the AddClaimAsync method from UserManager on every login.

Regarding external login providers, you have access to the claims when you call (in ExternalCallback and ExternalCallbackConfirmation if you are using the default templates) here:

var info = await _signInManager.GetExternalLoginInfoAsync();

The claims are in info.Principal.Claims.

The access token is not included by default. When it is, it will be here (along with the type and expiry date):

var accessToken = info.AuthenticationTokens.Single(f => f.Name == "access_token").Value;
var tokenType = info.AuthenticationTokens.Single(f => f.Name == "token_type").Value;
var expiryDate = info.AuthenticationTokens.Single(f => f.Name == "expires_at").Value;

To have the access token be included in the AuthenticationTokens collection, when you are configuring the GoogleAuthentication middleware set the SaveTokens flag to true:

        app.UseGoogleAuthentication(new GoogleOptions{
            ClientId = "...",
            ClientSecret = "...",
            SaveTokens = true

Now, if you want to have control over which claims go in the cookie you have to "take over" the process of creating the claims principal.

This is done for you when you use _signInManager.PasswordSignIn/ExternalLoginSignInAsync.

So, for example, for ExternalLoginSignInAsync replace:

var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);

With:

    var user =  await this._userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
    var claimsPrincipal = await this._signInManager.CreateUserPrincipalAsync(user);
    ((ClaimsIdentity)claimsPrincipal.Identity).AddClaim(new Claim("accessToken", info.AuthenticationTokens.Single(t => t.Name == "access_token").Value));
    await HttpContext.Authentication.SignInAsync("Identity.Application", claimsPrincipal);

"Identity.Application" is the default cookie name. You can change it in Startup's ConfigureServices method, for example to MainCookie:

        services.Configure<IdentityOptions>(options => {
            options.Cookies.ApplicationCookie.AuthenticationScheme = "MainCookie";
        });

You still need to handle the ExternalCallbackConfirmation action in the AccountController. It will be similar to the example above.

查看更多
登录 后发表回答