Skip to content

Instantly share code, notes, and snippets.

@saltukalakus
Last active May 12, 2020 13:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saltukalakus/91a51860479b19e7fab6b7cddf867d3b to your computer and use it in GitHub Desktop.
Save saltukalakus/91a51860479b19e7fab6b7cddf867d3b to your computer and use it in GitHub Desktop.
Signing in from the enterprise and social connections with metadata available starting from the first login (a.k.a signup)

Requirements:

  • We have an app that the users can log in only with a few selected social and the enterprise connections. These users should have a specific app_metadata and user_metadata starting from the first login.

Setup:

1-) Creaate a regular Auth0 DB connection with signup option turned off. Let's assume it's named as Auth0DBInitialMetadata.

2-) Set a rule which prevents login in with the Auth0 DB connection above. So that this connection isn't used for login directly unintentionally.

E.g.:

function whitelistForSpecificConnection(user, context, callback) {

  if(context.connection === 'Auth0DBInitialMetadata') {
    return callback(new UnauthorizedError('Access denied.'));
  }

  callback(null, user, context);
}

3-) Use the following account linking rule which merges the user's metadata and allows using the merged metadata on the next rules.

4-) No need to enable the Auth0DBInitialMetadata connection for the application. Only enabling the required social and enterprise connection is sufficient.

5-) (optional) Add a rule which inserts the user's app metadata and user metadata into the id_token as custom claims.

E.g.:

function(user, context, callback) {
  const namespace = 'https://myapp.example.com/';
  context.idToken[namespace + 'company'] = global.updateAppMetadata && global.updateAppMetadata.company || user.app_metadata && user.app_metadata.company || "unemployed";
  context.idToken[namespace + 'middle_name'] = global.updateUserMetadata && global.updateUserMetadata.middle_name || user.user_metadata && user.user_metadata.middle_name || "no middle name";
  callback(null, user, context);
}

6-) (optional) Add a rule to prevent login if certain metadata is missing so that the user can't use the social or the enterprise connection until they are manually created on the Auth0 regular DB.

E.g:

function(user, context, callback) {
  if (global.updateAppMetadata && global.updateAppMetadata.company || user.app_metadata && user.app_metadata.company) {
    return callback(null, user, context);
  }
 callback(new UnauthorizedError('Access denied.')); 
}

Flow:

1-) Use the management API to create the user.

E.g.

POST  /api/v2/users
{
  "email": "saltukalakus@gmail.com",
  "password": GENERATE_A_RANDOM_STRONG_PASSWORD,
  "connection": "Auth0DBInitialMetadata",
  "app_metadata": { "company": "auth0"}, //attributes users should not be able change themselves.
  "user_metadata": { "middle_name": "rasim" }, // attributes users may change themselves.
}

2-) Wait for the user to login with their social or enterprise account. As the account linking rule is in place, the user will have the required metadata starting from the first login.

Requirements:

  • We have an app that the users can log in only with a few selected social and the enterprise connections. These users should have a specific app_metadata and user_metadata starting from the first login.
  • We want to share a link to the user which should log them in for the first time. During this login we want to notify user to login with their Enterprise or Social account using the same email address and then redirect to login page.

Setup:

1-) Enable the passwordless email connection from the Auth0 dashboard. Disable sing-up support for this connection from the same screen.

2-) Use the following account linking rule which merges the user's metadata and allows using the merged metadata on the next rules.

3-a-) Enable the passwordless connection as well as the required social and enterprise connection on the application.

3-b-) Set the app type Regular and "Token Endpoint Authentication Endpoint" to post.

3-c-) Make sure the passwordless grand is enabled for the app.

4-) (optional) Add a rule which inserts the user's app metadata and user metadata into the id_token as custom claims.

E.g:

function(user, context, callback) {
  const namespace = 'https://myapp.example.com/';
  context.idToken[namespace + 'company'] = global.updateAppMetadata && global.updateAppMetadata.company || user.app_metadata && user.app_metadata.company || "unemployed";
  context.idToken[namespace + 'middle_name'] = global.updateUserMetadata && global.updateUserMetadata.middle_name || user.user_metadata && user.user_metadata.middle_name || "no middle name";
  callback(null, user, context);
}

5-) (optional) Add a rule to prevent login if certain metadata is missing so that the user can't use the social or the enterprise connection until the invitation link is clicked.

E.g:

