Suggested workflow for authentication via Twitter

Hello,
I have just tried Google and Twitter authentications, they both work but the Twitter one return to my app an user with email verified=false while for Google it is set to true.

I know Twitter has a different workflow since you have to set up an additional rule to get the email address. Anyway my question is, should I force the verified field to true in my application when I detect authentication came via Twitter or another more elegant solution exists?

Also, I guess Google dev keys are preset by Auth0. I just enabled the Google switch and got it working in 30 seconds. Too good to be true. Should I change them urgently with new keys specific for my account? What is the risk?

Many thanks

Should I change them urgently with new keys specific for my account?

The devs keys are only meant to be used during development purposes. Before you launch your site and let any production users go onto it, you should definitely already have proper keys of your own in place.

Especially since your users probably don’t want to read a weird application name “Auth0 example” on the consent screen :slight_smile:

But also: you don’t have any control of the twitter app that’s registered by Auth0 and got the dev keys. And you probably want to be in full control of that twitter app.


Regarding the email address: why do you need that email address of the user? The only reason would be that you want to send the users emails, otherwise there’s not really a need, right? So, let’s assume that’s the case (that you want to send out emails).

Twitter provides the option to signup with phone number or email address. If signing up with an email address, or adding an email address afterwards, Twitter always requires a verification code (otherwise it doesn’t let you signup or change the email address in the profile settings). So, you can be sure that the email on Twitter side is verified (if you get one back from Twitter, since an email address afaik isn’t required). Therefore I’d say an additional verification on your end isn’t necessary. You can set it to email_verified=true within a Rule then, calling the Management API.

2 Likes

Hi Mathias,
thanks again.

The dev key explanation totally makes sense. For some coincidences I had Twitter carefully configured with my own dev keys, app permission, logo, etc. and Google was left with default Auth0 dev keys. I didn’t notice in google login that it points to Auth0 app and privacy policy, thanks for the head up, I will fix it soon right after the 2nd issue about email verification that I’m going to describe below.

First some preamble: I agree with you that if Twitter provides me an email address that implies it is verified, I stated that in my 1st post. The problem is that I need a clean and unified management of “User” object in my code, both on client and backend API (via token).

When I login via Google or email+pwd, Auth0 returns me a User object including the email_verified field properly set. When I use twitter login this field is simply missing. That is not consistent. I tried to add in the rule the missing field, see the last 3 lines of the rule code (customized from Auth0 “Get email address from Twitter” template)

user.email = body.email;
user.email_verified = true;
return callback(err, user, context);

I saved the modified rule multiple times but the email_verified field is not present in my client code nor in the token. I could workaround this in my code by checking if userid cotains the word “twitter” , create the missing field and set it to true, but honestly it’s an horrible solution from clean code perspective. I should also replicate the dirty workaround in the backend API since the verified field would miss also from the token.

Auth0 should return already from default “Get email address from Twitter” rule the email_verified field to true, to be consistend to what it does with authentication via Google.

Now if you can help me to find a way to set the field from the rule that would be great, otherwise I will have use the “dirty” workaround.

When I login via Google or email+pwd, Auth0 returns me a User object including the email_verified field properly set. When I use twitter login this field is simply missing. That is not consistent.

It really depends what claims each IdP supports. From Google it’s obvious from their OIDC discovery doc that email_verified is a supported claim.

https://accounts.google.com/.well-known/openid-configuration

Twitter, since they just support OAuth2 but not OIDC afaik, it’s not obvious, and I couldn’t find it in the Twitter docs, but I think it’s simply not provided. And Auth0 can only rely on what the IdP provides, it wouldn’t make up the value for email_verified just on its own. (I haven’t tested Twitter in detail and what claims exactly come back).

email_verified field is not present in my client code nor in the token

You cannot update the email_verified field directly in a Rule like this. It’s a protected field. I’ll provide you with a different code snippet in a bit how do change it (making a call to the Management API), especially since you want to persist that info anyway, and just using user.email_verified = true in the rule doesn’t persist it in the user store, it’s just a temporary change.


Update: @AntonioM

Here’s two approach, depending whether you want/need to persist the email_verified flag in the user store or of it’s sufficient to put it in the ID token (and afterwards forget about it).

With persistence (Rule code):

function (user, context, callback) {

  if (context.connectionStrategy === "twitter")  {

    var ManagementClient = require('auth0@2.6.0').ManagementClient;
    var management = new ManagementClient({
        token: auth0.accessToken,
        domain: auth0.domain
    });
        
     // persist in user store
    management.updateUser({id: user.user_id}, {email_verified:true})
    .then(function(u){    
      context.idToken.email_verified = u.email_verified;
      context.idToken.email = u.email;    
      callback(null, u, context);
    })
    .catch(function(err){
      callback(err);
    });     
  } else { // if not twitter
    callback(null, user, context);
  }

}

Without persistence (Rule code):

function (user, context, callback) {

  if (context.connectionStrategy === "twitter")  {
    context.idToken.email_verified = user.email_verified;
    context.idToken.email = user.email;
  }

  callback(null, user, context);
}

Hi Mathias,
thanks again for your help.

I have adopted your solution #1 because it persists the info both in the user object and the token. But there was a little problem I fixed on my own where I’d like to have your approval.

Copying your code in the rule worked for the email_verified=true field but the email field disappeared from both the user and the token (!) That is of course the most important field, so I tried to hack the code myself to restore that field. In the end I came up with this solution, tested and working:

I replaced this line of yours
context.idToken.email = u.email;
with this one
context.idToken.email = user.email;
and it works. It seems the u.email does not contain a valid value so I took it from the external user object.

