Rule: Link accounts -> Missing authentication

I’ve added this new Rule that shall ‘link-accounts-with-same-ssn(it’s based upon Link-Users-With-Same-Email). The problem is that I’m getting:
statusCode: 401,
error: ‘Unauthorized’,
message: ‘Missing authentication’

The first request is working fine with the same bearer. What could I’ve missed? :pensive:

The Rule:

function (user, context, callback) {
  const request = require('request');
  const swedishBankIdAnotherDevice_connectionId = 'con_myswedishbankid_connectionid';
  const isSwedishBankIdConnection = context.connectionID === swedishBankIdAnotherDevice_connectionId;

  // Check if connection is SwedishBankId,
  // we shouldn't automatically merge accounts if this is not the case.
  if (!isSwedishBankIdConnection) {
    return callback(null, user, context);
  }

  if (!user.app_metadata || !user.app_metadata.userSocialSecurityNumber || user.app_metadata.userSocialSecurityNumber.length !== 12) {
    return callback(new Error('[!] Rule: BankIdUser is missing the required property app_metadata.userSocialSecurityNumber'));
  }

  const userApiUrl = auth0.baseUrl + '/users';
  const userSearchQueryUrl = userApiUrl + '?q=app_metadata.userSocialSecurityNumber%3A%22' + user.app_metadata.userSocialSecurityNumber + '%22&search_engine=v3';
  request({ **// <<< works fine**
    url: userSearchQueryUrl,
    headers: {
      Authorization: 'Bearer ' + auth0.accessToken
    }
  },
  function(err, response, body) {
    if (err) return callback(err);
    if (response.statusCode !== 200) return callback(new Error(body));

    var data = JSON.parse(body);
    data = data.filter(function(u) {
      return u.user_id !== user.user_id;
    });
    if (data.length > 1) {
      return callback(new Error('[!] Rule: Multiple user profiles already exist - cannot select base profile to link with'));
    }
    if (data.length === 0) {
      console.log('[-] Skipping link rule');
      return callback(null, user, context);
    }

    const originalUser = data[0];
    const provider = user.identities[0].provider;
    const providerUserId = user.identities[0].user_id;
    request.post({
      url: userApiUrl + '/' + originalUser.user_id + '/identities',
      headers: {
        Authorization: 'Bearer ' + auth0.accessToken
      },
      json: {
        provider: provider,
        user_id: String(providerUserId)
      }
    }, function(err, response, body) {
      if (response.statusCode >= 400) {
        **return callback(new Error('Error linking account: ' + response.statusMessage));** **// <<<< this is where I'm getting statusCode: 401, error: 'Unauthorized', message: 'Missing authentication'**
      }

      context.primaryUser = originalUser.user_id;
      callback(null, user, context);
    });
  });
}

@joakim.neatsoft,

Take a look at your access token and see what scopes you are granted see if you have the appropriate ones.

Also, check your auth0 logs and see if there is a more detailed message.

Everything looks okay with the rule at first glance.

Let me know.

Thanks,
Dan

I’ve the following scopes in the token:

"scopes": {
    "users": {
      "actions": [
        "read",
        "update"
      ]
    }
}

But it also says “Invalid Signature” when I’m using jwt.io to decode it, not sure what thats about.

I also have this from Auth0-logs:

image

{
      "date": "2019-12-04T06:25:19.399Z",
      "type": "f",
      "description": "Error linking account: Unauthorized",
      "connection": "criipto-verify-SE-BankID-Another-De",
      "connection_id": "con_<my connection>",
      "client_id": "ePbm……….u8DPaJ",
      "client_name": "Frontend test",
      "ip": "<my ip>",
      "user_agent": "Chrome 78.0.3904 / Windows 10.0.0",
      "details": {
        "body": {
          "wa": "wsignin1.0",
          "wresult": "<trust:RequestSecurityTokenResponseCollection xmlns:trust=…… <alot of stuff here>…….</trust:RequestSecurityTokenResponseCollection>",
          "wctx": "xeGcDj-5p……….TIIwweOBLo"
        },
        "qs": {},
        "connection": "criipto-verify-SE-BankID-Another-De",
        "error": {
          "message": "Error linking account: Unauthorized",
          "oauthError": "access_denied",
          "type": "oauth-authorization"
        },
        "session_id": "1yz_Egg0………yLgSw4vtL"
      },
      "hostname": "<mydomain>.eu.auth0.com",
      "user_id": "adfs|criipto-verify-SE-BankID-Another-De|41a55………b3368e2",
      "user_name": "JoakimBTest BTest",
      "strategy": "adfs",
      "strategy_type": "enterprise",
      "audience": "https://<tenant>.eu.auth0.com/userinfo",
      "scope": [
        "openid",
        "profile",
        "email"
      ],
      "log_id": "900201912040……….360811622418",
      "_id": "900201912040…….11622418",
      "isMobile": false
    }

It could be worth mentioning that when I loggin the application starts looping the login with the message from the Rule above (this might be standard behavior when the rule fails in this way?). It generates this:

The scenario for my application is this:
We’ve added Criiptos solution to Auth0 to have some users be able to login with Swedish BankId.

  • Create user Username/Password (add social security number as metadata, will be used to link accounts)
  • User signs in with BankId, the user gets created in Auth0. The rule described above kicks in and link the new BankId-user with the manually created user (username/password)

Application structure:

  • Client (React using @auth0/auth0-spa-js)
  • API (.Net Core)

@joakim.neatsoft,

Can you try requesting a token for the management api with the update:current_user_identities scope and see if you still get the error?

Here is the relevant node library:

Ok I did try to run it directly in the rule with no success “ManagementClient is not a constructor”.
After that I ran it from my machine using Node with success:

EDIT:
I can’t find the scope “update:current_user_identities” under my “Auth0 Management API \ Permission-tab”.

Ok so I’ve a working solution now. I just added this code after const providerUserId = user.identities[0].user_id

// Start linking accounts
      var ManagementClient = require('auth0@2.9.1').ManagementClient;
      var management = new ManagementClient({
        token: auth0.accessToken,
        domain: auth0.domain
      });
      var params = {
        provider: provider,
        user_id: providerUserId,
        connection_id: context.connectionID
      };

      management.linkUsers(originalUser.user_id, params, function (err, user) {
        if (err) {
          return callback(new Error('Error linking account: ' + err));
        }
        console.log('PrimaryUser: ' + originalUser.user_id + ' got linked with user: ' + providerUserId);
      });

thank you for all the help!

Glad you got it working!

You may want to update to the current auth0 package version available in rules, 2.19.0.