Account linking through Actions

I am adding another example. This one uses the recently announced api.authentication.setPrimaryUser() method if merging results in a change of sub.

exports.onExecutePostLogin = async (event, api) => {

    console.log('account-link action user: ', event?.user?.email);

    const {ManagementClient, AuthenticationClient} = require('auth0');

    if (event?.user?.email_verified !== true) { // no linking if email is not verified
        return;
    }

    /*
      if (event.user.identities.length > 1) { // no linking if user is already linked
          return;
      }
      */

    const {domain} = event.secrets || {};

    let {value: token} = api.cache.get('management-token') || {};

    if (!token) {
        const {clientId, clientSecret} = event.secrets || {};

        const cc = new AuthenticationClient({domain, clientId, clientSecret});

        try {
            const {data} = await cc.oauth.clientCredentialsGrant({audience: `https://${domain}/api/v2/`});

            token = data?.access_token;

            if (!token) {
                console.log('failed get api v2 cc token');
                return;
            }
            console.log('cache MIS!');

            const result = api.cache.set('management-token', token, {ttl: data.expires_in * 1000});

            if (result?.type === 'error') {
                console.log('failed to set the token in the cache with error code', result.code);
            }
        } catch (err) {
            console.log('failed calling cc grant', err);
            return;
        }
    }

    // This will make an Authentication API call
    const client = new ManagementClient({domain, token});

    // Search for other candidate users
    const {data: candidateUsers} = await client.usersByEmail.getByEmail({email: event?.user?.email});

    if (!Array.isArray(candidateUsers) || !candidateUsers.length) { // didn't find anything
        return;
    }

    const firstCandidate = candidateUsers.find((c) =>
        c.user_id !== event.user.user_id &&         // not the current user
        //c.identities[0].provider === "auth0" &&   // DB user
        c.email_verified                            // make sure email is verified
    );

    if (!firstCandidate) { // didn't find any other user with the same email other than ourselves
        return;
    }

    const primaryChanged = firstCandidate.provider === 'auth0';

    let primaryUserId, secondaryProvider, secondaryUserId, primaryCustomerId, secondaryCustomerId;

    if (primaryChanged) {
        primaryUserId = firstCandidate.user_id;
        secondaryProvider = event.user.identities[0].provider;
        secondaryUserId = event.user.identities[0].user_id;

        primaryCustomerId = firstCandidate.app_metadata.customer_id;
        secondaryCustomerId = event.user.app_metadata.customer_id;
    } else {
        primaryUserId = event.user.user_id;
        secondaryProvider = firstCandidate.identities[0].provider;
        secondaryUserId = firstCandidate.user_id;

        primaryCustomerId = event.user.app_metadata?.customer_id;
        secondaryCustomerId = firstCandidate.app_metadata?.customer_id;
    }

    try {
        await client.users.link({id: primaryUserId}, {provider: secondaryProvider, user_id: secondaryUserId});
    } catch (err) {
        console.log('unable to link, no changes');
        return;
    }

    // -- customer_id(s) logic --
    if (secondaryCustomerId) {   // we have a secondary customer id, time to do some merge
        if (primaryCustomerId) { // both customer_ids remain and merge
            api.user.setAppMetadata('customer_id', [primaryCustomerId, secondaryCustomerId]);
        } else {
            api.user.setAppMetadata('customer_id', secondaryCustomerId);
        }
    }

    if (primaryChanged) api.authentication.setPrimaryUser(primaryUserId);

};

4 Likes