如何在启动 angular 应用程序之前等待 firebase 身份验证
How to wait for firebase auth before starting angular app
我想在启动 "for real" 单页应用程序之前,在 firebase 身份验证检索用户令牌时显示一个小的加载徽标。
到目前为止我有一个身份验证服务:
constructor(
public afAuth: AngularFireAuth,
) {
this.afAuth.onAuthStateChanged(user => {
if (user) {
this.setCredentials(user)
}
})
}
setCredentials(user: firebase.User) {
return user.getIdTokenResult(true).then(idTokenResult => {
this.credentials = {
userId: idTokenResult.claims.id,
role: idTokenResult.claims.role,
token: idTokenResult.token,
};
// STARTS THE APPLICATION NOW ?
})
}
是否有可能实现这样的行为?我读过 APP_INITIALIZER 但没有成功。我想避免使用 localstorage / session 存储,而是完全依赖于这个初始化。
更新 :
创建了一个初始化函数:
export function initApp(auth: AuthService, afAuth: AngularFireAuth) {
return () => {
return new Promise((resolve) => {
afAuth.user.pipe(
take(1),
).subscribe(user => {
if (user) {
auth.setCredentials(user)
.then(() => resolve())
} else {
resolve();
}
})
});
}
}
并编辑了 AppModule 提供程序:
providers: [
interceptorProviders /* my interceptors */,
{
provide: APP_INITIALIZER,
useFactory: initApp,
deps: [AuthService, AngularFireAuth],
multi: true
}
]
仍然需要弄清楚如何添加等待徽标,但这是另一个问题。我会尽快更新的。
您应该在 CanActivate
路由器防护中使用身份验证服务:https://angular.io/api/router/CanActivate
这意味着您的 AppModule 将首先加载,然后您的子路由(例如带有路由器路径 '' 的 MainModule)具有守卫。然后在 AppModule 中,您可以检查服务的状态并显示加载信息,直到 MainModule 被激活(当 firebase 身份验证完成时)
回答我自己的问题
总而言之,在处理路由之前,我想确保与 firebase 用户关联的令牌声明(角色和用户 ID)存储在我的身份验证服务中,因为这些路由中的组件将使用这些凭据。
最后我没有按照APP_INITIALIZER 确实不是很好的解决方案
授权服务
private _credentials: BehaviorSubject<Credentials> = new BehaviorSubject<Credentials>(null);
public readonly credentials$: Observable<Credentials> = this._credentials.asObservable();
constructor(private afAuth: AngularFireAuth) {
this.afAuth.authState.subscribe(user => {
this._credentials.next(null);
if (user) {
user.getIdTokenResult().then(data => {
const credentials = {
role: data.claims.role,
token: data.token,
userId: data.claims.userId
}
this._credentials.next(credentials);
console.log(credentials);
})
} else {
this._credentials.next({role: null, token: null, userId: null});
}
})
}
get credentials(): Credentials {
return this._credentials.value;
}
在 app.component
中显示等待微调器
如果未设置凭据,下面会阻止显示路由。
在模板中:
<div *ngIf="!(credentials$ | async)" class="logged-wrapper">
<div class="spinner-wrapper">
<mat-spinner class="spinner"></mat-spinner>
</div>
</div>
<router-outlet *ngIf="(credentials$ | async)"></router-outlet>
在组件中:
credentials$: Observable<any>;
constructor(
private auth: AuthService,
) {
this.credentials$ = this.auth.credentials$;
}
Auth Guard
takewhile 允许我在继续之前确保我的凭据已设置。
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot):Promise<boolean> {
return new Promise((resolve) => {
this.auth.credentials$.pipe(
takeWhile(credentials => credentials === null),
).subscribe({
complete: () => {
const credentials = this.auth.credentials
if (!credentials.role) {
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } })
resolve(false);
}
if (next.data.roles && next.data.roles.indexOf(credentials.role) === -1) {
this.router.navigate(['/']);
resolve(false);
}
resolve(true)
}
})
})
}
我想在启动 "for real" 单页应用程序之前,在 firebase 身份验证检索用户令牌时显示一个小的加载徽标。
到目前为止我有一个身份验证服务:
constructor(
public afAuth: AngularFireAuth,
) {
this.afAuth.onAuthStateChanged(user => {
if (user) {
this.setCredentials(user)
}
})
}
setCredentials(user: firebase.User) {
return user.getIdTokenResult(true).then(idTokenResult => {
this.credentials = {
userId: idTokenResult.claims.id,
role: idTokenResult.claims.role,
token: idTokenResult.token,
};
// STARTS THE APPLICATION NOW ?
})
}
是否有可能实现这样的行为?我读过 APP_INITIALIZER 但没有成功。我想避免使用 localstorage / session 存储,而是完全依赖于这个初始化。
更新 :
创建了一个初始化函数:
export function initApp(auth: AuthService, afAuth: AngularFireAuth) {
return () => {
return new Promise((resolve) => {
afAuth.user.pipe(
take(1),
).subscribe(user => {
if (user) {
auth.setCredentials(user)
.then(() => resolve())
} else {
resolve();
}
})
});
}
}
并编辑了 AppModule 提供程序:
providers: [
interceptorProviders /* my interceptors */,
{
provide: APP_INITIALIZER,
useFactory: initApp,
deps: [AuthService, AngularFireAuth],
multi: true
}
]
仍然需要弄清楚如何添加等待徽标,但这是另一个问题。我会尽快更新的。
您应该在 CanActivate
路由器防护中使用身份验证服务:https://angular.io/api/router/CanActivate
这意味着您的 AppModule 将首先加载,然后您的子路由(例如带有路由器路径 '' 的 MainModule)具有守卫。然后在 AppModule 中,您可以检查服务的状态并显示加载信息,直到 MainModule 被激活(当 firebase 身份验证完成时)
回答我自己的问题
总而言之,在处理路由之前,我想确保与 firebase 用户关联的令牌声明(角色和用户 ID)存储在我的身份验证服务中,因为这些路由中的组件将使用这些凭据。
最后我没有按照APP_INITIALIZER 确实不是很好的解决方案
授权服务
private _credentials: BehaviorSubject<Credentials> = new BehaviorSubject<Credentials>(null);
public readonly credentials$: Observable<Credentials> = this._credentials.asObservable();
constructor(private afAuth: AngularFireAuth) {
this.afAuth.authState.subscribe(user => {
this._credentials.next(null);
if (user) {
user.getIdTokenResult().then(data => {
const credentials = {
role: data.claims.role,
token: data.token,
userId: data.claims.userId
}
this._credentials.next(credentials);
console.log(credentials);
})
} else {
this._credentials.next({role: null, token: null, userId: null});
}
})
}
get credentials(): Credentials {
return this._credentials.value;
}
在 app.component
中显示等待微调器如果未设置凭据,下面会阻止显示路由。 在模板中:
<div *ngIf="!(credentials$ | async)" class="logged-wrapper">
<div class="spinner-wrapper">
<mat-spinner class="spinner"></mat-spinner>
</div>
</div>
<router-outlet *ngIf="(credentials$ | async)"></router-outlet>
在组件中:
credentials$: Observable<any>;
constructor(
private auth: AuthService,
) {
this.credentials$ = this.auth.credentials$;
}
Auth Guard
takewhile 允许我在继续之前确保我的凭据已设置。
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot):Promise<boolean> {
return new Promise((resolve) => {
this.auth.credentials$.pipe(
takeWhile(credentials => credentials === null),
).subscribe({
complete: () => {
const credentials = this.auth.credentials
if (!credentials.role) {
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } })
resolve(false);
}
if (next.data.roles && next.data.roles.indexOf(credentials.role) === -1) {
this.router.navigate(['/']);
resolve(false);
}
resolve(true)
}
})
})
}