Using Auth0 authentication

We currently use Auth0 to verify that we have permission to access our API,however we’ve found that if we call it from the default function then Auth0 gets called over and over again and gets very upset.

So I’ve tried to put it into the Setup function and I can see it calling Auth0 and it retrieving the token but when I then try to assign that token to a global variable and use it to set the Auth header in the calls to our service, it’s undefined.

This is my setup function:

let externalIdentityToken; //this is set up at the top of the function with all the k6 variables

export function setup() {
    let auth0Request = JSON.stringify({
        client_id: environment.Auth0TestAppClientId,
        client_secret: environment.Auth0TestAppClientSecret,
        scope: environment.Auth0TestAppScope,
        username: environment.Auth0TestAccountUsername,
        password: environment.Auth0TestAccountPassword,
        grant_type: environment.Auth0TestAccountGrantType,
        realm: environment.Auth0Realm,
    });

    let url = `${environment.Auth0Domain}/oauth/token`;

    let params = {
        headers: {
            "Content-Type": "application/json",
        },
    };

    let response = http.post(url, auth0Request, params);

    externalIdentityToken = response.json().id_token;

    //console.log(JSON.stringify(externalIdentityToken));
}

However as soon as you use that variable within the default function, it’s undefined.

Is this the correct way to setup something I need to use repeatedly later on or is there a better way?

Hi @joanne.ainscough,
The setup and the default function aren’t ran in the same JS VM. This obviously won’t work with multiple VUs as they are different JS VMs and setup is ran only once per the whole test.

This means that any “global” variable is only global for the current VU.

The simplest fix for what you want is to use the fact that setup can return data to the default function(s) (and teardown). There is are two examples in the documentation about setup and teardown.

Arguably, though, there should be a token per VU and it probably has an expiration time, so it might just be better to make your own http.request function that adds the token and regets it every once in a while ( for example on 401s or just before it expires).

Hope this helps

HI @mstoykov ,

I’m interested in learning about what you said of making your own http.request that adds the token and regets it every once in a while? How would you do that? I was trying to create a function that uses setTimeout but k6 doesn’t allow you to use nodejs libraries. I was curious what function will be needed to get it after some time elapsed per VU since each VU will be using a different token.

@bbb513,

Not really checked but something like:

var token;
var tokenExperyTime;

function request(url, body, params) {
  if (Time.now() > tokenExperyTime) {
    // renew token
    tokenExpiryTime = Time.now() + 5000;
  }
  // use token
}

Even if setTimeout was supported I would still not recommend using it as it doesn’t gurantee that it will run before the token expire and a request using it will be done.

Hope this helps

for setting the token per vu you could use this once only code…

import exec from 'k6/execution';
export default function () {

if (exec.scenario.iterationInTest == 0){
//      do one thing like autho login.
 }

}

you could even combine it with @mstoykov code and solve two problems at once.

However, I usually avoid and/or in my if statements for simplicity and potential overhead at high volumes.

@BobRuub this won’t work for two reasons:

  1. this code will execute only once for one VU all others will never actually do this and consequently have no access to the token
  2. in the particular case discussed here, we want to renew the token through the execution.

if 2. wasn’t the case using setup is definitely the best option.

Apologies,

should it be exec.vu.iterationInTest

Probably things have changed because the original code does not work any more (or my environment is different), I want to share my code (differences are the added audience and another property name for the token):

export function setup() {
    let auth0Request = JSON.stringify({
        client_id: "<client id>",
        client_secret: "<client secret>",
        scope: "<your defined scopes, like read: users edit:users>",
        username: "test@test.com",
        password: "...",
        grant_type: "client_credentials",
        realm: "Username-Password-Authentication",
        audience: "https://auth0api.yours.com"
    });

  let url = "https://yours.eu.auth0.com/oauth/token";

    let params = {
        headers: {
            "Content-Type": "application/json",
        },
    };

    let response = http.post(url, auth0Request, params);
    //console.log(response); //Enable in case of errors
    //console.log(response.json()); //Enable in case of errors
    
    let externalIdentityToken = response.json().access_token;

    //console.log(JSON.stringify(externalIdentityToken));
    return { token: externalIdentityToken };
}

hi @codingdutch,

please open a new forum thread and add more information including what is the error you get and whether you can get it to not error with a different tool, preferably curl :wink:

Ah, sorry it was a confusing post of me, I realize now. Everything works, I just wanted to post my experiences, maybe it helps other people.
I made a mistake by the way in my original post, the grant_type should be ‘http://auth0.com/oauth/grant-type/password-realm’ for user password authentication.

2 Likes