Management API return 401 Unauthorized

Hi,

I use auth0.js in angular application.
The new auth0.WebAuth method work well but when I want use new auth0.Management method I receive an 401 Unauthorized response.
In detail, after create new WebAuth, I use Parse method of WebAuth to get access Token and Id token.
When I use my function management I have an error in dev console :
PATCH https://xxxx.eu.auth0.com/api/v2/users/auth0|5ad8b180b09f330xxxxxx

statusCode 401
error Unauthorized
message Invalid token
attributes {…}
error Invalid token

Code :

this.webAuth.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
window.location.hash = ‘’;
this.setSession(authResult);
resolve(true);
} else if (err) {
reject(err);
} else {
resolve(false);
}
});

private setSession(authResult): void {
// Set the time that the access token will expire at
const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime());
localStorage.setItem(‘access_token’, authResult.accessToken);
localStorage.setItem(‘id_token’, authResult.idToken);
localStorage.setItem(‘expires_at’, expiresAt);
}

private get management() {
return new auth0.Management({
domain: this.auth0Config.domain,
token: this.getIdToken()
});
}

@mike31 when you are calling the management API you need to use the access_token. It seems from this code you are using the id_token. Also, to get an access_token from Auth0 that is capable of using with the Management API you need to trigger authentication with the audience for the Management API.

Hi sgmeyer,

Thank you , after check the documentation, you’re right :slight_smile:
But I have still an error with access token :

statusCode 400
error Bad Request
message Bad HTTP authentication header format
errorCode Bearer

I see on header the Authorization seems well written
Authorization Bearer XmjZzvFSGR6GE5dLfTAkWDqixxxxxxx

@mike31, it seems you are using the 32 character opaque token for an access token. When you are authenticating users are you sending the audience https://{your-tenant}.auth0.com/api/v2/ with this? If so you should have received a JWT access_token instead. I suspect this error is failing because the access_token is not a JWT.

Can you share the code you are using to trigger authentication to fetch tokens? Particularly, I would like to see how the SDK, Lock, or whatever you are using for trigger auth is setup.

Hi,

thank you for your feedback
indeed, my access token has not correct format (xxxx.yyyyyy.wwwww)

I’m not sure for the audience, where I can check it ?

The code to trigger auth :
I use Auth0-js v 9.5, Angular 5 app and couchDB (nosql db).
Client ID and domain are getting from couchDB (work well)

this.auth0Config = config.data.auth0;
this.webAuth = new auth0.WebAuth({
clientID: this.auth0Config.clientID,
domain: this.auth0Config.domain,
responseType: ‘token id_token’,
redirectUri: window.location.origin,
scope: ‘openid profile roles user_id name email’,
theme: {
logo: this.auth0Config.logo,
primaryColor: this.auth0Config.primaryColor
},
languageDictionary: {
title: this.auth0Config.title,
mfaInputPlaceholder: ‘Code’,
mfaLoginTitle: ‘2-Step Vérification’,
mfaLoginInstructions: ‘Veuillez entrer le code de vérification généré par votre application mobile.’,
mfaSubmitLabel: ‘S'identifier’,
mfaCodeErrorHint: ‘Utilisez des numéros’,
error: {
login: {
‘lock.mfa_registration_required’: ‘L'authentification multifactorielle est nécessaire, mais votre appareil n'est pas inscrit.’,
‘lock.mfa_invalid_code’: ‘Mauvais code. Veuillez réessayer.’
}
}
},
language: this.auth0Config.language,
});

Hi,
I’ve just checked the audience and in APIs management interface I have in identifier (audience) : https://[mydomain].eu.auth0.com/api/v2/

Thks
Michael

Ok after tested, I added in auth0.WebAuth this line :
audience: ‘https://’ + this.auth0Config.domain + ‘/api/v2/’,