To make everything clear for you to examine I attach the complete code for my rule (I just replaced sensitive secrets with placeholders)

function (user, context, callback) {

  // additional request below is specific to Twitter

  if (context.connectionStrategy !== 'twitter') {

    return callback(null, user, context);

  }

  const oauth = require('oauth-sign');

  const uuid = require('uuid');

  const url = 'https://api.twitter.com/1.1/account/verify_credentials.json';

  const consumerKey = 'MY_CONSUMER_KEY';

  const consumerSecretKey = 'MY_CONSUMER_SECRET';

  const twitterIdentity = _.find(user.identities, { connection: 'twitter' });

  const oauthToken = twitterIdentity.access_token;

  const oauthTokenSecret = twitterIdentity.access_token_secret;

  const timestamp = Date.now() / 1000;

  const nonce = uuid.v4().replace(/-/g, '');

  const params = {

    include_email: true,

    oauth_consumer_key: consumerKey,

    oauth_nonce: nonce,

    oauth_signature_method: 'HMAC-SHA1',

    oauth_timestamp: timestamp,

    oauth_token: oauthToken,

    oauth_version: '1.0'

  };

  params.oauth_signature = oauth.hmacsign('GET', url, params, consumerSecretKey, oauthTokenSecret);

  const auth = Object.keys(params).sort().map(function (k) {

    return k + '="' + oauth.rfc3986(params[k]) + '"';

  }).join(', ');

  request.get(url + '?include_email=true', {

    headers: {

      'Authorization': 'OAuth ' + auth

    },

    json: true

  }, (err, resp, body) => {

    if (resp.statusCode !== 200) {

      return callback(new Error('Error retrieving email from twitter: ' + body || err));

    }

    user.email = body.email;

    var ManagementClient = require('auth0@2.6.0').ManagementClient;

    var management = new ManagementClient({

        token: auth0.accessToken,

        domain: auth0.domain

    });    

    management.updateUser({id: user.user_id}, {email_verified:true})

    .then(function(u){    

      context.idToken.email_verified = u.email_verified;

      context.idToken.email = user.email;    

      return callback(null, u, context);

    })

    .catch(function(err){

      return callback(err);

    });    

  });

}
2 Likes

In my test I didn’t get an email back from Twitter but also didn’t test much further how to retrieve it. So that one code line from me was just “theoretical”. Your approach looks good by calling Twitter API, I believe it’s the only way to get it (haven’t checked the Twitter docs in detail).

Note that for this part,

  const consumerKey = 'MY_CONSUMER_KEY';
  const consumerSecretKey = 'MY_CONSUMER_SECRET';

… you shouldn’t hard code the values in the rule code but use the key/value mechanism Auth0 provides for Rules (you find them at the bottom of the Rules list in the Dasboard).

you shouldn’t hard code the values in the rule code but use the key/value mechanism Auth0 provides for Rules (you find them at the bottom of the Rules list in the Dasboard)

Oh Thank you! I fixed that and it works. I hate hardcoding secrets in the code but I didn’t see the heads up in the bottom. I was too focused on having email+verified fields working.

Now everything is ok both in Twitter auth and email+pwd auth.

I will just add the google dev keys and then I can finally focus on my app logic and UI

1 Like

Happy to hear :slight_smile:

Happy to hear :slight_smile:

Wait…I still need your help :disappointed_relieved:

I tried to add the Google dev keys… what a painful series of forms and security checks, anyway I passed them all and I finally got key & secret

I add the above keys to the Auth0 dashboard and it works, but… the google login still talks about giving permission to “Auth0” app… not to my app. I gave Google precise app name and logo and nothing appears on the login. The only correct info are the terms& conditions and privacy links which points to the one I provided.

Do you have any idea why Google keep mentioning Auth0 app instead of my app?

I tried to alter my keys in the dashboard with wrong data and I get an error so my keys are good. Is it maybe that Google need to verify my app before showing data about it? (they say the logo needs their verification, they maybe fear profanity images) They keys are freshly created (10 minutes ago)

Here is the login screen capture

Maybe they infer the app name from the Authorized domains?

I will wait until tomorrow and report if there is any change, maybe when verification completes.

I think that’s not the app name but the domain it’s coming from. Since it’s federated from your Auth0 tenant / domain, it’s auth0.com.
You can avoid it by using a custom domain though.

The Google consent screen you should see afterwards should show your app name, not?

(Haven’t checked myself, on mobile right now.)

I can confirm, it’s the domain name you’re using at Auth0. When you use a custom domain (requires Enterprise subscription), Google will also show that domain.

Thanks again Mathias,
I already sensed the reason yesterday

Maybe they infer the app name from the Authorized domains?

and you confirmed my guess. Ok I’ll live with it. At the moment I’ll stay with the free plan but I’ll keep it in mind for future needs and to explain it to customers when they want new Apps with google social authentication (with FB, the most common and important I guess).

Personal consideration: how strange this Authentication world (new to me) is…

If you use Twitter you have to code workarounds to get email and verification but they give you for free a nice big banner with your name and logo.

If you use Google you get an impeccable code workflow but you need to pay :moneybag::moneybag::moneybag: if you want your name/logo to appear on their popup :grin: although I have to say that if you click on the Auth0 app link in google popup it will show another popup with the developer info (name, email, redirect url). Same for terms and conditions. Overall the user has a mean to be reassured he is dealing with the same app and not redirected to some evil website. It’s just that is not that immediate.

My logo approval is still pending, if when approved there will be a significant improvement in the google consent popup, I will update this thread just to provide general information to the public

1 Like

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.