如何基于Auth0示例应用配置Angular Universal SSR及解决升级报错
Angular 15 + Auth0 v2.0.1 SSR部署Heroku启动报错解决方案
问题描述
升级Angular到15版本、Auth0到v2.0.1后,带SSR的Angular应用部署到Heroku启动报错。编译无问题,但在依赖注入阶段将AuthService注入自定义包装类时触发location is not defined错误。已用isPlatformBrowser和isPlatformServer隔离浏览器相关代码,升级前逻辑正常,且错误仅发生在构造/依赖注入阶段,而非调用AuthService方法时。
错误日志
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)
问题重现步骤
- 在Auth0控制台创建SPA类型Angular应用
- 下载示例应用,执行
npm install并升级Angular CLI - 添加Universal SSR:
ng add @nguniversal/express-engine - 修改server.ts中express导入方式后,执行
npm run prerender触发window is not defined错误
解决方案
一、代码配置调整
1. 延迟初始化Auth0客户端
避免服务在服务器端初始化时访问浏览器API,仅在浏览器环境下初始化Auth0客户端:
// auth.service.ts import { Injectable, PLATFORM_ID, Inject } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; import { Auth0Client } from '@auth0/auth0-spa-js'; @Injectable({ providedIn: 'root' }) export class AuthService { private auth0Client: Auth0Client | null = null; constructor(@Inject(PLATFORM_ID) private platformId: object) {} async initAuth0Client() { if (isPlatformBrowser(this.platformId) && !this.auth0Client) { this.auth0Client = new Auth0Client({ domain: '你的Auth0域名', client_id: '你的客户端ID', redirect_uri: `${window.location.origin}/callback` }); } } async handleCallback() { await this.initAuth0Client(); if (this.auth0Client) { await this.auth0Client.handleRedirectCallback(); } } // 其他Auth0相关方法需先调用initAuth0Client }
2. 调整包装类注入逻辑
自定义包装类Auth0Wrapper避免在构造函数中执行依赖浏览器API的逻辑,移到组件初始化阶段:
// auth0-wrapper.service.ts import { Injectable, Inject, PLATFORM_ID } from '@angular/core'; import { AuthService } from './auth.service'; import { isPlatformBrowser } from '@angular/common'; @Injectable({ providedIn: 'root' }) export class Auth0Wrapper { constructor( private authService: AuthService, @Inject(PLATFORM_ID) private platformId: object ) {} async init() { if (isPlatformBrowser(this.platformId)) { await this.authService.initAuth0Client(); // 此处处理回调等逻辑 } } }
在AppComponent的ngOnInit中调用Auth0Wrapper.init(),而非服务构造函数。
3. Server.ts适配处理
拦截Auth0回调路由,直接返回客户端应用让浏览器处理:
// server.ts import { ngExpressEngine } from '@nguniversal/express-engine'; import * as express from 'express'; import { join } from 'path'; import { AppServerModule } from './src/main.server'; import { APP_BASE_HREF } from '@angular/common'; const app = express(); const distFolder = join(process.cwd(), 'dist/your-app/browser'); app.engine('html', ngExpressEngine({ bootstrap: AppServerModule, })); app.set('view engine', 'html'); app.set('views', distFolder); app.get('*.*', express.static(distFolder, { maxAge: '1y' })); // 拦截回调路由,交由客户端处理 app.get('/callback', (req, res) => { res.sendFile(join(distFolder, 'index.html')); }); app.get('*', (req, res) => { res.render('index', { req, providers: [ { provide: APP_BASE_HREF, useValue: req.baseUrl } ]}); });
二、Auth0控制台配置
1. 应用核心配置
- 确认应用类型为Single Page Application
- 在「Application URIs」中添加:
- 允许的回调URL:
https://你的Heroku域名/callback、http://localhost:4200/callback(本地开发) - 允许的注销URL:
https://你的Heroku域名/、http://localhost:4200/ - 允许的Web Origins:
https://你的Heroku域名、http://localhost:4200
- 允许的回调URL:
2. 本地开发环境配置
- Hosts文件修改:
Windows路径:C:\Windows\System32\drivers\etc\hosts;macOS/Linux路径:/etc/hosts,添加一行:127.0.0.1 local.your-app.com - 自签名证书生成(Auth0要求回调地址为HTTPS):
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes - Angular本地启动配置:
ng serve --ssl --ssl-key key.pem --ssl-cert cert.pem --host local.your-app.com --port 4200 - 在Auth0控制台添加
https://local.your-app.com:4200/callback到允许的回调URL和Web Origins。
内容的提问来源于stack exchange,提问作者Alex Cooper
相关产品推荐
相关产品推荐