And my access token is now in jwt format :slight_smile: but I have now a 403 forbidden error when I use new auth0.Management… :frowning:
The email and paswword are good and the user is not blocked
In access token, the scope is “scope”: “openid profile email” and I have “aud”:
[
“https://[mydomain].eu.auth0.com/api/v2/”,
“https://[mydomain].eu.auth0.com/userinfo”
],

Any ideas ?

@Mike31 nice work so far. I think the last piece you need is to specify the scopes required to call the endpoint. In your example you are calling the update user endpoint. According to the API explorer docs (Auth0 Management API v2) you need to request the update:users scope. This will inform the Auth0 to specify the authorization policy for the JWT/access_token.

image

@sgmeyer Thank you :slight_smile:

I try to add these 2 scope parameters but without result.
have I misunderstood something ??

In my auth0.WebAuth method I have the line :
scope: ‘openid profile roles user_id name email update:users update:users_app_metadata’,

@mike31, I don’t think you actually need update:users_app_metadata, but I am curious… can you share the decoded body of your JWT? Also, are you still getting the same error?

Hi,
Yes I still the same error 403 Forbidden :frowning:

No problem, here is the decode of access token jwt:
{
“http://[mydomain].local:4200/app_metadata”: {
“roles”: [
“Delegated Admin - User”
],
“couchDB”: {
“host”: “xxxxxxxxxx-6aa9-4b05-b600-7f75ed8bbcdd-bluemix.cloudant.com”,
“name”: “develop_app”,
“key”: “yyyyyyyyyyyyyyyyyyyyyyy”,
“password”: “b8350508ba9557819c8ef003952xxxxxxxxxxxx”
}
},
“http://[mydomain].local:4200/user_metadata”: {
“family_name”: “XXXXX”,
“given_name”: “XXXXXX”
},
“iss”: “https://[mydomain].eu.auth0.com/”,
“sub”: “auth0|5ad8b180b09f330744ab1e7f”,
“aud”: [
“https://[mydomain].eu.auth0.com/api/v2/”,
“https://[mydomain].eu.auth0.com/userinfo”
],
“iat”: 1526676645,
“exp”: 1526683845,
“azp”: “VhguTED3M5I0iaFwYY0RqXFrcsEpl0Vu”,
“scope”: “openid profile email”
}

@mike31 the access_token is still missing the necessary scope. If you look at the scope claim you have only openid profile email. It is missing the user scopes. You mentioned you had this webauth configured as so:

scope: ‘openid profile roles user_id name email update:users update:users_app_metadata’,

Any chance you have a rule or something filtering out scopes? From what I can tell your JWT is not being issued with the necessary scopes. Since you are doing web auth can you capture the call to /authorize endpoint? This will have quite a few get params. I want to make sure those params are being sent to authorize. Then also, can you let me know if any rules might be modifying the inbound scopes?

@sgmeyer You are right , the scope is not good.
Yes I added update:users update:users_app_metadata in webauth but the access token returned contain only openid profile email (the scope standard of v9).

The /autorize (the scope seems good) :
GET https://[myDomain].eu.auth0.com/authorize?client_id=VhguTED3M5I0iaFwYY0RqXFrcsEpl0Vu&response_type=token%20id_token&redirect_uri=http%3A%2F%2F[myDomain].local%3A4200&scope=openid%20profile%20roles%20user_id%20name%20email%20update%3Ausers%20update%3Ausers_app_metadata&audience=https%3A%2F%2F[myDomain].eu.auth0.com%2Fapi%2Fv2%2F&state=OBQk_gz1oabZgCbb~bjfPUnps6IAunlE&nonce=nwmkLFU-CBA~v9I0-ELqHpBHdwCDlH5o&auth0Client=eyJuYW1lIjoiYXV0aDAuanMiLCJ2ZXJzaW9uIjoiOS41LjAifQ%3D%3D

