AngularFireAuth 模拟器登录在页面重新加载时丢失
Posted
技术标签:
【中文标题】AngularFireAuth 模拟器登录在页面重新加载时丢失【英文标题】:AngularFireAuth emulator login is lost on page reload 【发布时间】:2021-03-09 12:12:13 【问题描述】:我的 Angular 项目成功地使用来自 @angular/fire 的 AngularFireAuth
注入连接到本地 Firebase 身份验证模拟器。我使用这些资源作为我的实现的参考:
Firebase 文档:Instrument your app to talk to the Authentication emulator
堆栈溢出:How to configure AngularFireAuthModule and/or AngularFireAuth to point to auth emulator
但是,一旦我重新加载页面,登录状态就会丢失,我必须重新登录。
首先我尝试在 Angular 组件构造函数中像这样设置持久性:
constructor( private _fireauth: AngularFireAuth)
this._fireAuth.useEmulator(`http://$location.hostname:9099/`);
this._fireauth.setPersistence(firebase.auth.Auth.Persistence.LOCAL);
这没什么区别。然后我在浏览器网络控制台中注意到,显然 firebase 库在页面加载后确实找到了本地存储的会话,但最初尝试联系谷歌服务器来验证它,这当然失败了,因为它是一个本地模拟器会话:
这似乎是有道理的,因为_fireAuth.useEmulator()
只在组件构造函数中被调用。应用程序的其他部分(例如AngularFireAuthGuard
)可能会尝试更早地访问身份验证状态。在应用程序的任何其他部分可能会尝试访问身份验证状态之前调用 useEmulator()
的几次尝试失败:
Attempt1:
在 @Injectable(providedIn: 'root')
又名服务构造函数中调用 useEmulator()
Attempt2:
提供一个 APP_INITIALIZER
在应用程序启动之前执行代码:
export function initializeApp1(afa: AngularFireAuth): any
return () =>
return new Promise(resolve =>
afa.useEmulator(`http://$location.hostname:9099/`);
setTimeout(() => resolve(), 5000);
);
;
@NgModule(
declarations: [
...
],
imports: [
...,
AngularFireModule.initializeApp(environment.firebase),
AngularFireAuthModule,
...
],
providers: [
AngularFireAuth,
provide: APP_INITIALIZER,
useFactory: initializeApp1,
deps: [AngularFireAuth],
multi: true
],
bootstrap: [AppComponent]
)
Attempt3:
在AngularFireAuth
构造函数中提供USE_EMULATOR
可注入的seems to be implemented:
providers: [
provide: USE_EMULATOR,
useValue: [location.hostname, 9099]
,
]
我特别认为Attempt3
会尽可能早地设置模拟器模式,因为它可以比在构造函数中设置早多少? Injectable 似乎可以工作,因为浏览器控制台会在页面加载时打印通常的警告消息:auth.esm.js:133 WARNING: You are using the Auth Emulator, which is intended for local testing only. Do not use with production credentials.
。但是,每次在页面加载时检测到本地 indexedDB 中的存储会话时,仍然会触发对 googleapis.com
的请求,然后我就退出了。另外值得注意的是:在 Attempt2
中,我使用 5 秒超时来显着减慢应用程序初始化速度,并且在页面加载后立即触发对 googleapis.com
的请求,在 Angular 完成初始化之前。
此时我不知道该尝试什么了。 @angular/fire
is obviously build upon the basic Firebase javascript library,所以我可能不得不更早地设置模拟器模式,但也许我在这里走错了方向?
================================================ ==================
[编辑:] 似乎将Attempt 2
与Attempt 3
结合起来是可行的,只要initializeApp1()
方法使用的setTimout()
至少约为。 100 毫秒。当我删除超时或将其设置为低至 10 毫秒时,登录就会丢失。任何超过 100 毫秒的超时也有效。在这一点上,它看起来很像竞态条件?
工作示例:
export function initializeApp1(afa: AngularFireAuth): any
return () =>
return new Promise(resolve =>
afa.useEmulator(`http://$location.hostname:9099/`);
setTimeout(() => resolve(), 100); // delay Angular initialization by 100ms
);
;
@NgModule(
declarations: [
...
],
imports: [
...,
AngularFireModule.initializeApp(environment.firebase),
AngularFireAuthModule,
...
],
providers: [
// Provide the AngularFireAuth constructor with the emulator parameters
provide: USE_EMULATOR,
useValue: [location.hostname, 9099]
,
// Delay the app initialization process by 100ms
provide: APP_INITIALIZER,
useFactory: initializeApp1,
// for some reason this dependency is necessary for this solution to work.
// Maybe in order to trigger the constructor *before* waiting 100ms?
deps: [AngularFireAuth],
multi: true
],
bootstrap: [AppComponent]
)
我还注意到AngularFireAuth
源代码中的this comment。到目前为止,我真的不明白发生了什么,但也许问题是相关的:
// HACK,因为我们正在导出 auth.Auth,而不是 auth,导入 firebase.auth(例如,
import auth from 'firebase/app'
)的开发人员会意外地得到一个未定义的 auth 对象,因为我们完全是懒惰的。让我们在这里急切地加载 Auth SDK。可能仍然存在竞争条件……但这大大降低了我们重新评估 API 的几率。
【问题讨论】:
在同样的问题上苦苦挣扎。 auth 模拟器相当新,它可能是需要在其中一个库中纠正的错误/缺失功能。 我刚刚添加了一些新见解。它看起来像是AngularFireAuth
可注入的构造函数中的竞争条件。
我也发现了那个评论,但是调试多了一点后我更加不确定了。
其实是buggithub.com/firebase/firebase-js-sdk/issues/4110
我设法通过清理我的依赖项来绕过它,以便在身份验证后初始化 firestore。我发现我的自定义身份验证服务依赖于 firestore。
【参考方案1】:
以下解决方案对我有用 -
将此添加到 app.module.ts 的顶部(@NgModule
之前)-
const firebaseConfig =
...
;
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import 'firebase/functions';
const app = firebase.initializeApp(firebaseConfig, 'myApp');
if (environment.useEmulators)
app.auth().useEmulator('http://localhost:9099');
app.firestore().useEmulator('localhost', 8080);
app.functions().useEmulator('localhost', 5001);
然后在您的 imports 部分进一步向下:
imports: [
...
AngularFireModule.initializeApp(firebaseConfig, 'myApp'),
...
],
我猜这个方法比 AngularFire 更早地“获取”了 Auth 服务。但正如您所提到的,身份验证模拟器是相当新的,希望在下一个版本中它会得到修复。基于this github post。
【讨论】:
我按照你的步骤,好像和github.com/firebase/firebase-js-sdk/issues/4110差不多。但是,我的身份验证似乎仍在云端。我正在使用 Angular 9.1.13、Firebase 8.9.1 和 AngularFire 6.1.5。【参考方案2】:我已经在我的代码中调整了初始化应用程序函数,以使用 rxjs 而不是 Promise。
export const initializeApp = (angularFireAuth: AngularFireAuth): (() => Observable<boolean>) =>
if (!environment.firebaseConfig.useEmulator)
return () => of(true);
else
return () => of(true).pipe(
delay(100),
tap(_ =>
angularFireAuth.useEmulator('http://localhost:9099/');
)
);
;
【讨论】:
以上是关于AngularFireAuth 模拟器登录在页面重新加载时丢失的主要内容,如果未能解决你的问题,请参考以下文章
python requests 爬虫模拟登录后访问一些界面还是会重定向到登录界面?