Search code examples
c#asp.netasp.net-web-api2asp.net-identitytwitter-oauth

Add Claims to Asp.Net Web API 2 Bearer Token in ASP.net Identity?


Is it possible to add claims to a Bearer Token in order to access those claims in the future? This is the Bearer Token that is issued by the Web API. It is contained in the URL that is displayed upon redirect from the Twitter app authorization process.

The reason I ask is that I'm trying to store the Twitter AccessToken and AccessTokenSecret as additional claims. I'm doing this so that I can access these claims when I access the GetUserInfo method using a Bearer Token. From what I can tell, this is not possible.

It seems like I might have to store these in SQL and retrieve them using the Twitter userName when I access GetUserInfo. When I access GetUserInfo, I get the Twitter userName and userId, but I can't get the AccessToken and AccessTokenSecret. I might be able to store the context.ScreenName, context.AccessToken and context.AccessTokenSecret in SQL using the public override Task Authenticated(TwitterAuthenticatedContext context) method.

I need the AccessToken and AccessTokenSecret, so I can call this Twitter endpoint to get the user's email address.

https://api.twitter.com/1.1/account/verify_credentials.json

At this stage in the process, my user is not logged in and their local account has not been created. I am attempting to get the email address from Twitter in order to create their local account.

UPDATE 1

Here is some code. I'm calling this method from Provider in TwitterAuthenticationOptions in Startup.Auth like so. As you can see, I'm adding the claims to the context in Authenticated.

Provider = new TwitterAuthProvider(), 

public class TwitterAuthProvider : TwitterAuthenticationProvider
{
    public string XmlSchemaString { get; private set; }

    public override Task Authenticated(TwitterAuthenticatedContext context)
    {
        context.Identity.AddClaim(new Claim("access_token", context.AccessToken, XmlSchemaString, "Twitter"));
        context.Identity.AddClaim(new Claim("access_token_secret", context.AccessTokenSecret, XmlSchemaString, "Twitter"));
        context.Identity.AddClaim(new Claim("user_name", context.ScreenName, XmlSchemaString, "Twitter"));
        context.Identity.AddClaim(new Claim("account_type", "Twitter", XmlSchemaString, "Twitter"));

        return Task.FromResult<object>(null);
    }

}

Any help is much appreciated. Thanks in advance!


Solution

  • OK, I finally got this to work. The key is in the ExternalLoginData method (see below). This method gets called when the application redirects back from Twitter, Facebook etc... I assume this is part of the https://api.yourdomain.com/signin-twitter and https://api.yourdomain.com/signin-facebook etc... callbacks. Based on what I can gather, return new ExternalLoginData gets the AccessToken and AccessTokenSecret. GetClaims is then called. If the AccessToken and AccessTokenSecret are not null, they get re-added as Claims. These claims can then be accessed in the GetUserInfo or RegisterExternal method using this line of code.

    ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity). 
    

    You can then get the exact values using this code.

    AccessToken = externalLogin != null ? externalLogin.AccessToken : null,
                        AccessTokenSecret = externalLogin != null ? externalLogin.AccessTokenSecret : null
    

    Here is the new ExternalLoginData method.

    private class ExternalLoginData
            {
                public string LoginProvider { get; set; }
                public string ProviderKey { get; set; }
                public string UserName { get; set; }
                public string AccessToken { get; set; }
                public string AccessTokenSecret { get; set; }
    
    
                public IList<Claim> GetClaims()
                {
                    IList<Claim> claims = new List<Claim>();
                    claims.Add(new Claim(ClaimTypes.NameIdentifier, ProviderKey, null, LoginProvider));
    
                    if (UserName != null)
                    {
                        claims.Add(new Claim(ClaimTypes.Name, UserName, null, LoginProvider));
                    }
    
                    if (AccessToken != null)
                    {
                        claims.Add(new Claim("access_token", AccessToken, null, LoginProvider));
                    }
    
                    if (AccessTokenSecret != null)
                    {
                        claims.Add(new Claim("access_token_secret", AccessTokenSecret, null, LoginProvider));
                    }
    
                    return claims;
                }
    
                public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
                {
                    if (identity == null)
                    {
                        return null;
                    }
    
                    Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
    
                    if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
                        || String.IsNullOrEmpty(providerKeyClaim.Value))
                    {
                        return null;
                    }
    
                    if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
                    {
                        return null;
                    }
    
                    return new ExternalLoginData
                    {
                        LoginProvider = providerKeyClaim.Issuer,
                        ProviderKey = providerKeyClaim.Value,
                        UserName = identity.FindFirstValue(ClaimTypes.Name),
                        AccessToken = identity.Claims.FirstOrDefault(x => x.Type.Contains("access_token")).Value,
                        AccessTokenSecret = identity.Claims.FirstOrDefault(x => x.Type.Contains("access_token_secret")).Value,
                    };
                }
            }
    

    I'm then using the public static TwitterDto TwitterLogin method to get the email address from Twitter (see link below).

    https://stackoverflow.com/a/36356009/5360237

    Hopefully, this will help someone else since this is not documented at all. :)