Ocial Logins Not Adding Custom Claims to Access Token via Action Script

Hello Auth0 Support,

I am facing an issue where custom claims are not being added to the access token during social login (Google) using an Auth0 Post Login Action script. This same setup works perfectly for Universal Login (email & password).


Setup

  • Auth0 Action: Post Login
  • External Database: Used for both login & signup
  • Social Login: Google (via Auth0 connection)
  • Universal Login: Username/password (working fine)
  • Access Token: Expected to have custom claims (e.g., role, user_id, etc.)

What Works (Universal Login Flow)

When users log in via Universal Login (email/password):

  • The Action script successfully queries our external DB.
  • The DB returns user-specific data.
  • Claims are set via event.accessToken.setCustomClaim().
  • These claims appear in the access token as expected.

What Fails (Google Social Login Flow)

When users log in via Google Social Login:

  • The same Action script runs.
  • The external DB is queried using event.user.email.
  • No custom claims appear in the access token.
  • It seems event.user may be incomplete or uninitialized at the time of execution.

Reproduction Steps

  1. Configure PostLogin Action to fetch user data from external DB and set custom claims.
  2. Trigger login via:
  • Universal Login → custom claims present in access token.
  • Google Social Login → custom claims missing from access token.

Action Script (Example Snippet)

jsCopyEditexports.onExecutePostLogin = async (event, api) => {  try {    const email = event.user.email;    // Call our external DBconst userData = await getUserFromExternalDB(email);    if (userData) {      api.accessToken.setCustomClaim("app_metadata.role", userData.role);      api.accessToken.setCustomClaim("app_metadata.user_id", userData.userId);    }  } catch (err) {    console.log("Error fetching user data:", err);  }};

Screenshots


Questions

  • Why are custom claims missing in the access token during social login?
  • Is there a timing/initialization difference between Universal and Social login for the Action script?
  • Is there a recommended workaround to ensure external user data is fetched and custom claims are added for social logins?

Please let me know if any logs, full code, or additional debugging information is needed. I appreciate your help in resolving this.

Thanks,

Hi @exchanga

Thank you for reaching out to us!

Please allow me some time to look into the issue for you and I will be back with more information as soon as possible.

Have a good one,
Gerald

Hi @exchanga

Thank you for providing all the details!

Generally I would say that you’re doing a great job with the Action script and there’s most likely a small issue with it that is causing issues with the Google Social Connector, while working fine with your Username/Password DB. Would you be able to share the entire Action script, or at least the full part that has to do with setting custom claims so we can take a closer look at why this behaviour might happen?

Please remove any sensitive data from the Action just to keep everything confidential, such as tenant names, client_ids etc.

Looking forward to more information!
Gerald

I am sharing here my action code file can you reffer this and give me solution as quick as possible.

const sqlserver = require(‘tedious’);
exports.onExecutePostLogin = async (event, api) => {

const { user } = event;

api.accessToken.setCustomClaim('ecode', user.ecode);
api.accessToken.setCustomClaim('idc', user.idc);
api.accessToken.setCustomClaim('idr', user.idr);

let dbServer = event.secrets.db_server;
let dbName = event.secrets.db_database;
let dbUser = event.secrets.db_username;
let dbPass = event.secrets.db_password;

const Connection = sqlserver.Connection;
const Request = sqlserver.Request;
const TYPES = sqlserver.TYPES;
const connection = new Connection({
    userName: dbUser,
    password: dbPass,
    server: dbServer,
    options: {
        database: dbName
    }
});
const query = 'SELECT Id, UserName, Email FROM dbo.Users WHERE Email = @Email';
const query1 = 'SELECT ClaimType, ClaimValue FROM dbo.UserClaims WHERE UserId = @UserId and (ClaimType=@Idr or ClaimType=@Idc or ClaimType=@Ecode)';

connection.on('debug', function (text) {
    //console.log(text);
}).on('errorMessage', function (text) {
    //  console.log(JSON.stringify(text, null, 2));
}).on('infoMessage', function (text) {
    // console.log(JSON.stringify(text, null, 2));
});
connection.on('connect', function (err) {
    if (err) { console.log('token2'); throw new Error(err); }
    let userInfo = null;
    const request = new Request(query, function (err, rowCount, rows) {
        if (err) throw new Error(err);
        if (rowCount <= 0) {
            console.log('triger singup');
            singup();
        }
        console.log(rowCount, userInfo);
        if (rowCount > 0) {
            let idr = null;
            let idc = null;
            let ecode = null;
            const request1 = new Request(query1, function (err1, rowCount1, rows1) {
                if (err1 || rowCount1 < 1) throw new Error(err1);
                api.accessToken.setCustomClaim(`idr`,idr);
                api.accessToken['idc']=idc
                api.accessToken.setCustomClaim('idc',idc);
                api.accessToken.setCustomClaim('ecode',ecode);
                api.accessToken.setCustomClaim('sub',userInfo.user_id);
                api.idToken.sub = userInfo.user_id;
                throw new Error(null, user, api);
            });

            request1.on('row', function (columns1) {
                if (columns1) {
                    if (columns1[0].value === 'idr') idr = columns1[1].value;
                    if (columns1[0].value === 'idc') idc = columns1[1].value;
                    if (columns1[0].value === 'ecode') ecode = columns1[1].value;
                }
            });

            request1.addParameter('UserId', TYPES.VarChar, userInfo.user_id);
            request1.addParameter('Idr', TYPES.VarChar, 'idr');
            request1.addParameter('Idc', TYPES.VarChar, 'idc');
            request1.addParameter('Ecode', TYPES.VarChar, 'ecode');
            connection.execSql(request1);
        }

    });
    request.on('row', function (columns) {
        console.log('L6', columns, 'asd');
        if (columns.length > 0) {
            userInfo = {
                user_id: columns[0].value,
                nickname: columns[1].value,
                email: columns[2].value
            };
        }
    });

    request.addParameter('Email', TYPES.VarChar, user.email);
    console.log(request);
    connection.execSql(request);
});

function singup() {
    
}

};

Hi there!

I believe that implementing and testing a simple code that adds custom claims should be the first step, ensure that it works on it’s own and build it up from there in order to be included within your full Action code. Something that should work :

exports.onExecutePostLogin = async (event, api) => { 
const namespace = 'https://my-app.example.com';
api.accessToken.setCustomClaim(namespace, 'claim on a google user's access token');
};

The https://www.jwt.io/ tool can be used to decode the Access token and make sure that the custom claims are added. After this, the code can slowly be built in the full Action and confirm where is the issue that prevents the claims from being added with the Social Provider.

I recommend checking out the following documentation for Sample codes for Scopes and Claims as it can provide useful additional information.

Hope this helped!
Gerald