@sneat/auth-core 0.27.39 → 0.27.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/index.js +1 -0
- package/esm2022/index.js.map +1 -1
- package/esm2022/lib/pending-credential-link.js +122 -0
- package/esm2022/lib/pending-credential-link.js.map +1 -0
- package/esm2022/lib/sneat-auth-state-service.js +92 -3
- package/esm2022/lib/sneat-auth-state-service.js.map +1 -1
- package/index.d.ts +1 -0
- package/lib/pending-credential-link.d.ts +39 -0
- package/lib/sneat-auth-state-service.d.ts +19 -0
- package/package.json +1 -1
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
package/esm2022/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from './lib/sneat-auth.interface';
|
|
|
4
4
|
export * from './lib/sneat-auth-guard';
|
|
5
5
|
export * from './lib/redirect-to-login.guard';
|
|
6
6
|
export * from './lib/sneat-auth-state-service';
|
|
7
|
+
export * from './lib/pending-credential-link';
|
|
7
8
|
export * from './lib/telegram-auth.service';
|
|
8
9
|
export * from './lib/user';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
package/esm2022/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/auth/core/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,YAAY,CAAC","sourcesContent":["export * from './lib/login-required-service.service';\nexport * from './lib/private-token-store.service';\nexport * from './lib/sneat-auth.interface';\nexport * from './lib/sneat-auth-guard';\nexport * from './lib/redirect-to-login.guard';\nexport * from './lib/sneat-auth-state-service';\nexport * from './lib/telegram-auth.service';\nexport * from './lib/user';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/auth/core/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,YAAY,CAAC","sourcesContent":["export * from './lib/login-required-service.service';\nexport * from './lib/private-token-store.service';\nexport * from './lib/sneat-auth.interface';\nexport * from './lib/sneat-auth-guard';\nexport * from './lib/redirect-to-login.guard';\nexport * from './lib/sneat-auth-state-service';\nexport * from './lib/pending-credential-link';\nexport * from './lib/telegram-auth.service';\nexport * from './lib/user';\n"]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { FacebookAuthProvider, GithubAuthProvider, GoogleAuthProvider, OAuthCredential, OAuthProvider, } from 'firebase/auth';
|
|
2
|
+
/**
|
|
3
|
+
* Firebase's error for "this email already has an account that signs in a
|
|
4
|
+
* different way". The Sneat identity project allows one account per email, so
|
|
5
|
+
* signing in with GitHub when the account was created with Google (or an
|
|
6
|
+
* email link) fails with it.
|
|
7
|
+
*/
|
|
8
|
+
export const ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL = 'auth/account-exists-with-different-credential';
|
|
9
|
+
/** Where the credential waits for the user to sign in the usual way. */
|
|
10
|
+
export const PENDING_CREDENTIAL_LINK_STORAGE_KEY = 'sneat:auth:pending-credential-link';
|
|
11
|
+
/**
|
|
12
|
+
* How long a pending credential is kept. Long enough to finish an email-link
|
|
13
|
+
* sign-in in another tab; short enough that a forgotten one does not link on
|
|
14
|
+
* some unrelated later sign-in.
|
|
15
|
+
*/
|
|
16
|
+
export const PENDING_CREDENTIAL_LINK_TTL_MS = 30 * 60 * 1000;
|
|
17
|
+
export function isAccountExistsWithDifferentCredential(err) {
|
|
18
|
+
return (err?.code ===
|
|
19
|
+
ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL);
|
|
20
|
+
}
|
|
21
|
+
function credentialFromError(err) {
|
|
22
|
+
const providerId = err.customData?._tokenResponse?.providerId;
|
|
23
|
+
// Each provider class only knows how to read its own token response.
|
|
24
|
+
switch (providerId) {
|
|
25
|
+
case 'github.com':
|
|
26
|
+
return GithubAuthProvider.credentialFromError(err);
|
|
27
|
+
case 'google.com':
|
|
28
|
+
return GoogleAuthProvider.credentialFromError(err);
|
|
29
|
+
case 'facebook.com':
|
|
30
|
+
return FacebookAuthProvider.credentialFromError(err);
|
|
31
|
+
default:
|
|
32
|
+
return OAuthProvider.credentialFromError(err);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Reads the credential Firebase hands back with an
|
|
37
|
+
* `account-exists-with-different-credential` error, or undefined for any other
|
|
38
|
+
* error (or when Firebase gave no usable credential).
|
|
39
|
+
*/
|
|
40
|
+
export function pendingCredentialLinkFromError(err, now = Date.now()) {
|
|
41
|
+
if (!isAccountExistsWithDifferentCredential(err)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const authError = err;
|
|
45
|
+
const credential = credentialFromError(authError);
|
|
46
|
+
if (!credential) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
providerId: credential.providerId,
|
|
51
|
+
email: authError.customData?.email,
|
|
52
|
+
createdAt: now,
|
|
53
|
+
credential: credential.toJSON(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function storage() {
|
|
57
|
+
try {
|
|
58
|
+
return globalThis.localStorage;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function savePendingCredentialLink(link) {
|
|
65
|
+
try {
|
|
66
|
+
storage()?.setItem(PENDING_CREDENTIAL_LINK_STORAGE_KEY, JSON.stringify(link));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Storage blocked: the user can still connect the provider from settings.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function clearPendingCredentialLink() {
|
|
73
|
+
try {
|
|
74
|
+
storage()?.removeItem(PENDING_CREDENTIAL_LINK_STORAGE_KEY);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// nothing to clear
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** The stored pending link, or undefined when there is none or it expired. */
|
|
81
|
+
export function loadPendingCredentialLink(now = Date.now()) {
|
|
82
|
+
let link;
|
|
83
|
+
try {
|
|
84
|
+
const json = storage()?.getItem(PENDING_CREDENTIAL_LINK_STORAGE_KEY);
|
|
85
|
+
link = json ? JSON.parse(json) : undefined;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
link = undefined;
|
|
89
|
+
}
|
|
90
|
+
if (!link?.providerId ||
|
|
91
|
+
!link.credential ||
|
|
92
|
+
typeof link.createdAt !== 'number' ||
|
|
93
|
+
now - link.createdAt > PENDING_CREDENTIAL_LINK_TTL_MS) {
|
|
94
|
+
if (link) {
|
|
95
|
+
clearPendingCredentialLink();
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return link;
|
|
100
|
+
}
|
|
101
|
+
/** Restores the Firebase credential saved in a pending link. */
|
|
102
|
+
export function credentialOfPendingLink(link) {
|
|
103
|
+
return OAuthCredential.fromJSON(link.credential) ?? undefined;
|
|
104
|
+
}
|
|
105
|
+
/** A human name for a provider ID, for messages. */
|
|
106
|
+
export function authProviderTitle(providerId) {
|
|
107
|
+
switch (providerId) {
|
|
108
|
+
case 'github.com':
|
|
109
|
+
return 'GitHub';
|
|
110
|
+
case 'google.com':
|
|
111
|
+
return 'Google';
|
|
112
|
+
case 'facebook.com':
|
|
113
|
+
return 'Facebook';
|
|
114
|
+
case 'microsoft.com':
|
|
115
|
+
return 'Microsoft';
|
|
116
|
+
case 'apple.com':
|
|
117
|
+
return 'Apple';
|
|
118
|
+
default:
|
|
119
|
+
return providerId;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=pending-credential-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-credential-link.js","sourceRoot":"","sources":["../../../../../../libs/auth/core/src/lib/pending-credential-link.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AuthCredential,\n FacebookAuthProvider,\n GithubAuthProvider,\n GoogleAuthProvider,\n OAuthCredential,\n OAuthProvider,\n} from 'firebase/auth';\n\n/**\n * Firebase's error for \"this email already has an account that signs in a\n * different way\". The Sneat identity project allows one account per email, so\n * signing in with GitHub when the account was created with Google (or an\n * email link) fails with it.\n */\nexport const ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL =\n 'auth/account-exists-with-different-credential';\n\n/** Where the credential waits for the user to sign in the usual way. */\nexport const PENDING_CREDENTIAL_LINK_STORAGE_KEY =\n 'sneat:auth:pending-credential-link';\n\n/**\n * How long a pending credential is kept. Long enough to finish an email-link\n * sign-in in another tab; short enough that a forgotten one does not link on\n * some unrelated later sign-in.\n */\nexport const PENDING_CREDENTIAL_LINK_TTL_MS = 30 * 60 * 1000;\n\n/** A provider credential to attach to the account once the user signs in. */\nexport interface IPendingCredentialLink {\n readonly providerId: string;\n readonly email?: string;\n readonly createdAt: number;\n /** `OAuthCredential.toJSON()` — restored with `OAuthCredential.fromJSON()`. */\n readonly credential: object;\n}\n\ninterface IAuthErrorShape {\n readonly code?: string;\n readonly customData?: {\n readonly email?: string;\n readonly _tokenResponse?: { readonly providerId?: string };\n };\n}\n\nexport function isAccountExistsWithDifferentCredential(err: unknown): boolean {\n return (\n (err as IAuthErrorShape | undefined)?.code ===\n ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL\n );\n}\n\nfunction credentialFromError(\n err: IAuthErrorShape,\n): OAuthCredential | AuthCredential | null {\n const providerId = err.customData?._tokenResponse?.providerId;\n // Each provider class only knows how to read its own token response.\n switch (providerId) {\n case 'github.com':\n return GithubAuthProvider.credentialFromError(err as never);\n case 'google.com':\n return GoogleAuthProvider.credentialFromError(err as never);\n case 'facebook.com':\n return FacebookAuthProvider.credentialFromError(err as never);\n default:\n return OAuthProvider.credentialFromError(err as never);\n }\n}\n\n/**\n * Reads the credential Firebase hands back with an\n * `account-exists-with-different-credential` error, or undefined for any other\n * error (or when Firebase gave no usable credential).\n */\nexport function pendingCredentialLinkFromError(\n err: unknown,\n now = Date.now(),\n): IPendingCredentialLink | undefined {\n if (!isAccountExistsWithDifferentCredential(err)) {\n return undefined;\n }\n const authError = err as IAuthErrorShape;\n const credential = credentialFromError(authError);\n if (!credential) {\n return undefined;\n }\n return {\n providerId: credential.providerId,\n email: authError.customData?.email,\n createdAt: now,\n credential: credential.toJSON(),\n };\n}\n\nfunction storage(): Storage | undefined {\n try {\n return globalThis.localStorage;\n } catch {\n return undefined;\n }\n}\n\nexport function savePendingCredentialLink(link: IPendingCredentialLink): void {\n try {\n storage()?.setItem(\n PENDING_CREDENTIAL_LINK_STORAGE_KEY,\n JSON.stringify(link),\n );\n } catch {\n // Storage blocked: the user can still connect the provider from settings.\n }\n}\n\nexport function clearPendingCredentialLink(): void {\n try {\n storage()?.removeItem(PENDING_CREDENTIAL_LINK_STORAGE_KEY);\n } catch {\n // nothing to clear\n }\n}\n\n/** The stored pending link, or undefined when there is none or it expired. */\nexport function loadPendingCredentialLink(\n now = Date.now(),\n): IPendingCredentialLink | undefined {\n let link: IPendingCredentialLink | undefined;\n try {\n const json = storage()?.getItem(PENDING_CREDENTIAL_LINK_STORAGE_KEY);\n link = json ? (JSON.parse(json) as IPendingCredentialLink) : undefined;\n } catch {\n link = undefined;\n }\n if (\n !link?.providerId ||\n !link.credential ||\n typeof link.createdAt !== 'number' ||\n now - link.createdAt > PENDING_CREDENTIAL_LINK_TTL_MS\n ) {\n if (link) {\n clearPendingCredentialLink();\n }\n return undefined;\n }\n return link;\n}\n\n/** Restores the Firebase credential saved in a pending link. */\nexport function credentialOfPendingLink(\n link: IPendingCredentialLink,\n): OAuthCredential | undefined {\n return OAuthCredential.fromJSON(link.credential) ?? undefined;\n}\n\n/** A human name for a provider ID, for messages. */\nexport function authProviderTitle(providerId: string): string {\n switch (providerId) {\n case 'github.com':\n return 'GitHub';\n case 'google.com':\n return 'Google';\n case 'facebook.com':\n return 'Facebook';\n case 'microsoft.com':\n return 'Microsoft';\n case 'apple.com':\n return 'Apple';\n default:\n return providerId;\n }\n}\n"]}
|
|
@@ -5,9 +5,10 @@ import { BehaviorSubject, from } from 'rxjs';
|
|
|
5
5
|
import { Injectable, inject } from '@angular/core';
|
|
6
6
|
import { ErrorLogger } from '@sneat/core';
|
|
7
7
|
import { distinctUntilChanged, shareReplay } from 'rxjs/operators';
|
|
8
|
-
import { getAuth, signInWithCredential, GoogleAuthProvider, OAuthProvider, GithubAuthProvider, FacebookAuthProvider, signInWithEmailLink, signInWithCustomToken, signInWithPopup, signInWithRedirect, linkWithPopup, unlink, createUserWithEmailAndPassword, signInWithEmailAndPassword, } from 'firebase/auth';
|
|
8
|
+
import { getAuth, signInWithCredential, GoogleAuthProvider, OAuthProvider, GithubAuthProvider, FacebookAuthProvider, signInWithEmailLink, signInWithCustomToken, signInWithPopup, signInWithRedirect, linkWithPopup, linkWithCredential, unlink, createUserWithEmailAndPassword, signInWithEmailAndPassword, } from 'firebase/auth';
|
|
9
9
|
// TODO: fix & remove this eslint hint @nrwl/nx/enforce-module-boundaries
|
|
10
10
|
import { newRandomId } from '@sneat/random';
|
|
11
|
+
import { clearPendingCredentialLink, credentialOfPendingLink, loadPendingCredentialLink, pendingCredentialLinkFromError, savePendingCredentialLink, } from './pending-credential-link';
|
|
11
12
|
import * as i0 from "@angular/core";
|
|
12
13
|
export var AuthStatuses;
|
|
13
14
|
(function (AuthStatuses) {
|
|
@@ -44,6 +45,16 @@ export class SneatAuthStateService {
|
|
|
44
45
|
// tap(v => console.log('SneatAuthStateService => SneatAuthState:', v)),
|
|
45
46
|
shareReplay(1));
|
|
46
47
|
this.tokenGeneration = 0;
|
|
48
|
+
this.pendingCredentialLink$ = new BehaviorSubject(
|
|
49
|
+
// Reading also purges an expired entry left from an earlier visit, so a
|
|
50
|
+
// stale access token does not linger in storage.
|
|
51
|
+
loadPendingCredentialLink());
|
|
52
|
+
/**
|
|
53
|
+
* The pending link as it changes — a redirect sign-in error is read after
|
|
54
|
+
* the login page has rendered, so the page subscribes rather than reading
|
|
55
|
+
* once.
|
|
56
|
+
*/
|
|
57
|
+
this.pendingCredentialLinkChanges = this.pendingCredentialLink$.asObservable();
|
|
47
58
|
const errorLogger = this.errorLogger;
|
|
48
59
|
this.fbAuth.onIdTokenChanged({
|
|
49
60
|
next: (firebaseUser) => {
|
|
@@ -139,6 +150,9 @@ export class SneatAuthStateService {
|
|
|
139
150
|
return;
|
|
140
151
|
}
|
|
141
152
|
const status = AuthStatuses.authenticated;
|
|
153
|
+
if (fbUser && !fbUser.isAnonymous) {
|
|
154
|
+
void this.linkPendingCredential(fbUser);
|
|
155
|
+
}
|
|
142
156
|
this.authUser$.next(authUser);
|
|
143
157
|
this.authState$.next({
|
|
144
158
|
...this.authState$.value,
|
|
@@ -180,6 +194,7 @@ export class SneatAuthStateService {
|
|
|
180
194
|
}
|
|
181
195
|
signOut() {
|
|
182
196
|
clearCurrentSpace();
|
|
197
|
+
this.clearPendingCredentialLink();
|
|
183
198
|
return this.fbAuth.signOut();
|
|
184
199
|
}
|
|
185
200
|
signInWithToken(token) {
|
|
@@ -249,8 +264,82 @@ export class SneatAuthStateService {
|
|
|
249
264
|
await signInWithRedirect(this.fbAuth, authProvider);
|
|
250
265
|
return undefined;
|
|
251
266
|
}
|
|
252
|
-
|
|
253
|
-
|
|
267
|
+
try {
|
|
268
|
+
return await signInWithPopup(this.fbAuth, authProvider);
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
this.rememberPendingCredentialLink(err);
|
|
272
|
+
throw err;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Keeps the credential from an `account-exists-with-different-credential`
|
|
277
|
+
* error, so it is linked to the existing account as soon as the user signs
|
|
278
|
+
* in the way they did before. Returns the pending link, or undefined for any
|
|
279
|
+
* other error. Call it for redirect sign-in errors too.
|
|
280
|
+
*/
|
|
281
|
+
rememberPendingCredentialLink(err) {
|
|
282
|
+
const link = pendingCredentialLinkFromError(err);
|
|
283
|
+
if (link) {
|
|
284
|
+
savePendingCredentialLink(link);
|
|
285
|
+
this.pendingCredentialLink$.next(link);
|
|
286
|
+
this.analyticsService.logEvent('pendingCredentialLinkSaved', {
|
|
287
|
+
provider: link.providerId,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return link;
|
|
291
|
+
}
|
|
292
|
+
/** The provider waiting to be linked on next sign-in, if any. */
|
|
293
|
+
get pendingCredentialLink() {
|
|
294
|
+
return loadPendingCredentialLink();
|
|
295
|
+
}
|
|
296
|
+
clearPendingCredentialLink() {
|
|
297
|
+
clearPendingCredentialLink();
|
|
298
|
+
if (this.pendingCredentialLink$.value) {
|
|
299
|
+
this.pendingCredentialLink$.next(undefined);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async linkPendingCredential(fbUser) {
|
|
303
|
+
const link = loadPendingCredentialLink();
|
|
304
|
+
if (!link) {
|
|
305
|
+
// Gone or expired: stop announcing it.
|
|
306
|
+
this.clearPendingCredentialLink();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (fbUser.providerData.some((p) => p.providerId === link.providerId)) {
|
|
310
|
+
this.clearPendingCredentialLink();
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (!link.email ||
|
|
314
|
+
!fbUser.email ||
|
|
315
|
+
link.email.toLowerCase() !== fbUser.email.toLowerCase()) {
|
|
316
|
+
// Link only to the account with the same email: the one-account-per-email
|
|
317
|
+
// rule means that account is the owner of the pending credential. Users
|
|
318
|
+
// with no email (Telegram, phone) never qualify. Leave the link for its
|
|
319
|
+
// owner; it expires on its own.
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
this.clearPendingCredentialLink();
|
|
323
|
+
const credential = credentialOfPendingLink(link);
|
|
324
|
+
if (!credential) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const { user } = await linkWithCredential(fbUser, credential);
|
|
329
|
+
if (this.fbAuth.currentUser?.uid !== user.uid) {
|
|
330
|
+
// Signed out (or switched) while linking: don't resurrect the user.
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const authUser = createSneatAuthUserFromFbUser(user);
|
|
334
|
+
this.authUser$.next(authUser);
|
|
335
|
+
this.authState$.next({ ...this.authState$.value, user: authUser });
|
|
336
|
+
this.analyticsService.logEvent('pendingCredentialLinked', {
|
|
337
|
+
provider: link.providerId,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
this.errorLogger.logError(err, `Failed to connect ${link.providerId} to your account`);
|
|
342
|
+
}
|
|
254
343
|
}
|
|
255
344
|
async linkWith(authProviderID) {
|
|
256
345
|
const authProvider = getAuthProvider(authProviderID);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sneat-auth-state-service.js","sourceRoot":"","sources":["../../../../../../libs/auth/core/src/lib/sneat-auth-state-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EACL,sBAAsB,GAEvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EAEjB,cAAc,EAEd,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,IAAI,EAAc,MAAM,MAAM,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,WAAW,EAAgB,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAEL,OAAO,EACP,oBAAoB,EAIpB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,MAAM,EACN,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AAEvB,yEAAyE;AAEzE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;;AAE5C,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,iDAAiC,CAAA;IACjC,+CAA+B,CAAA;IAC/B,qDAAqC,CAAA;AACvC,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB;AAiCD,MAAM,iBAAiB,GAAG,YAAY,CAAC,cAAc,CAAC;AACtD,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,MAAM,EAAE,iBAAiB;IACzB,YAAY,EAAE,kBAA2B;CAC1C,CAAC;AAEF,4EAA4E;AAC5E,6EAA6E;AAC7E,qDAAqD;AAErD,MAAM,OAAO,qBAAqB;IAoChC,iCAAiC;IAEjC;QArCiB,gBAAW,GAAG,MAAM,CAAe,WAAW,CAAC,CAAC;QAChD,qBAAgB,GAC/B,MAAM,CAAoB,gBAAgB,CAAC,CAAC;QACrC,WAAM,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC7B,qBAAgB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;QAErE,gFAAgF;QAC/D,iBAAY,GAC3B,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,IAAI,OAAO,CAAC;QAErD,OAAE,GAAG,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAE7B,gBAAW,GAAG,IAAI,eAAe,CAChD,iBAAiB,CAClB,CAAC;QACc,eAAU,GAAG,IAAI,CAAC,WAAW;aAC1C,YAAY,EAAE;aACd,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;QAEf,cAAS,GAAG,IAAI,eAAe,CAE9C,SAAS,CAAC,CAAC;QACG,aAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC;QAExC,eAAU,GAAG,IAAI,eAAe,CAC/C,qBAAqB,CACtB,CAAC;QACc,cAAS,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,IAAI;QAC7D,wEAAwE;QACxE,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;QAEM,oBAAe,GAAG,CAAC,CAAC;QAM1B,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC;YAC3B,IAAI,EAAE,CAAC,YAAY,EAAE,EAAE;gBACrB,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC5C,OAAO;gBACT,CAAC;gBACD,MAAM,eAAe,GAAG,EAAE,IAAI,CAAC,eAAe,CAAC;gBAC/C,MAAM,MAAM,GAAe,YAAY;oBACrC,CAAC,CAAC,YAAY,CAAC,aAAa;oBAC5B,CAAC,CAAC,YAAY,CAAC,gBAAgB,CAAC;gBAClC,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,IAAI,CAAC,qBAAqB,EAAE,CAAC;oBAC7B,OAAO;gBACT,CAAC;gBACD,MAAM,QAAQ,GAAG,6BAA6B,CAAC,YAAY,CAAC,CAAC;gBAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5C,IAAI,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG,EAAE,CAAC;oBACxC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;gBACnD,CAAC;gBACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBACnB,GAAG,OAAO;oBACV,MAAM,EAAE,YAAY,CAAC,cAAc;oBACnC,YAAY,EAAE,eAAe;oBAC7B,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;oBACjE,IAAI,EAAE,QAAQ;iBACf,CAAC,CAAC;gBACH,YAAY;qBACT,UAAU,CAAC,KAAK,CAAC;qBACjB,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;oBACd,IACE,eAAe,KAAK,IAAI,CAAC,eAAe;wBACxC,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,YAAY,EACxC,CAAC;wBACD,OAAO;oBACT,CAAC;oBACD,IAAI,CAAC,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC;oBACpC,MAAM,QAAQ,GAAG,6BAA6B,CAAC,YAAY,CAAC,CAAC;oBAC7D,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;oBAC9C,IACE,gBAAgB,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG;wBACvC,gBAAgB,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,EACvD,CAAC;wBACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAChC,CAAC;oBACD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;oBAC5C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;wBACnB,GAAG,OAAO;wBACV,MAAM;wBACN,YAAY,EAAE,OAAO;wBACrB,KAAK;wBACL,IAAI,EAAE,QAAQ;qBACf,CAAC,CAAC;oBACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,6BAA6B;gBAC9D,CAAC,CAAC;qBACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBACb,IACE,eAAe,KAAK,IAAI,CAAC,eAAe;wBACxC,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,YAAY,EACxC,CAAC;wBACD,OAAO;oBACT,CAAC;oBACD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;oBAC5C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;wBACnB,GAAG,OAAO;wBACV,YAAY,EAAE,QAAQ;wBACtB,GAAG,EAAE,+BAA+B,GAAG,EAAE;qBAC1C,CAAC,CAAC;oBACH,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,+BAA+B,CAAC,CAAC;gBAClE,CAAC,CAAC,CAAC;YACP,CAAC;YACD,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;gBACb,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC5C,OAAO;gBACT,CAAC;gBACD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBACnB,GAAG,OAAO;oBACV,YAAY,EAAE,QAAQ;oBACtB,GAAG,EAAE,qCAAqC,GAAG,EAAE;iBAChD,CAAC,CAAC;gBACH,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,mCAAmC,CAAC,CAAC;YACjE,CAAC;YACD,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;YAC7B,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC;YACtB,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE;gBACf,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC5C,OAAO;gBACT,CAAC;gBACD,eAAe;gBACf,8EAA8E;gBAC9E,YAAY;gBACZ,KAAK;gBACL,MAAM,QAAQ,GAAG,6BAA6B,CAAC,MAAM,CAAC,CAAC;gBACvD,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,EAAE,IAAI,CAAC,eAAe,CAAC;oBACvB,IAAI,CAAC,qBAAqB,EAAE,CAAC;oBAC7B,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,YAAY,CAAC,aAAa,CAAC;gBAC1C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBACnB,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK;oBACxB,KAAK,EACH,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,GAAG;wBAC/B,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK;wBAC7B,CAAC,CAAC,IAAI;oBACV,IAAI,EAAE,QAAQ;oBACd,MAAM;oBACN,YAAY,EAAE,OAAO;iBACtB,CAAC,CAAC;gBACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YACD,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;gBACb,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC5C,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,WAAW,CAAC,QAAQ,CACvB,GAAG,EACH,mDAAmD,CACpD,CAAC;gBACF,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBACnB,GAAG,OAAO;oBACV,YAAY,EAAE,QAAQ;oBACtB,GAAG,EAAE,uCAAuC,GAAG,EAAE;iBAClD,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAEO,qBAAqB;QAC3B,iBAAiB,EAAE,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YACnB,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK;YACxB,MAAM,EAAE,YAAY,CAAC,gBAAgB;YACrC,YAAY,EAAE,OAAO;YACrB,KAAK,EAAE,IAAI;YACX,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;IACvD,CAAC;IAEM,OAAO;QACZ,iBAAiB,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC;IAEM,eAAe,CAAC,KAAa;QAClC,OAAO,qBAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IAEM,mBAAmB,CAAC,KAAa;QACtC,OAAO,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACvD,CAAC;IAEM,8BAA8B,CACnC,KAAa,EACb,QAAgB;QAEhB,OAAO,IAAI,CAAC,iBAAiB,CAC3B,8BAA8B,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAC7D,CAAC;IACJ,CAAC;IAEM,0BAA0B,CAC/B,KAAa,EACb,QAAgB;QAEhB,OAAO,IAAI,CAAC,iBAAiB,CAC3B,0BAA0B,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CACzD,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAC7B,UAAmC;QAEnC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,GAAG;YAChB,KAAK,EAAE,MAAM,IAAI,CAAC,UAAU,EAAE;SAC/B,CAAC;IACJ,CAAC;IAIO,KAAK,CAAC,mBAAmB,CAC/B,cAA8B;QAE9B,IAAI,YAAsC,CAAC;QAE3C,MAAM,CAAC,GAA2B,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QAE3D,QAAQ,cAAc,EAAE,CAAC;YACvB,KAAK,YAAY;gBACf,YAAY,GAAG,MAAM,sBAAsB,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;gBAChE,MAAM;YACR,KAAK,WAAW;gBACd,YAAY,GAAG,MAAM,sBAAsB,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;gBAC/D,MAAM;YACR,KAAK,cAAc;gBACjB,YAAY,GAAG,MAAM,sBAAsB,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC;gBAClE,MAAM;YACR,KAAK,eAAe;gBAClB,YAAY,GAAG,MAAM,sBAAsB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;gBACnE,MAAM;YACR;gBACE,OAAO,OAAO,CAAC,MAAM,CAAC,6BAA6B,GAAG,cAAc,CAAC,CAAC;QAC1E,CAAC;QACD,eAAe;QACf,sHAAsH;QACtH,kBAAkB;QAClB,KAAK;QACL,0FAA0F;QAC1F,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAC1D,cAAc,EACd,YAAY,CACb,CAAC;QAEF,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,0BAA0B,CACtC,cAA8B,EAC9B,YAA0B;QAE1B,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,cAAc,CAAC,CAAC;QACxD,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,CAAC;YAC1C,OAAO,EAAE,YAAY,CAAC,UAAU,EAAE,OAAO;YACzC,WAAW,EAAE,YAAY,CAAC,UAAU,EAAE,WAAW;YACjD,QAAQ,EAAE,YAAY,CAAC,UAAU,EAAE,KAAK;SACzC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,cAAc,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAEpE,wDAAwD;QACxD,MAAM,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QAEvC,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,cAA8B;QAE9B,MAAM,YAAY,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;YACrC,0EAA0E;YAC1E,yEAAyE;YACzE,MAAM,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;YACpD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,cAAc,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACxE,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC;IAEM,KAAK,CAAC,QAAQ,CACnB,cAA8B;QAE9B,MAAM,YAAY,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC7B,OAAO,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,cAAc,GAAG,MAAM,aAAa,CACxC,IAAI,CAAC,MAAM,CAAC,WAAW,EACvB,YAAY,CACb,CAAC;QACF,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAAC,cAAsB;QACpD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QAC5C,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;gBAC9D,MAAM,QAAQ,GAAG,6BAA6B,CAAC,WAAW,CAAC,CAAC;gBAC5D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;YAC3B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,OAAO,CAAC,MAAM,CACnB,oBAAoB,cAAc,WAAW,GAAG,KAAK,CACtD,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,OAAO,CAAC,MAAM,CAAC,iCAAiC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,UAAU,CACrB,cAA8B;QAE9B,eAAe;QACf,4IAA4I;QAC5I,KAAK;QACL,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,eAAe,EAAE;YAC9C,QAAQ,EAAE,cAAc;SACzB,CAAC,CAAC;QACH,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,KAAK,CACP,uDAAuD,cAAc,iCAAiC,IAAI,CAAC,eAAe,iBAAiB,CAC5I,CACF,CAAC;YACJ,CAAC;YAED,IAAI,cAAc,GAA+B,SAAS,CAAC;YAE3D,IAAI,SAAS,CAAC,gBAAgB,EAAE,EAAE,CAAC;gBACjC,cAAc,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAAC,CAAC;YAClE,CAAC;iBAAM,CAAC;gBACN,cAAc,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;YAC/D,CAAC;YAED,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YACjC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,cAAc,EAAE;gBAC7C,QAAQ,EAAE,cAAc;aACzB,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YACjC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;8GA5WU,qBAAqB;kHAArB,qBAAqB;;2FAArB,qBAAqB;kBADjC,UAAU;;AA+XX,SAAS,eAAe,CAAC,cAA8B;IACrD,QAAQ,cAAc,EAAE,CAAC;QACvB,KAAK,YAAY;YACf,OAAO,IAAI,kBAAkB,EAAE,CAAC;QAClC,KAAK,WAAW;YACd,OAAO,IAAI,aAAa,CAAC,WAAW,CAAC,CAAC;QACxC,EAAE;QACF,qHAAqH;QACrH,wDAAwD;QACxD,0EAA0E;QAC1E,qCAAqC;QACrC,MAAM;QACN,KAAK,eAAe;YAClB,OAAO,IAAI,aAAa,CAAC,eAAe,CAAC,CAAC;QAC5C,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,oBAAoB,GAAG,IAAI,oBAAoB,EAAE,CAAC;YACxD,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACvC,OAAO,oBAAoB,CAAC;QAC9B,CAAC;QACD,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,kBAAkB,GAAG,IAAI,kBAAkB,EAAE,CAAC;YACpD,kBAAkB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACzC,kBAAkB,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC1C,OAAO,kBAAkB,CAAC;QAC5B,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,cAAc,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED,SAAS,6BAA6B,CACpC,MAAmB;IAEnB,OAAO,CACL,MAAM,IAAI;QACR,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EACR,MAAM,CAAC,YAAY,EAAE,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;YACzD,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,UAAU;YACnC,CAAC,CAAC,MAAM,CAAC,UAAU;QACvB,YAAY,EAAE,MAAM,CAAC,YAAY;KAClC,CACF,CAAC;AACJ,CAAC","sourcesContent":["import { SignInWithOAuthOptions } from '@capacitor-firebase/authentication/dist/esm/definitions';\nimport { Capacitor } from '@capacitor/core';\nimport {\n FirebaseAuthentication,\n SignInResult,\n} from '@capacitor-firebase/authentication';\nimport {\n AnalyticsService,\n clearCurrentSpace,\n EnumAsUnionOfKeys,\n EnvConfigToken,\n IAnalyticsService,\n SNEAT_FIREBASE_AUTH,\n SneatUrlOperationBlocker,\n} from '@sneat/core';\nimport { BehaviorSubject, from, Observable } from 'rxjs';\nimport { Injectable, inject } from '@angular/core';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { distinctUntilChanged, shareReplay } from 'rxjs/operators';\nimport {\n AuthProvider,\n getAuth,\n signInWithCredential,\n User,\n UserCredential,\n UserInfo,\n GoogleAuthProvider,\n OAuthProvider,\n GithubAuthProvider,\n FacebookAuthProvider,\n signInWithEmailLink,\n signInWithCustomToken,\n signInWithPopup,\n signInWithRedirect,\n linkWithPopup,\n unlink,\n createUserWithEmailAndPassword,\n signInWithEmailAndPassword,\n} from 'firebase/auth';\n\n// TODO: fix & remove this eslint hint @nrwl/nx/enforce-module-boundaries\n\nimport { newRandomId } from '@sneat/random';\n\nexport enum AuthStatuses {\n authenticating = 'authenticating',\n authenticated = 'authenticated',\n notAuthenticated = 'notAuthenticated',\n}\n\nexport type AuthStatus = EnumAsUnionOfKeys<typeof AuthStatuses>;\n\n/**\n * A user-visible, evidence-based session progress phase. The UI maps these to\n * friendly copy (for example “Checking your sign-in”), never SDK terminology.\n */\nexport type SneatAuthLoadingPhase =\n | 'checking-session'\n | 'getting-token'\n | 'ready'\n | 'failed';\n\nexport interface ISneatAuthUser extends UserInfo {\n readonly isAnonymous: boolean;\n readonly emailVerified: boolean;\n readonly providerData: readonly UserInfo[];\n}\n\nexport interface ISneatAuthState {\n readonly status: AuthStatus;\n readonly loadingPhase?: SneatAuthLoadingPhase;\n readonly token?: string | null;\n readonly user?: ISneatAuthUser | null;\n readonly err?: unknown;\n}\n\nexport interface ISneatEmailAuthResult {\n readonly userID: string;\n readonly token: string;\n}\n\nconst initialAuthStatus = AuthStatuses.authenticating;\nexport const initialSneatAuthState = {\n status: initialAuthStatus,\n loadingPhase: 'checking-session' as const,\n};\n\n// The host must provide this from its authenticated route/session boundary.\n// Keeping it out of the root injector is what allows public shells to render\n// before Firebase Auth and Firestore are downloaded.\n@Injectable()\nexport class SneatAuthStateService {\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly analyticsService =\n inject<IAnalyticsService>(AnalyticsService);\n readonly fbAuth = inject(SNEAT_FIREBASE_AUTH);\n private readonly operationBlocker = inject(SneatUrlOperationBlocker);\n\n // Web OAuth sign-in strategy from the app's environment config (default popup).\n private readonly signInMethod =\n inject(EnvConfigToken, { optional: true })?.signInMethod ?? 'popup';\n\n private readonly id = newRandomId({ len: 5 });\n\n private readonly authStatus$ = new BehaviorSubject<AuthStatus>(\n initialAuthStatus,\n );\n public readonly authStatus = this.authStatus$\n .asObservable()\n .pipe(distinctUntilChanged());\n\n private readonly authUser$ = new BehaviorSubject<\n ISneatAuthUser | null | undefined\n >(undefined);\n public readonly authUser = this.authUser$.asObservable();\n\n private readonly authState$ = new BehaviorSubject<ISneatAuthState>(\n initialSneatAuthState,\n );\n public readonly authState = this.authState$.asObservable().pipe(\n // tap(v => console.log('SneatAuthStateService => SneatAuthState:', v)),\n shareReplay(1),\n );\n\n private tokenGeneration = 0;\n private tokenUserID?: string;\n\n // private readonly fbAuth: Auth;\n\n constructor() {\n const errorLogger = this.errorLogger;\n this.fbAuth.onIdTokenChanged({\n next: (firebaseUser) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n const tokenGeneration = ++this.tokenGeneration;\n const status: AuthStatus = firebaseUser\n ? AuthStatuses.authenticated\n : AuthStatuses.notAuthenticated;\n if (!firebaseUser) {\n this.publishSignedOutState();\n return;\n }\n const authUser = createSneatAuthUserFromFbUser(firebaseUser);\n const current = this.authState$.value || {};\n if (current.user?.uid !== authUser?.uid) {\n this.analyticsService.identify(firebaseUser.uid);\n }\n this.authState$.next({\n ...current,\n status: AuthStatuses.authenticating,\n loadingPhase: 'getting-token',\n token: current.user?.uid === authUser?.uid ? current.token : null,\n user: authUser,\n });\n firebaseUser\n .getIdToken(false)\n .then((token) => {\n if (\n tokenGeneration !== this.tokenGeneration ||\n this.fbAuth.currentUser !== firebaseUser\n ) {\n return;\n }\n this.tokenUserID = firebaseUser.uid;\n const authUser = createSneatAuthUserFromFbUser(firebaseUser);\n const previousAuthUser = this.authUser$.value;\n if (\n previousAuthUser?.uid !== authUser?.uid ||\n previousAuthUser?.isAnonymous !== authUser?.isAnonymous\n ) {\n this.authUser$.next(authUser);\n }\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n status,\n loadingPhase: 'ready',\n token,\n user: authUser,\n });\n this.authStatus$.next(status); // Should be after authState$\n })\n .catch((err) => {\n if (\n tokenGeneration !== this.tokenGeneration ||\n this.fbAuth.currentUser !== firebaseUser\n ) {\n return;\n }\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n loadingPhase: 'failed',\n err: `fbUser.getIdToken() failed: ${err}`,\n });\n this.errorLogger.logError(err, 'Failed in fbUser.getIdToken()');\n });\n },\n error: (err) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n loadingPhase: 'failed',\n err: `fbAuth.onIdTokenChanged() failed: ${err}`,\n });\n errorLogger.logError(err, 'failed in fbAuth.onIdTokenChanged');\n },\n complete: () => void 0,\n });\n this.fbAuth.onAuthStateChanged({\n complete: () => void 0,\n next: (fbUser) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n // console.log(\n // `SneatAuthStateService => authStatus: ${this.authStatus$.value}; fbUser`,\n // fbUser,\n // );\n const authUser = createSneatAuthUserFromFbUser(fbUser);\n if (!authUser) {\n ++this.tokenGeneration;\n this.publishSignedOutState();\n return;\n }\n const status = AuthStatuses.authenticated;\n this.authUser$.next(authUser);\n this.authState$.next({\n ...this.authState$.value,\n token:\n this.tokenUserID === authUser.uid\n ? this.authState$.value.token\n : null,\n user: authUser,\n status,\n loadingPhase: 'ready',\n });\n this.authStatus$.next(status);\n },\n error: (err) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n this.errorLogger.logError(\n err,\n 'failed to retrieve Firebase auth user information',\n );\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n loadingPhase: 'failed',\n err: `fbAuth.onAuthStateChanged() failed: ${err}`,\n });\n },\n });\n }\n\n private publishSignedOutState(): void {\n clearCurrentSpace();\n this.tokenUserID = undefined;\n this.authUser$.next(null);\n this.authState$.next({\n ...this.authState$.value,\n status: AuthStatuses.notAuthenticated,\n loadingPhase: 'ready',\n token: null,\n user: null,\n });\n this.authStatus$.next(AuthStatuses.notAuthenticated);\n }\n\n public signOut(): Promise<void> {\n clearCurrentSpace();\n return this.fbAuth.signOut();\n }\n\n public signInWithToken(token: string): Promise<UserCredential> {\n return signInWithCustomToken(this.fbAuth, token);\n }\n\n public signInWithEmailLink(email: string): Observable<UserCredential> {\n return from(signInWithEmailLink(this.fbAuth, email));\n }\n\n public createUserWithEmailAndPassword(\n email: string,\n password: string,\n ): Promise<ISneatEmailAuthResult> {\n return this.toEmailAuthResult(\n createUserWithEmailAndPassword(this.fbAuth, email, password),\n );\n }\n\n public signInWithEmailAndPassword(\n email: string,\n password: string,\n ): Promise<ISneatEmailAuthResult> {\n return this.toEmailAuthResult(\n signInWithEmailAndPassword(this.fbAuth, email, password),\n );\n }\n\n private async toEmailAuthResult(\n credential: Promise<UserCredential>,\n ): Promise<ISneatEmailAuthResult> {\n const { user } = await credential;\n return {\n userID: user.uid,\n token: await user.getIdToken(),\n };\n }\n\n private isSigningInWith?: AuthProviderName;\n\n private async signInOnNativeLayer(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential> {\n let signInResult: SignInResult | undefined;\n\n const o: SignInWithOAuthOptions = { skipNativeAuth: true };\n\n switch (authProviderID) {\n case 'google.com':\n signInResult = await FirebaseAuthentication.signInWithGoogle(o);\n break;\n case 'apple.com':\n signInResult = await FirebaseAuthentication.signInWithApple(o);\n break;\n case 'facebook.com':\n signInResult = await FirebaseAuthentication.signInWithFacebook(o);\n break;\n case 'microsoft.com':\n signInResult = await FirebaseAuthentication.signInWithMicrosoft(o);\n break;\n default:\n return Promise.reject('unsupported auth provider: ' + authProviderID);\n }\n // console.log(\n // `SneatAuthStateService.signInWith(${authProviderID}) => signed in on native layer, authenticating in webview...`,\n // signInResult,\n // );\n // we need to authenticate on webview layer using the id token and nonce from signInResult\n const userCredential = await this.authenticateOnWebviewLayer(\n authProviderID,\n signInResult,\n );\n\n return Promise.resolve(userCredential);\n }\n\n private async authenticateOnWebviewLayer(\n authProviderID: AuthProviderID,\n signInResult: SignInResult,\n ): Promise<UserCredential> {\n const oauthProvider = new OAuthProvider(authProviderID);\n const credential = oauthProvider.credential({\n idToken: signInResult.credential?.idToken,\n accessToken: signInResult.credential?.accessToken,\n rawNonce: signInResult.credential?.nonce,\n });\n const auth = getAuth();\n const userCredential = await signInWithCredential(auth, credential);\n\n // Get a valid Firebase ID token that has a 'kid' header\n await userCredential.user.getIdToken();\n\n return Promise.resolve(userCredential);\n }\n\n private async signInWithWebSDK(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential | undefined> {\n const authProvider = getAuthProvider(authProviderID);\n if (this.signInMethod === 'redirect') {\n // Full-page redirect; this navigates away and never resolves. The sign-in\n // is completed on return by BaseAppComponent's getRedirectResult() call.\n await signInWithRedirect(this.fbAuth, authProvider);\n return undefined;\n }\n const userCredential = await signInWithPopup(this.fbAuth, authProvider);\n return Promise.resolve(userCredential);\n }\n\n public async linkWith(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential | undefined> {\n const authProvider = getAuthProvider(authProviderID);\n if (!this.fbAuth.currentUser) {\n return Promise.reject('no current user');\n }\n const userCredential = await linkWithPopup(\n this.fbAuth.currentUser,\n authProvider,\n );\n return Promise.resolve(userCredential);\n }\n\n public async unlinkAuthProvider(authProviderID: string): Promise<void> {\n const currentUser = this.fbAuth.currentUser;\n if (currentUser) {\n try {\n const updatedUser = await unlink(currentUser, authProviderID);\n const authUser = createSneatAuthUserFromFbUser(updatedUser);\n this.authUser$.next(authUser);\n return Promise.resolve();\n } catch (error) {\n return Promise.reject(\n `Failed to unlink ${authProviderID} account:` + error,\n );\n }\n } else {\n return Promise.reject('No user is currently signed in.');\n }\n }\n\n public async signInWith(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential | undefined> {\n // console.log(\n // `SneatAuthStateService.signInWith(${authProviderID}), isSigningInWith=${this.isSigningInWith}, location.protocol=${location.protocol}`,\n // );\n this.analyticsService.logEvent('signingInWith', {\n provider: authProviderID,\n });\n try {\n if (this.isSigningInWith) {\n return Promise.reject(\n new Error(\n `a repeated call to SneatAuthStateService.signInWith(${authProviderID}) white previous sign in with ${this.isSigningInWith} is in progress`,\n ),\n );\n }\n\n let userCredential: UserCredential | undefined = undefined;\n\n if (Capacitor.isNativePlatform()) {\n userCredential = await this.signInOnNativeLayer(authProviderID);\n } else {\n userCredential = await this.signInWithWebSDK(authProviderID);\n }\n\n this.isSigningInWith = undefined;\n this.analyticsService.logEvent('signedInWith', {\n provider: authProviderID,\n });\n return Promise.resolve(userCredential);\n } catch (e) {\n this.isSigningInWith = undefined;\n return Promise.reject(e);\n }\n }\n}\n\nexport type AuthProviderID =\n | 'apple.com'\n | 'google.com'\n | 'microsoft.com'\n | 'facebook.com'\n | 'github.com'\n | 'phone';\n\nexport type AuthProviderName =\n | 'Google'\n | 'Apple'\n | 'Microsoft'\n | 'Facebook'\n | 'GitHub';\n\nfunction getAuthProvider(authProviderID: AuthProviderID): AuthProvider {\n switch (authProviderID) {\n case 'google.com':\n return new GoogleAuthProvider();\n case 'apple.com':\n return new OAuthProvider('apple.com');\n //\n // https://developer.apple.com/documentation/sign_in_with_apple/incorporating-sign-in-with-apple-into-other-platforms\n // (authProvider as OAuthProvider).setCustomParameters({\n // \t// \t// Localize the Apple authentication screen in current app locale.\n // \tlocale: 'en', // TODO: set locale\n // });\n case 'microsoft.com':\n return new OAuthProvider('microsoft.com');\n case 'facebook.com': {\n const facebookAuthProvider = new FacebookAuthProvider();\n facebookAuthProvider.addScope('email');\n return facebookAuthProvider;\n }\n case 'github.com': {\n const githubAuthProvider = new GithubAuthProvider();\n githubAuthProvider.addScope('read:user');\n githubAuthProvider.addScope('user:email');\n return githubAuthProvider;\n }\n default:\n throw new Error('unsupported auth provider: ' + authProviderID);\n }\n}\n\nfunction createSneatAuthUserFromFbUser(\n fbUser: User | null,\n): ISneatAuthUser | null {\n return (\n fbUser && {\n isAnonymous: fbUser.isAnonymous,\n emailVerified: fbUser.emailVerified,\n email: fbUser.email,\n uid: fbUser.uid,\n displayName: fbUser.displayName,\n phoneNumber: fbUser.phoneNumber,\n photoURL: fbUser.photoURL,\n providerId:\n fbUser.providerData?.length === 1 && fbUser.providerData[0]\n ? fbUser.providerData[0].providerId\n : fbUser.providerId,\n providerData: fbUser.providerData,\n }\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sneat-auth-state-service.js","sourceRoot":"","sources":["../../../../../../libs/auth/core/src/lib/sneat-auth-state-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { SignInWithOAuthOptions } from '@capacitor-firebase/authentication/dist/esm/definitions';\nimport { Capacitor } from '@capacitor/core';\nimport {\n FirebaseAuthentication,\n SignInResult,\n} from '@capacitor-firebase/authentication';\nimport {\n AnalyticsService,\n clearCurrentSpace,\n EnumAsUnionOfKeys,\n EnvConfigToken,\n IAnalyticsService,\n SNEAT_FIREBASE_AUTH,\n SneatUrlOperationBlocker,\n} from '@sneat/core';\nimport { BehaviorSubject, from, Observable } from 'rxjs';\nimport { Injectable, inject } from '@angular/core';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { distinctUntilChanged, shareReplay } from 'rxjs/operators';\nimport {\n AuthProvider,\n getAuth,\n signInWithCredential,\n User,\n UserCredential,\n UserInfo,\n GoogleAuthProvider,\n OAuthProvider,\n GithubAuthProvider,\n FacebookAuthProvider,\n signInWithEmailLink,\n signInWithCustomToken,\n signInWithPopup,\n signInWithRedirect,\n linkWithPopup,\n linkWithCredential,\n unlink,\n createUserWithEmailAndPassword,\n signInWithEmailAndPassword,\n} from 'firebase/auth';\n\n// TODO: fix & remove this eslint hint @nrwl/nx/enforce-module-boundaries\n\nimport { newRandomId } from '@sneat/random';\nimport {\n IPendingCredentialLink,\n clearPendingCredentialLink,\n credentialOfPendingLink,\n loadPendingCredentialLink,\n pendingCredentialLinkFromError,\n savePendingCredentialLink,\n} from './pending-credential-link';\n\nexport enum AuthStatuses {\n authenticating = 'authenticating',\n authenticated = 'authenticated',\n notAuthenticated = 'notAuthenticated',\n}\n\nexport type AuthStatus = EnumAsUnionOfKeys<typeof AuthStatuses>;\n\n/**\n * A user-visible, evidence-based session progress phase. The UI maps these to\n * friendly copy (for example “Checking your sign-in”), never SDK terminology.\n */\nexport type SneatAuthLoadingPhase =\n | 'checking-session'\n | 'getting-token'\n | 'ready'\n | 'failed';\n\nexport interface ISneatAuthUser extends UserInfo {\n readonly isAnonymous: boolean;\n readonly emailVerified: boolean;\n readonly providerData: readonly UserInfo[];\n}\n\nexport interface ISneatAuthState {\n readonly status: AuthStatus;\n readonly loadingPhase?: SneatAuthLoadingPhase;\n readonly token?: string | null;\n readonly user?: ISneatAuthUser | null;\n readonly err?: unknown;\n}\n\nexport interface ISneatEmailAuthResult {\n readonly userID: string;\n readonly token: string;\n}\n\nconst initialAuthStatus = AuthStatuses.authenticating;\nexport const initialSneatAuthState = {\n status: initialAuthStatus,\n loadingPhase: 'checking-session' as const,\n};\n\n// The host must provide this from its authenticated route/session boundary.\n// Keeping it out of the root injector is what allows public shells to render\n// before Firebase Auth and Firestore are downloaded.\n@Injectable()\nexport class SneatAuthStateService {\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly analyticsService =\n inject<IAnalyticsService>(AnalyticsService);\n readonly fbAuth = inject(SNEAT_FIREBASE_AUTH);\n private readonly operationBlocker = inject(SneatUrlOperationBlocker);\n\n // Web OAuth sign-in strategy from the app's environment config (default popup).\n private readonly signInMethod =\n inject(EnvConfigToken, { optional: true })?.signInMethod ?? 'popup';\n\n private readonly id = newRandomId({ len: 5 });\n\n private readonly authStatus$ = new BehaviorSubject<AuthStatus>(\n initialAuthStatus,\n );\n public readonly authStatus = this.authStatus$\n .asObservable()\n .pipe(distinctUntilChanged());\n\n private readonly authUser$ = new BehaviorSubject<\n ISneatAuthUser | null | undefined\n >(undefined);\n public readonly authUser = this.authUser$.asObservable();\n\n private readonly authState$ = new BehaviorSubject<ISneatAuthState>(\n initialSneatAuthState,\n );\n public readonly authState = this.authState$.asObservable().pipe(\n // tap(v => console.log('SneatAuthStateService => SneatAuthState:', v)),\n shareReplay(1),\n );\n\n private tokenGeneration = 0;\n private tokenUserID?: string;\n\n // private readonly fbAuth: Auth;\n\n constructor() {\n const errorLogger = this.errorLogger;\n this.fbAuth.onIdTokenChanged({\n next: (firebaseUser) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n const tokenGeneration = ++this.tokenGeneration;\n const status: AuthStatus = firebaseUser\n ? AuthStatuses.authenticated\n : AuthStatuses.notAuthenticated;\n if (!firebaseUser) {\n this.publishSignedOutState();\n return;\n }\n const authUser = createSneatAuthUserFromFbUser(firebaseUser);\n const current = this.authState$.value || {};\n if (current.user?.uid !== authUser?.uid) {\n this.analyticsService.identify(firebaseUser.uid);\n }\n this.authState$.next({\n ...current,\n status: AuthStatuses.authenticating,\n loadingPhase: 'getting-token',\n token: current.user?.uid === authUser?.uid ? current.token : null,\n user: authUser,\n });\n firebaseUser\n .getIdToken(false)\n .then((token) => {\n if (\n tokenGeneration !== this.tokenGeneration ||\n this.fbAuth.currentUser !== firebaseUser\n ) {\n return;\n }\n this.tokenUserID = firebaseUser.uid;\n const authUser = createSneatAuthUserFromFbUser(firebaseUser);\n const previousAuthUser = this.authUser$.value;\n if (\n previousAuthUser?.uid !== authUser?.uid ||\n previousAuthUser?.isAnonymous !== authUser?.isAnonymous\n ) {\n this.authUser$.next(authUser);\n }\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n status,\n loadingPhase: 'ready',\n token,\n user: authUser,\n });\n this.authStatus$.next(status); // Should be after authState$\n })\n .catch((err) => {\n if (\n tokenGeneration !== this.tokenGeneration ||\n this.fbAuth.currentUser !== firebaseUser\n ) {\n return;\n }\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n loadingPhase: 'failed',\n err: `fbUser.getIdToken() failed: ${err}`,\n });\n this.errorLogger.logError(err, 'Failed in fbUser.getIdToken()');\n });\n },\n error: (err) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n loadingPhase: 'failed',\n err: `fbAuth.onIdTokenChanged() failed: ${err}`,\n });\n errorLogger.logError(err, 'failed in fbAuth.onIdTokenChanged');\n },\n complete: () => void 0,\n });\n this.fbAuth.onAuthStateChanged({\n complete: () => void 0,\n next: (fbUser) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n // console.log(\n // `SneatAuthStateService => authStatus: ${this.authStatus$.value}; fbUser`,\n // fbUser,\n // );\n const authUser = createSneatAuthUserFromFbUser(fbUser);\n if (!authUser) {\n ++this.tokenGeneration;\n this.publishSignedOutState();\n return;\n }\n const status = AuthStatuses.authenticated;\n if (fbUser && !fbUser.isAnonymous) {\n void this.linkPendingCredential(fbUser);\n }\n this.authUser$.next(authUser);\n this.authState$.next({\n ...this.authState$.value,\n token:\n this.tokenUserID === authUser.uid\n ? this.authState$.value.token\n : null,\n user: authUser,\n status,\n loadingPhase: 'ready',\n });\n this.authStatus$.next(status);\n },\n error: (err) => {\n if (this.operationBlocker.isBlocked('auth')) {\n return;\n }\n this.errorLogger.logError(\n err,\n 'failed to retrieve Firebase auth user information',\n );\n const current = this.authState$.value || {};\n this.authState$.next({\n ...current,\n loadingPhase: 'failed',\n err: `fbAuth.onAuthStateChanged() failed: ${err}`,\n });\n },\n });\n }\n\n private publishSignedOutState(): void {\n clearCurrentSpace();\n this.tokenUserID = undefined;\n this.authUser$.next(null);\n this.authState$.next({\n ...this.authState$.value,\n status: AuthStatuses.notAuthenticated,\n loadingPhase: 'ready',\n token: null,\n user: null,\n });\n this.authStatus$.next(AuthStatuses.notAuthenticated);\n }\n\n public signOut(): Promise<void> {\n clearCurrentSpace();\n this.clearPendingCredentialLink();\n return this.fbAuth.signOut();\n }\n\n public signInWithToken(token: string): Promise<UserCredential> {\n return signInWithCustomToken(this.fbAuth, token);\n }\n\n public signInWithEmailLink(email: string): Observable<UserCredential> {\n return from(signInWithEmailLink(this.fbAuth, email));\n }\n\n public createUserWithEmailAndPassword(\n email: string,\n password: string,\n ): Promise<ISneatEmailAuthResult> {\n return this.toEmailAuthResult(\n createUserWithEmailAndPassword(this.fbAuth, email, password),\n );\n }\n\n public signInWithEmailAndPassword(\n email: string,\n password: string,\n ): Promise<ISneatEmailAuthResult> {\n return this.toEmailAuthResult(\n signInWithEmailAndPassword(this.fbAuth, email, password),\n );\n }\n\n private async toEmailAuthResult(\n credential: Promise<UserCredential>,\n ): Promise<ISneatEmailAuthResult> {\n const { user } = await credential;\n return {\n userID: user.uid,\n token: await user.getIdToken(),\n };\n }\n\n private isSigningInWith?: AuthProviderName;\n\n private async signInOnNativeLayer(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential> {\n let signInResult: SignInResult | undefined;\n\n const o: SignInWithOAuthOptions = { skipNativeAuth: true };\n\n switch (authProviderID) {\n case 'google.com':\n signInResult = await FirebaseAuthentication.signInWithGoogle(o);\n break;\n case 'apple.com':\n signInResult = await FirebaseAuthentication.signInWithApple(o);\n break;\n case 'facebook.com':\n signInResult = await FirebaseAuthentication.signInWithFacebook(o);\n break;\n case 'microsoft.com':\n signInResult = await FirebaseAuthentication.signInWithMicrosoft(o);\n break;\n default:\n return Promise.reject('unsupported auth provider: ' + authProviderID);\n }\n // console.log(\n // `SneatAuthStateService.signInWith(${authProviderID}) => signed in on native layer, authenticating in webview...`,\n // signInResult,\n // );\n // we need to authenticate on webview layer using the id token and nonce from signInResult\n const userCredential = await this.authenticateOnWebviewLayer(\n authProviderID,\n signInResult,\n );\n\n return Promise.resolve(userCredential);\n }\n\n private async authenticateOnWebviewLayer(\n authProviderID: AuthProviderID,\n signInResult: SignInResult,\n ): Promise<UserCredential> {\n const oauthProvider = new OAuthProvider(authProviderID);\n const credential = oauthProvider.credential({\n idToken: signInResult.credential?.idToken,\n accessToken: signInResult.credential?.accessToken,\n rawNonce: signInResult.credential?.nonce,\n });\n const auth = getAuth();\n const userCredential = await signInWithCredential(auth, credential);\n\n // Get a valid Firebase ID token that has a 'kid' header\n await userCredential.user.getIdToken();\n\n return Promise.resolve(userCredential);\n }\n\n private async signInWithWebSDK(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential | undefined> {\n const authProvider = getAuthProvider(authProviderID);\n if (this.signInMethod === 'redirect') {\n // Full-page redirect; this navigates away and never resolves. The sign-in\n // is completed on return by BaseAppComponent's getRedirectResult() call.\n await signInWithRedirect(this.fbAuth, authProvider);\n return undefined;\n }\n try {\n return await signInWithPopup(this.fbAuth, authProvider);\n } catch (err) {\n this.rememberPendingCredentialLink(err);\n throw err;\n }\n }\n\n /**\n * Keeps the credential from an `account-exists-with-different-credential`\n * error, so it is linked to the existing account as soon as the user signs\n * in the way they did before. Returns the pending link, or undefined for any\n * other error. Call it for redirect sign-in errors too.\n */\n public rememberPendingCredentialLink(\n err: unknown,\n ): IPendingCredentialLink | undefined {\n const link = pendingCredentialLinkFromError(err);\n if (link) {\n savePendingCredentialLink(link);\n this.pendingCredentialLink$.next(link);\n this.analyticsService.logEvent('pendingCredentialLinkSaved', {\n provider: link.providerId,\n });\n }\n return link;\n }\n\n /** The provider waiting to be linked on next sign-in, if any. */\n public get pendingCredentialLink(): IPendingCredentialLink | undefined {\n return loadPendingCredentialLink();\n }\n\n private readonly pendingCredentialLink$ = new BehaviorSubject<\n IPendingCredentialLink | undefined\n >(\n // Reading also purges an expired entry left from an earlier visit, so a\n // stale access token does not linger in storage.\n loadPendingCredentialLink(),\n );\n\n /**\n * The pending link as it changes — a redirect sign-in error is read after\n * the login page has rendered, so the page subscribes rather than reading\n * once.\n */\n public readonly pendingCredentialLinkChanges =\n this.pendingCredentialLink$.asObservable();\n\n private clearPendingCredentialLink(): void {\n clearPendingCredentialLink();\n if (this.pendingCredentialLink$.value) {\n this.pendingCredentialLink$.next(undefined);\n }\n }\n\n private async linkPendingCredential(fbUser: User): Promise<void> {\n const link = loadPendingCredentialLink();\n if (!link) {\n // Gone or expired: stop announcing it.\n this.clearPendingCredentialLink();\n return;\n }\n if (fbUser.providerData.some((p) => p.providerId === link.providerId)) {\n this.clearPendingCredentialLink();\n return;\n }\n if (\n !link.email ||\n !fbUser.email ||\n link.email.toLowerCase() !== fbUser.email.toLowerCase()\n ) {\n // Link only to the account with the same email: the one-account-per-email\n // rule means that account is the owner of the pending credential. Users\n // with no email (Telegram, phone) never qualify. Leave the link for its\n // owner; it expires on its own.\n return;\n }\n this.clearPendingCredentialLink();\n const credential = credentialOfPendingLink(link);\n if (!credential) {\n return;\n }\n try {\n const { user } = await linkWithCredential(fbUser, credential);\n if (this.fbAuth.currentUser?.uid !== user.uid) {\n // Signed out (or switched) while linking: don't resurrect the user.\n return;\n }\n const authUser = createSneatAuthUserFromFbUser(user);\n this.authUser$.next(authUser);\n this.authState$.next({ ...this.authState$.value, user: authUser });\n this.analyticsService.logEvent('pendingCredentialLinked', {\n provider: link.providerId,\n });\n } catch (err) {\n this.errorLogger.logError(\n err,\n `Failed to connect ${link.providerId} to your account`,\n );\n }\n }\n\n public async linkWith(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential | undefined> {\n const authProvider = getAuthProvider(authProviderID);\n if (!this.fbAuth.currentUser) {\n return Promise.reject('no current user');\n }\n const userCredential = await linkWithPopup(\n this.fbAuth.currentUser,\n authProvider,\n );\n return Promise.resolve(userCredential);\n }\n\n public async unlinkAuthProvider(authProviderID: string): Promise<void> {\n const currentUser = this.fbAuth.currentUser;\n if (currentUser) {\n try {\n const updatedUser = await unlink(currentUser, authProviderID);\n const authUser = createSneatAuthUserFromFbUser(updatedUser);\n this.authUser$.next(authUser);\n return Promise.resolve();\n } catch (error) {\n return Promise.reject(\n `Failed to unlink ${authProviderID} account:` + error,\n );\n }\n } else {\n return Promise.reject('No user is currently signed in.');\n }\n }\n\n public async signInWith(\n authProviderID: AuthProviderID,\n ): Promise<UserCredential | undefined> {\n // console.log(\n // `SneatAuthStateService.signInWith(${authProviderID}), isSigningInWith=${this.isSigningInWith}, location.protocol=${location.protocol}`,\n // );\n this.analyticsService.logEvent('signingInWith', {\n provider: authProviderID,\n });\n try {\n if (this.isSigningInWith) {\n return Promise.reject(\n new Error(\n `a repeated call to SneatAuthStateService.signInWith(${authProviderID}) white previous sign in with ${this.isSigningInWith} is in progress`,\n ),\n );\n }\n\n let userCredential: UserCredential | undefined = undefined;\n\n if (Capacitor.isNativePlatform()) {\n userCredential = await this.signInOnNativeLayer(authProviderID);\n } else {\n userCredential = await this.signInWithWebSDK(authProviderID);\n }\n\n this.isSigningInWith = undefined;\n this.analyticsService.logEvent('signedInWith', {\n provider: authProviderID,\n });\n return Promise.resolve(userCredential);\n } catch (e) {\n this.isSigningInWith = undefined;\n return Promise.reject(e);\n }\n }\n}\n\nexport type AuthProviderID =\n | 'apple.com'\n | 'google.com'\n | 'microsoft.com'\n | 'facebook.com'\n | 'github.com'\n | 'phone';\n\nexport type AuthProviderName =\n 'Google' | 'Apple' | 'Microsoft' | 'Facebook' | 'GitHub';\n\nfunction getAuthProvider(authProviderID: AuthProviderID): AuthProvider {\n switch (authProviderID) {\n case 'google.com':\n return new GoogleAuthProvider();\n case 'apple.com':\n return new OAuthProvider('apple.com');\n //\n // https://developer.apple.com/documentation/sign_in_with_apple/incorporating-sign-in-with-apple-into-other-platforms\n // (authProvider as OAuthProvider).setCustomParameters({\n // \t// \t// Localize the Apple authentication screen in current app locale.\n // \tlocale: 'en', // TODO: set locale\n // });\n case 'microsoft.com':\n return new OAuthProvider('microsoft.com');\n case 'facebook.com': {\n const facebookAuthProvider = new FacebookAuthProvider();\n facebookAuthProvider.addScope('email');\n return facebookAuthProvider;\n }\n case 'github.com': {\n const githubAuthProvider = new GithubAuthProvider();\n githubAuthProvider.addScope('read:user');\n githubAuthProvider.addScope('user:email');\n return githubAuthProvider;\n }\n default:\n throw new Error('unsupported auth provider: ' + authProviderID);\n }\n}\n\nfunction createSneatAuthUserFromFbUser(\n fbUser: User | null,\n): ISneatAuthUser | null {\n return (\n fbUser && {\n isAnonymous: fbUser.isAnonymous,\n emailVerified: fbUser.emailVerified,\n email: fbUser.email,\n uid: fbUser.uid,\n displayName: fbUser.displayName,\n phoneNumber: fbUser.phoneNumber,\n photoURL: fbUser.photoURL,\n providerId:\n fbUser.providerData?.length === 1 && fbUser.providerData[0]\n ? fbUser.providerData[0].providerId\n : fbUser.providerId,\n providerData: fbUser.providerData,\n }\n );\n}\n"]}
|
package/index.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export * from './lib/sneat-auth.interface';
|
|
|
4
4
|
export * from './lib/sneat-auth-guard';
|
|
5
5
|
export * from './lib/redirect-to-login.guard';
|
|
6
6
|
export * from './lib/sneat-auth-state-service';
|
|
7
|
+
export * from './lib/pending-credential-link';
|
|
7
8
|
export * from './lib/telegram-auth.service';
|
|
8
9
|
export * from './lib/user';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { OAuthCredential } from 'firebase/auth';
|
|
2
|
+
/**
|
|
3
|
+
* Firebase's error for "this email already has an account that signs in a
|
|
4
|
+
* different way". The Sneat identity project allows one account per email, so
|
|
5
|
+
* signing in with GitHub when the account was created with Google (or an
|
|
6
|
+
* email link) fails with it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL = "auth/account-exists-with-different-credential";
|
|
9
|
+
/** Where the credential waits for the user to sign in the usual way. */
|
|
10
|
+
export declare const PENDING_CREDENTIAL_LINK_STORAGE_KEY = "sneat:auth:pending-credential-link";
|
|
11
|
+
/**
|
|
12
|
+
* How long a pending credential is kept. Long enough to finish an email-link
|
|
13
|
+
* sign-in in another tab; short enough that a forgotten one does not link on
|
|
14
|
+
* some unrelated later sign-in.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PENDING_CREDENTIAL_LINK_TTL_MS: number;
|
|
17
|
+
/** A provider credential to attach to the account once the user signs in. */
|
|
18
|
+
export interface IPendingCredentialLink {
|
|
19
|
+
readonly providerId: string;
|
|
20
|
+
readonly email?: string;
|
|
21
|
+
readonly createdAt: number;
|
|
22
|
+
/** `OAuthCredential.toJSON()` — restored with `OAuthCredential.fromJSON()`. */
|
|
23
|
+
readonly credential: object;
|
|
24
|
+
}
|
|
25
|
+
export declare function isAccountExistsWithDifferentCredential(err: unknown): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Reads the credential Firebase hands back with an
|
|
28
|
+
* `account-exists-with-different-credential` error, or undefined for any other
|
|
29
|
+
* error (or when Firebase gave no usable credential).
|
|
30
|
+
*/
|
|
31
|
+
export declare function pendingCredentialLinkFromError(err: unknown, now?: number): IPendingCredentialLink | undefined;
|
|
32
|
+
export declare function savePendingCredentialLink(link: IPendingCredentialLink): void;
|
|
33
|
+
export declare function clearPendingCredentialLink(): void;
|
|
34
|
+
/** The stored pending link, or undefined when there is none or it expired. */
|
|
35
|
+
export declare function loadPendingCredentialLink(now?: number): IPendingCredentialLink | undefined;
|
|
36
|
+
/** Restores the Firebase credential saved in a pending link. */
|
|
37
|
+
export declare function credentialOfPendingLink(link: IPendingCredentialLink): OAuthCredential | undefined;
|
|
38
|
+
/** A human name for a provider ID, for messages. */
|
|
39
|
+
export declare function authProviderTitle(providerId: string): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EnumAsUnionOfKeys } from '@sneat/core';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { UserCredential, UserInfo } from 'firebase/auth';
|
|
4
|
+
import { IPendingCredentialLink } from './pending-credential-link';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
5
6
|
export declare enum AuthStatuses {
|
|
6
7
|
authenticating = "authenticating",
|
|
@@ -60,6 +61,24 @@ export declare class SneatAuthStateService {
|
|
|
60
61
|
private signInOnNativeLayer;
|
|
61
62
|
private authenticateOnWebviewLayer;
|
|
62
63
|
private signInWithWebSDK;
|
|
64
|
+
/**
|
|
65
|
+
* Keeps the credential from an `account-exists-with-different-credential`
|
|
66
|
+
* error, so it is linked to the existing account as soon as the user signs
|
|
67
|
+
* in the way they did before. Returns the pending link, or undefined for any
|
|
68
|
+
* other error. Call it for redirect sign-in errors too.
|
|
69
|
+
*/
|
|
70
|
+
rememberPendingCredentialLink(err: unknown): IPendingCredentialLink | undefined;
|
|
71
|
+
/** The provider waiting to be linked on next sign-in, if any. */
|
|
72
|
+
get pendingCredentialLink(): IPendingCredentialLink | undefined;
|
|
73
|
+
private readonly pendingCredentialLink$;
|
|
74
|
+
/**
|
|
75
|
+
* The pending link as it changes — a redirect sign-in error is read after
|
|
76
|
+
* the login page has rendered, so the page subscribes rather than reading
|
|
77
|
+
* once.
|
|
78
|
+
*/
|
|
79
|
+
readonly pendingCredentialLinkChanges: Observable<IPendingCredentialLink | undefined>;
|
|
80
|
+
private clearPendingCredentialLink;
|
|
81
|
+
private linkPendingCredential;
|
|
63
82
|
linkWith(authProviderID: AuthProviderID): Promise<UserCredential | undefined>;
|
|
64
83
|
unlinkAuthProvider(authProviderID: string): Promise<void>;
|
|
65
84
|
signInWith(authProviderID: AuthProviderID): Promise<UserCredential | undefined>;
|