Search code examples
angularauth0angular15

How can Auth0 be configured to work with Angular Universal SSR starting from the Sample App?


My Angular app using Auth0 with SSR was working fine until upgrade to Auth0 v2.0.1 (as part of upgrade to Angular 15).

There are no compilation errors but the deployed app errors on the server (Heroku deployment)

The error is on app start up and relates to injection of AuthService into my wrapper class.

I've used isPlatformBrowser and isPlatformServer to ensure that code which depends on browser (location) does not run on the server and this worked fine until the upgrade.

ERROR ReferenceError: location is not defined
at AuthService.shouldHandleCallback (...\server\main.js:1:4215732)
at new AuthService (...server\main.js:1:4213627)
at Object.AuthService_Factory [as factory] (...\server\main.js:1:4216049)    
at R3Injector.hydrate (...\server\main.js:1:2376384)
at R3Injector.get (...\server\main.js:1:2374606)
at injectInjectorOnly (...\server\main.js:1:2257584)
at Object.ɵɵinject (...\server\main.js:1:2257754)
at Object.Auth0Wrapper_Factory [as factory] (...\server\main.js:1:160425)    
at R3Injector.hydrate (...\server\main.js:1:2376384)
at R3Injector.get (...\server\main.js:1:2374606)

The error appears to be purely on construction/dependency injection and not on any method call on the injected AuthService (which I've protected with a check on isPlatformBrowser)

Similar issue here (5 years ago), here (1 year ago) and here (9 months ago).

None have accepted answers.


Reproducing the problem from the Auth0 Sample App

  1. Create a new application in the Auth0 dashboard - Type = SPA, Technology Angular
  2. Download the sample app (Defer setting callback URLs in the dashboard until later)
  3. Run - npm install (and update local Angular CLI - ng update @angular/cli @angular/core)
  4. Add Universal SSR - ng add @nguniversal/express-engine

Angular Universal added

At this stage the app will build and run but without the login (npm start) but the server build fails (npm run prerender).

The generated server.ts for SSR requires an update. "Error: server.ts:13:18 - error TS2349: This expression is not callable." (A namespace-style import cannot be called or constructed - stackoverflow)

The solution is to replace import * as express from 'express'; with import express from 'express';

After this update, npm run prerender then fails with

Unhandled Promise rejection: window is not defined ; Zone: <root> ; 
Task: Promise.then ; Value: ReferenceError: window is not defined
at C:\...\A0-with-SSR\dist\login-demo\server\main.js:1:4112058
at C:\...\A0-with-SSR\dist\login-demo\server\main.js:1:4114283

The error above is different from the error in my original application, but the same in nature: location and window are not defined on the server. However in my original application, the error relates to Auth0 initialisation but in the reproduction of the issue using the sample app, the error message does not refer to Auth0.

So how can Auth0 be set up to use Angular Universal SSR from the Sample App provided?


Code is available to clone from GitHub


Additional info required to configure the Auth0 application in the dashboard (Allowed Callback URLs, Web Origins and Logout URLs)

If you use http://localhost:4200 as recommended, you'll get an error, which isn't the subject of this post. This relates to the requirement for https even in the development environment. The only way I've found around this is to create an entry in the hosts file for a test domain pointing to localhost 127.0.0.1

On Windows, update hosts in C:\Windows\System32\drivers\etc to include

# localhost name resolution is handled within DNS itself.
# 127.0.0.1   localhost
# ::1         localhost
127.0.0.1     mylocaldomain.com

A self-signed certificate will need to be generated and installed. More here (use mylocaldomain.com in place of localhost)

Configuration of the Auth0 application can now be completed in the Auth0 dashboard using https://mylocaldomain.com:4200

Back in the local Angular Application code, in auth0_config.json, replace http://localhost:4200 with https://mylocaldomain.com:4200 and remove audience from authorizationParams (we're not concerned with the backend API here)

Amend the start script in package.json to use ssl with the self signed certificate

"start":"ng serve -o --host mylocaldomain.com --ssl --ssl-key ssl\server.key --ssl-cert ssl\server.crt",

The start script can now be run and login to Auth0 is possible.


Solution

  • Frederik Prijk at Auth0 has provided the answer to this, which, in summary, is to ensure the AuthModule is not imported on the server.

    • In app.module, remove AuthModule from imports and AuthHttpInterceptors from providers
    • Add the above to a new AppClientModule app-client.module.ts , which imports both AppModule and AuthModule.
    • Update AppServerModule to replace AuthService and AuthConfigService

    This is the app.server.module providers:

    providers: [
      {
        provide: AuthService,
        useValue: {},
      },
      { provide: AuthConfigService, 
        useValue: {} as any },
    ],
    

    More details here - https://github.com/auth0/auth0-angular/issues/432

    Sample app on GitHub here - https://github.com/CueToDo/add-A0-to-SSR