英文:
Offline Authentication
问题
以下是您提供的文本的翻译部分:
In an Angular PWA I'm using firebase security rules to restrict access to nearly all of my data to authenticated users. In an auth-guard I check if the user is authenticated and if not, the user is signed in anonymously (signInAnonymously()
) so I ensure that I have a user-id that I can use to flag objects that the user creates. This flag is necessary so I know if the user is the creator and if he is allowed to delete his own comment.
This all works fine - also when I lose connectivity. But if I close the PWA, enable flight-mode and then open the PWA (without connectivity) - signInAnonymously
returns an error (because of course the user cannot be signed in on the server). I on the other hand expect to either get the user from LocalStorage / IndexedDb
returned or that after opening the app the onAuthStateChanged
-Event fires with the locally stored user.
None of this happens (although I see, that the user is in fact stored in indexeddb by Firebase). Docs: Authentication State Persistence also state, that
For a web application, the default behavior is to persist a user's session even after the user closes the browser.
What am I missing? What should I do differently to achieve my goal of:
The PWA should behave like a native app. If I have no connectivity, as I use
enableIndexedDbPersistence()
for Firestore, the app should load whichever data is in cache and as soon as connectivity is restored, the app should seamlessly use online data.
Edit:
This is the code of my AuthService
:
@Injectable()
export class AuthService {
private authState: User = null;
private _isAuthenticated = new ReplaySubject<boolean>();
public isAuthenticated$ = this._isAuthenticated.asObservable();
public get userId() {
if this.authState)
return this.authState.uid;
throw "No User-Id!";
}
constructor(private fAuth: Auth) {
onAuthStateChanged(this.fAuth, async (user) => {
AdminConsoleDialogComponent.addLogLine(`onAuthStateChanged: ${user?.uid}`);
this.authState = user;
try {
if (!this authState) {
this authState = (await signInAnonymously(this fAuth)).user;
}
} catch (e) {
AdminConsoleDialogComponent.addLogLine(`onAuthStateChanged Error: ${e}`);
console.error(e);
} finally {
this _isAuthenticated.next(this.authState != null);
}
});
}
}
Output as follows (from addLogLine
-Calls):
- Open the PWA with connectivity:
onAuthStateChanged: tszjaBTtltVo9XV5B4kHAHnNNt22
- Close/Kill the PWA, go into Flight-Mode and reopen PWA without connectivity:
onAuthStateChanged: undefined
onAuthStateChanged Error: FirebaseError: Firebase: Error (auth/internal-error).
- Close/Kill the PWA, go out of Flight-Mode and reopen PWA with connectivity:
onAuthStateChanged: undefined
onAuthStateChanged: D54ENbliWVhyYc9izXjFbtzXhUG2
I just realized that I even get a new anonymous user.
英文:
In an Angular PWA I'm using firebase security rules to restrict access to nearly all of my data to authenticated users. In an auth-guard I check if the user is authenticated and if not, the user is signed in anonymously (signInAnonymously()
) so I ensure that I have a user-id that I can use to flag objects that the user creates. This flag is necessary so I know if the user is the creator and if he is allowed to delete his own comment.
This all works fine - also when I lose connectivity. But if I close the PWA, enable flight-mode and then open the PWA (without connectivity) - signInAnonymously
returns an error (because of course the user cannot be signed in on the server). I on the other hand expect to either get the user from LocalStorage / IndexedDb
returned or that after opening the app the onAuthStateChanged
-Event fires with the locally stored user.
None of this happens (although I see, that the user is in fact stored in indexeddb by Firebase). Docs: Authentication State Persistence also state, that
> For a web application, the default behavior is to persist a user's session even after the user closes the browser.
What am I missing? What should I do differently to achieve my goal of:
> The PWA should behave like a native app. If I have no connectivity, as I use enableIndexedDbPersistence()
for Firestore, the app should load whichever data is in cache and as soon as connectivity is restored, the app should seamlessly use online data.
Edit:
This is the code of my AuthService
:
@Injectable()
export class AuthService {
private authState: User = null;
private _isAuthenticated = new ReplaySubject<boolean>();
public isAuthenticated$ = this._isAuthenticated.asObservable();
public get userId() {
if (this.authState)
return this.authState.uid;
throw "No User-Id!";
}
constructor(private fAuth: Auth) {
onAuthStateChanged(this.fAuth, async (user) => {
AdminConsoleDialogComponent.addLogLine(`onAuthStateChanged: ${user?.uid}`);
this.authState = user;
try {
if (!this.authState) {
this.authState = (await signInAnonymously(this.fAuth)).user;
}
} catch (e) {
AdminConsoleDialogComponent.addLogLine(`onAuthStateChanged Error: ${e}`);
console.error(e);
} finally {
this._isAuthenticated.next(this.authState != null);
}
});
}
}
Output as follows (from addLogLine
-Calls):
- Open the PWA with connectivity:
> onAuthStateChanged: tszjaBTtltVo9XV5B4kHAHnNNt22 - Close/Kill the PWA, go into Flight-Mode and reopen PWA without connectivity:
> onAuthStateChanged: undefined
>
> onAuthStateChanged Error: FirebaseError: Firebase: Error (auth/internal-error). - Close/Kill the PWA, go out of Flight-Mode and reopen PWA with connectivity:
> onAuthStateChanged: undefined
>
> onAuthStateChanged: D54ENbliWVhyYc9izXjFbtzXhUG2
I just realized that I even get a new anonymous user.
答案1
得分: 0
这似乎是你的代码在页面加载时总是调用 signInAnonymously
来匿名登录用户。实际上,这是一种反模式,因为这意味着 SDK 会向服务器发出调用,这就解释了为什么你的代码失败。
恢复用户的登录状态的惯用方法是使用 onAuthStateChanged
监听器,如文档中的第一个代码示例所示,用于获取当前用户的信息(https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user):
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// 用户已登录,查看文档以获取可用属性的列表
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// 用户已注销
// ...
}
});
只有在没有当前用户时,才需要调用 signInAnonymously
(或任何其他登录提供者)来首次登录。
更新 2022-04-04:我尝试在这里复制问题:https://stackblitz.com/edit/auth-v9-restore。
当我使用右侧的小 ↻ 图标刷新预览面板时,它会重新加载匿名用户,无论是在我的没有Wi-Fi连接的笔记本电脑上,还是在飞行模式下的手机上。
英文:
It sounds like your code always calls signInAnonymously
when the page loads, to sign in the user anonymously. That's actually an anti-pattern, as it means the SDK will call to the server, which explains why your code fails.
The idiomatic way to restore the sign-in state of your user is to use an onAuthStateChange
listener, as shown in this first code sample from the documentation on getting the current user:
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
Then only when there is no current user, will you want to call signInAnonymously
(or any other sign-in provider) to sign in for the first time.
Update 2022-04-04: I tried to reproduce the problem here: https://stackblitz.com/edit/auth-v9-restore.
When I refresh the preview panel with the little ↻ icon on the right, it reloads the anonymous user for me both on my laptop with the wifi disabled, and on my phone in airplane mode.
答案2
得分: 0
无法精确确定,但在删除了node_modules/package-lock.json后(并使用这个更新@angular/*软件包从12.2.5
到15.2.7
),似乎正常工作。最近一定有一个错误修复。
英文:
I cannot pinpoint it exactly but after deleting node_modules/package-lock.json (and with this updating @angular/* packages from 12.2.5
to 15.2.7
) it seems to work fine. There must have been a bugfix recently.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论