0

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!

Dumber_Texan2
  • 840
  • 2
  • 12
  • 34
  • Doesn't the auth flow come back into `ExternalLoginCallback`? At this point you can add all the custom claims you want – Brendan Green Feb 28 '19 at 01:59
  • The ExternalLoginCallback works with an MVC Web App and I actually have this working with a different Twitter and Facebook app. Now I'm trying to get another Twitter and Facebook app to work with my Web API, so I've configured the Startup.Auth to use their credentials. Everything works! I can see the AccessToken and AccessTokenSecret being created. I then add them as additional claims using context.Identity.AddClaim. When I log back on using the Bearer Token, the claims are not part of the Identity. – Dumber_Texan2 Feb 28 '19 at 15:20

2 Answers2

0

You can't change tokens made by a third party.
They are signed with a private key and validated during login.

You could put host IdentityServer in between the Twiiter identity prodiver and your applications. IdentityServer is open source OpenId and OAuth 2.0 server and allows you to dictate how tokens and claims should be handled. But it is not a simple thing to start with. All depends on how far you want to go for this solution.

https://identityserver.io/

I can certainly recommend it!

Schwarzie2478
  • 2,186
  • 26
  • 30
  • Thanks! Any idea how Owin adds the Twitter ScreenName to the Bearer Token Identity? I can get that using identity.FindFirstValue(ClaimTypes.Name). Perhaps this method could be overwritten or somehow added to so something like this identity.FindFirstValue("access_token") would work. – Dumber_Texan2 Feb 28 '19 at 17:49
  • I'm not familiar with the Twitter as such, but they are OAuth tokens, which I understand at least a bit. Windows Authentication maps the name claim to the User Identity that you can call in your methods, You can always check the contents of a token in jwt.io to see which claims you get... – Schwarzie2478 Feb 28 '19 at 18:00
0

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. :)

Dumber_Texan2
  • 840
  • 2
  • 12
  • 34