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