Issues migrating from auth0-js to auth0-angular or auth0-spa-js

Auth0 Community post

Issues migrating from auth0-js to auth0-angularor auth0-spa-js

We have a large monorepo running multiple Angular apps, on Angular v18.04 (soon to be upgraded to Angular v19).

For years we have been using auth0-js SDK but we are moving over to using some features of the Universal Login signup flows, which aren’t supported in that SDK. Namely passing custom params into the authorise request to allow us to handle client branding in the login and signup forms.

We were advised on these forums to upgrade to auth0-angular SDK (v2.2.3). Seems this only supports up to v17 of Angular. I tried anyway but ran into issues.

Following the QuickStart and the docs, in our AppModule when the app bootstraps, I tried all the various ways of initialising the SDK:

  • Using provider initialisation function
  • Using AuthModule.forRoot(…) in the imports
  • Using provideAuth0(…) in the providers

But hit the following errors:

NullInjectorError: NullInjectorError: No provider for InjectionToken auth0.client!

Or

RuntimeError: NG0200: Circular dependency in DI detected for _location.

I assumed this is possibly related to the SDK not supporting Angular 18+?
Do we know when the SDK will support Angular v18 and 19?

So the fallback was to go with auth0-spa-js instead (v2.1.3).

The major difference is that the old auth0-js used Implicit Flow and the newer SDKs uses the Proof Key for Code Exchange (PKCE) flow. Once users were authenticated, our code used to retrieve the access token from the ‘access_token’ searchParam and returnUrl (from state searchParam) of the /callback URL auth0 redirected to.

I’m having more success with the auth0-spa-js SDK but struggling to retrieve the returnTo URL once the user is authenticated and back in our app.

Auth0 Client init:

const auth0ClientOptions: Auth0ClientOptions = {
      domain: 'from our injected config',
      clientId: 'from our injected config',
      useRefreshTokens: true,
      useRefreshTokensFallback: true,
      cacheLocation: 'localstorage',,
      authorizationParams: {
        scope: 'openid offline_access',
        audience: 'from our injected config',
        redirect_uri: window.location.origin + '/callback',
      },
    };

    this.auth0Client = new Auth0Client(auth0ClientOptions);

App bootstrapping:

We have a lot o complex code running as our app bootstraps. The following is called as part of APP_INITIALIZER injection token provider. This checks if user has active session then redirects to the auth0 login if not.

async authorizeIfNoSession(state: string = ''): Promise<boolean> {
    if (isMeticulousTestRunner()) return false;

    try {
      const token = await this.auth0Client.getTokenSilently();
      await this.setupUserFromJwt(token);
      return false;
    } catch (error) {
      this.configurationService.loggingIn = true;
      await this.auth0Client.loginWithRedirect({
        authorizationParams: {
          state: state,
          prompt: 'login',
          redirect_uri: window.location.origin + '/callback',
        },
      });

      return true;
    }
  }

Once auth0 redirects:

As our pp bootstraps we also run a custom service (BaseHrefService) via the APP_BASE_HREF injection token in our AppModule. As our app is a multi-tenant app and can be running on different ephemeral environments we do a lot of URL manipulation in this service as well as handling the auth0 callback (hence not having custom /callback routed app component like I have seen other people use). Here the code needs access to the returnTo URL as well as the access token, in order to use some of the user’s claims.

As mentioned above previously we used to extract the return URL from the state searchParam in the callback URL. With the SPA SDK, the callback URL you redirect to now has ‘code’ and ‘state’ searchParams. And state is no longer the return URL.

So in our BaseHrefService I check if the user is authenticated (auth0Client.isAuthenticated()), then if true I call the following function:

Nb: this code was adapted from the SPA sample app (see line 117 here auth0-javascript-samples/01-Login/public/js/app.js at master · auth0-samples/auth0-javascript-samples · GitHub)

async getReturnUrl(): Promise<string> {
    const defaultRoute = this.window.location.href;

    const query = window.location.search;
    const shouldParseResult = query.includes('code=') && query.includes('state=');

    if (shouldParseResult && !this.redirectUrl) {
      try {
        // parse the URL hash/query params
        const result = await this.auth0Client.handleRedirectCallback();

        console.log('auth0 handleRedirectCallback result: ', result);

        // get the URL to return to (specified during login)
        const returnTo = result.appState?.returnTo || defaultRoute;

        this.redirectUrl = returnTo;

        return returnTo;
      } catch (error) {
        console.log('Error handling redirect:', error);
        return defaultRoute;
      }
    }

    return defaultRoute;
  }

When this is called I am seeing lots of weird behaviour, errors such as:

  • Invalid State
  • or the call to auth0Client.handleRedirectCallback() returns no AppState
  • or other promises resolving where we previously were calling getTokenSilently().

Debugging I can see the user is authenticated and I can get the access token. Just can’t figure out how to get the returnTo URL.

Running the auth0 SPA sample, I had similar issues and could not debug the response from handleRedirectCallback(). I never gets hit once logged in.

Can anyone suggest what I’m doing wrong here or the best way to get the originating returnTo URL that was sent into the login request?

Thanks in advance

Hi @sifloww

Welcome to the Auth0 Community!

Thank you for posting your inquiry regarding migration to auth0-spa-js and I am sorry for the delayed response to your post.

I believe i was able to reproduce your problem. Using the Auth0 Sample App, if I were to create a function which returns the returnTo URL just as you have done in your implementation then call it after checking if the user is authenticated ( auth0Client.isAuthenticated() ), the app would not redirect them and keep them inside a loop.

if (isAuthenticated) {
    console.log("> User is authenticated"); 
    await getURL();
    updateUI();
    return;
  }

I would recommend to call the function after the authentication is done, outside of the scope, that should redirect the user the according page where they are logged in as seen in the example below:

if (isAuthenticated) {
    console.log("> User is authenticated"); 
    window.history.replaceState({}, document.title, window.location.pathname);
    updateUI();
    return;
  }

  console.log("> User not authenticated");

  await getURL();

With your integration, you should be able to find the state and code that are being returned inside the network trace.

I believe the main issue you were facing is that the application could not redirect the user to the proper URL after being authenticated since the auth0.isAuthenticated() function handles authentication of the user and not authorization, thus after the ID token has been returned it could not properly redirect the user using the implemented function.

Let me know if by any chance you are facing any other issues with your migration or if you have further questions on the matter.

Kind regards,
Nik