For autorize method I just call this function :
public login() {
this.webAuth.authorize();
};
I try to add scope update:users in this function but I have still the same scope in access Token :frowning:
I use 2 rules but which have no impact on scope (I think)
The first connect and update to couchDB to add some informations ans update app_metadata to add couchDB informations.
the second is used to pass custom claims in app_metadata:
function (user, context, callback) {
const namespace = ‘http://[My local domain]:4200/’;
context.idToken = context.idToken || {};
context.idToken[namespace + ‘app_metadata’] = user.app_metadata;
context.idToken[namespace + ‘user_metadata’] = user.user_metadata;
callback(null, user, context);
}
I think I must have a bad parameters in my Tenant, I don’t know where … :frowning:

I seem to be having the exact same issue.

    const lock = new Auth0Lock(
      'xxxxxxxxxxxxxxxxxxxxxxx',
      'mytenant.eu.auth0.com', {
        auth: {
          redirectUrl:  document.location.origin + '/dashboard/',
          responseType: 'token id_token',
          responseMode: 'fragment',
          audience: 'https://mytenant.eu.auth0.com/api/v2/',
          scope: 'openid profile email update:users'
        }
      }
    );

The JWT payload I get in return is

{
  "iss": "https://mytenant.eu.auth0.com/",
  "sub": "facebook|10156699209982922",
  "aud": [
    "https://mytenant.eu.auth0.com/api/v2/",
    "https://mytenant.eu.auth0.com/userinfo"
  ],
  "iat": 1527014876,
  "exp": 1527022076,
  "azp": "PcXb6iEOrHuMvgR9zgSHN6nhcLBuZNYq",
  "scope": "openid profile email"
}

and the update:users scope is missing.

Now, since update:users is not a OIDC standard claim, does it need to be namespaced? And if so, what does the namespaced scope look like?

Thank you for your help!

The specification only defines scopes for the user_info endpoint. These do not need to be namespaced in the same way as the specification requires claims. Since these scopes will be stored as part of the value of the scope claim. I actually think Auth0 might be skimming these scopes automatically as a measure from issuing a token to manage on behalf of a user.

I have to play around, but it might be required to manually add these scopes to the scope claim.

Ok here is what I’ve found. There is a scope called update:current_user_metadata is the only thing Auth0 will permit a user to have scope access to. The reason for this is that is the only property on the profile a user can update directly. You should be careful to allow a user to directly update a profile considering those details could be used to make authorization details.

So unless you want to patch information that isn’t in user_metadata you will need to build a proxy (if you will) that your application can talk to. Then that proxy would, via client_credentials, get its own token to update a user. This prevents the user from having a token that could update sensitive information used in rules to determine what the user is and is not allowed to do.

Oh perfect !! thank you Shawn :slight_smile:
I added in scope update:current_user_metadata and I can see it in access_token.
The profil update work fine now !!
Just when you said “those details could be used to make authorization details” , if a user change his email , it could be an impact with authentification ?? the token will be expired ?

Thank you

Just when you said “those details could be used to make authorization details” , if a user change his email , it could be an impact with authentication ?? the token will be expired ?

The token is stateless and will not expire if profile data changes. Let’s say we had a rule that would allow a scope to be added if the user.roles array contained admin. If we issued a token to a user that allowed them to update:users we effectively have given the user a token that has enough power to change their authorization policy. This is not desirable. By design user.user_metadata is allowed to be editable by the user. Any data in there should not influence authorization decisions. In other words we don’t want the user to be able to define their own authorization policies.

Also, for completeness here is a list of all the current user scopes:

read:current_user
update:current_user_identities
create:current_user_metadata
update:current_user_metadata
delete:current_user_metadata
create:current_user_device_credentials
delete:current_user_device_credentials

There is a scope called update:current_user_metadata

Thank you, @sgmeyer! Right after I had posted my question I was wondering if update:users would allow updates to any user, not just the current one - but update:current_user_metadata solves that :slight_smile:

Thank you for your help!

2 Likes