function(user, context, callback) {
  if (global.updateAppMetadata && global.updateAppMetadata.company || user.app_metadata && user.app_metadata.company) {
    return callback(null, user, context);
  }
 callback(new UnauthorizedError('Access denied.')); 
}

Flow:

1-) Use the management API to create the passwordless user while setting the email_verified to true for the user.

E.g.:

POST  https://saltuk-invite.auth0.com/api/v2/users
{
  "email": "saltukalakus@gmail.com",
  "connection": "email",
  "app_metadata": { "company": "auth0"}, //attributes users should not be able change themselves.
  "user_metadata": { "middle_name": "rasim" }, // //attributes users may change themselves.
  "email_verified": true
}

2-) Send the magic link to the user with a backend flow. I'm assuming the app is running on https://jwt.io

E.g.:

POST https://saltuk-invite.auth0.com/passwordless/start
Content-Type: application/json
{
  "client_id": "iDm1qr5LkD2GCc7beq4Oqw5OgzvU4aOO",
  "client_secret": "pYGLW..redacted",
  "connection": "email",
  "email": "saltukalakus@gmail.com", //set for connection=email
  "send": "link", //if left null defaults to link
  "authParams": { // any authentication parameters that you would like to add
    "scope": "openid profile",
    "redirect_uri": "https://jwt.io",
    "response_type": "id_token", // if it is regular web app set to code
    "nonce": "abc"
  }
}

3-) Application redirects the user to the login page with prompt=login parameter. Before the redirection, we may ask the user to use the same email address.

E.g.:

https://saltuk-invite.auth0.com/authorize?scope=openid+profile&response_type=id_token&state=abc&nonce=xys&client_id=iDm1qr5LkD2GCc7beq4Oqw5OgzvU4aOO&redirect_uri=https%3A%2F%2Fjwt.io&prompt=login

Limitations:

Currently new universal login doesn't support passwordless. So Clasic Universal Login has to be used.

Requirements:

  • We have an app that the users can log in only with a few selected social and the enterprise connections. These users should have a specific app_metadata and user_metadata starting from the first login.

Setup:

1-) Set a rule which retirieves the user's metadata from an external server

E.g.:

function getFullContactProfile(user, context, callback) {
  const API_KEY = configuration.API_KEY;
  const request = require('request');

  // skip if company metadata is already there
  if (user.app_metadata && user.app_metadata.company) return callback(null, user, context);

  request.get('https://api.userprofile.com/person.json', {
    qs: {
      email:  user.email,
      apiKey: API_KEY
    },
    json: true
  }, (error, response, body) => {
    if (error || (response && response.statusCode !== 200)) {
      return callback(error);
    }

    // if we reach here, it means api returned info and we'll add it to the metadata
    user.user_metadata = {} || body.user_metadata;
    user.app_metadata = {} || body.app_metadata;
    
    auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
      .then(auth0.users.updateUserMetadata(user.user_id, user.user_metadata))
      .then(function(){
        callback(null, user, context);
      })
      .catch(function(err){
        callback(err);
      });
    });
}

2-) (optional) Add a rule which inserts the user's app metadata and user metadata into the id_token as custom claims.

E.g.:

function(user, context, callback) {
  const namespace = 'https://myapp.example.com/';
  context.idToken[namespace + 'company'] = user.app_metadata && user.app_metadata.company || "unemployed";
  context.idToken[namespace + 'middle_name'] = user.user_metadata && user.user_metadata.middle_name || "no middle name";
  callback(null, user, context);
}

3-) (optional) Add a rule to prevent login if certain metadata is missing so that the user can't use the social or the enterprise connection until they are manually created on the Auth0 regular DB.

E.g:

function(user, context, callback) {
  if (user.app_metadata && user.app_metadata.company) {
    return callback(null, user, context);
  }
 callback(new UnauthorizedError('Access denied.')); 
}

Flow:

1-) Wait for the user to login with their social or enterprise account. As the metadata fect rule is in place, the user will have the required metadata starting from the first login.

@saltukalakus
Copy link
Author

Passwordless flow isn't supported with the new universal login as of April 2020

@saltukalakus
Copy link
Author

Rules order is important:

Screen Shot 2020-04-25 at 23 41 42

@saltukalakus
Copy link
Author

Disable signups for both of the flows.

Passwordless:
Screen Shot 2020-04-25 at 23 56 14

Regular DB:
Screen Shot 2020-04-25 at 23 58 51

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment