@sneat/auth-core 0.1.3 → 0.1.4

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.
Files changed (58) hide show
  1. package/esm2022/index.js +8 -0
  2. package/esm2022/index.js.map +1 -0
  3. package/esm2022/lib/login-required-service.service.js +20 -0
  4. package/esm2022/lib/login-required-service.service.js.map +1 -0
  5. package/esm2022/lib/private-token-store.service.js +36 -0
  6. package/esm2022/lib/private-token-store.service.js.map +1 -0
  7. package/esm2022/lib/sneat-auth-guard.js +79 -0
  8. package/esm2022/lib/sneat-auth-guard.js.map +1 -0
  9. package/esm2022/lib/sneat-auth-state-service.js +267 -0
  10. package/esm2022/lib/sneat-auth-state-service.js.map +1 -0
  11. package/{src/lib/sneat-auth.interface.ts → esm2022/lib/sneat-auth.interface.js} +1 -5
  12. package/esm2022/lib/sneat-auth.interface.js.map +1 -0
  13. package/esm2022/lib/telegram-auth.service.js +39 -0
  14. package/esm2022/lib/telegram-auth.service.js.map +1 -0
  15. package/esm2022/lib/user/index.js +3 -0
  16. package/esm2022/lib/user/index.js.map +1 -0
  17. package/esm2022/lib/user/sneat-user.service.js +171 -0
  18. package/esm2022/lib/user/sneat-user.service.js.map +1 -0
  19. package/esm2022/lib/user/user-record.service.js +30 -0
  20. package/esm2022/lib/user/user-record.service.js.map +1 -0
  21. package/esm2022/sneat-auth-core.js +5 -0
  22. package/esm2022/sneat-auth-core.js.map +1 -0
  23. package/lib/login-required-service.service.d.ts +6 -0
  24. package/lib/private-token-store.service.d.ts +8 -0
  25. package/lib/sneat-auth-guard.d.ts +26 -0
  26. package/lib/sneat-auth-state-service.d.ts +51 -0
  27. package/lib/sneat-auth.interface.d.ts +5 -0
  28. package/lib/telegram-auth.service.d.ts +9 -0
  29. package/lib/user/sneat-user.service.d.ts +34 -0
  30. package/lib/user/user-record.service.d.ts +25 -0
  31. package/package.json +14 -2
  32. package/sneat-auth-core.d.ts +5 -0
  33. package/eslint.config.js +0 -7
  34. package/ng-package.json +0 -7
  35. package/project.json +0 -38
  36. package/src/lib/login-required-service.service.spec.ts +0 -39
  37. package/src/lib/login-required-service.service.ts +0 -14
  38. package/src/lib/private-token-store.service.spec.ts +0 -75
  39. package/src/lib/private-token-store.service.ts +0 -36
  40. package/src/lib/sneat-auth-guard.spec.ts +0 -124
  41. package/src/lib/sneat-auth-guard.ts +0 -107
  42. package/src/lib/sneat-auth-state-service.spec.ts +0 -332
  43. package/src/lib/sneat-auth-state-service.ts +0 -387
  44. package/src/lib/sneat-auth.interface.spec.ts +0 -39
  45. package/src/lib/telegram-auth.service.spec.ts +0 -186
  46. package/src/lib/telegram-auth.service.ts +0 -49
  47. package/src/lib/user/sneat-user.service.spec.ts +0 -151
  48. package/src/lib/user/sneat-user.service.ts +0 -266
  49. package/src/lib/user/user-record.service.spec.ts +0 -145
  50. package/src/lib/user/user-record.service.ts +0 -38
  51. package/src/test-setup.ts +0 -3
  52. package/tsconfig.json +0 -13
  53. package/tsconfig.lib.json +0 -19
  54. package/tsconfig.lib.prod.json +0 -7
  55. package/tsconfig.spec.json +0 -31
  56. package/vite.config.mts +0 -10
  57. /package/{src/index.ts → index.d.ts} +0 -0
  58. /package/{src/lib/user/index.ts → lib/user/index.d.ts} +0 -0
@@ -1,387 +0,0 @@
1
- import { SignInWithOAuthOptions } from '@capacitor-firebase/authentication/dist/esm/definitions';
2
- import { Capacitor } from '@capacitor/core';
3
- import {
4
- FirebaseAuthentication,
5
- SignInResult,
6
- } from '@capacitor-firebase/authentication';
7
- import {
8
- AnalyticsService,
9
- EnumAsUnionOfKeys,
10
- IAnalyticsService,
11
- } from '@sneat/core';
12
- import { BehaviorSubject, from, Observable } from 'rxjs';
13
- import { Injectable, inject } from '@angular/core';
14
- import { ErrorLogger, IErrorLogger } from '@sneat/core';
15
- import { distinctUntilChanged, shareReplay } from 'rxjs/operators';
16
- import {
17
- Auth,
18
- AuthProvider,
19
- getAuth,
20
- signInWithCredential,
21
- User,
22
- UserCredential,
23
- UserInfo,
24
- } from '@angular/fire/auth';
25
-
26
- import {
27
- GoogleAuthProvider,
28
- OAuthProvider,
29
- GithubAuthProvider,
30
- FacebookAuthProvider,
31
- signInWithEmailLink,
32
- signInWithCustomToken,
33
- signInWithPopup,
34
- linkWithPopup,
35
- unlink,
36
- } from 'firebase/auth';
37
-
38
- // TODO: fix & remove this eslint hint @nrwl/nx/enforce-module-boundaries
39
-
40
- import { newRandomId } from '@sneat/random';
41
-
42
- export enum AuthStatuses {
43
- authenticating = 'authenticating',
44
- authenticated = 'authenticated',
45
- notAuthenticated = 'notAuthenticated',
46
- }
47
-
48
- export type AuthStatus = EnumAsUnionOfKeys<typeof AuthStatuses>;
49
-
50
- export interface ISneatAuthUser extends UserInfo {
51
- readonly isAnonymous: boolean;
52
- readonly emailVerified: boolean;
53
- readonly providerData: readonly UserInfo[];
54
- }
55
-
56
- export interface ISneatAuthState {
57
- readonly status: AuthStatus;
58
- readonly token?: string | null;
59
- readonly user?: ISneatAuthUser | null;
60
- readonly err?: unknown;
61
- }
62
-
63
- const initialAuthStatus = AuthStatuses.authenticating;
64
- export const initialSneatAuthState = { status: initialAuthStatus };
65
-
66
- @Injectable({ providedIn: 'root' })
67
- export class SneatAuthStateService {
68
- private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);
69
- private readonly analyticsService =
70
- inject<IAnalyticsService>(AnalyticsService);
71
- readonly fbAuth = inject(Auth);
72
-
73
- private readonly id = newRandomId({ len: 5 });
74
-
75
- private readonly authStatus$ = new BehaviorSubject<AuthStatus>(
76
- initialAuthStatus,
77
- );
78
- public readonly authStatus = this.authStatus$
79
- .asObservable()
80
- .pipe(distinctUntilChanged());
81
-
82
- private readonly authUser$ = new BehaviorSubject<
83
- ISneatAuthUser | null | undefined
84
- >(undefined);
85
- public readonly authUser = this.authUser$.asObservable();
86
-
87
- private readonly authState$ = new BehaviorSubject<ISneatAuthState>(
88
- initialSneatAuthState,
89
- );
90
- public readonly authState = this.authState$.asObservable().pipe(
91
- // tap(v => console.log('SneatAuthStateService => SneatAuthState:', v)),
92
- shareReplay(1),
93
- );
94
-
95
- // private readonly fbAuth: Auth;
96
-
97
- constructor() {
98
- const errorLogger = this.errorLogger;
99
- this.fbAuth.onIdTokenChanged({
100
- next: (firebaseUser) => {
101
- const status: AuthStatus = firebaseUser
102
- ? AuthStatuses.authenticated
103
- : AuthStatuses.notAuthenticated;
104
- if (
105
- firebaseUser &&
106
- this.authState$.value?.user?.uid !== firebaseUser?.uid
107
- ) {
108
- this.analyticsService.identify(firebaseUser.uid);
109
- }
110
- firebaseUser
111
- ?.getIdToken()
112
- .then((token) => {
113
- const current = this.authState$.value || {};
114
- this.authState$.next({
115
- ...current,
116
- status,
117
- token,
118
- user: this.authUser$.value,
119
- });
120
- this.authStatus$.next(status); // Should be after authState$
121
- })
122
- .catch((err) => {
123
- const current = this.authState$.value || {};
124
- this.authState$.next({
125
- ...current,
126
- err: `fbUser.getIdToken() failed: ${err}`,
127
- });
128
- this.errorLogger.logError(err, 'Failed in fbUser.getIdToken()');
129
- });
130
- },
131
- error: (err) => {
132
- const current = this.authState$.value || {};
133
- this.authState$.next({
134
- ...current,
135
- err: `fbAuth.onIdTokenChanged() failed: ${err}`,
136
- });
137
- errorLogger.logError(err, 'failed in fbAuth.onIdTokenChanged');
138
- },
139
- complete: () => void 0,
140
- });
141
- this.fbAuth.onAuthStateChanged({
142
- complete: () => void 0,
143
- next: (fbUser) => {
144
- // console.log(
145
- // `SneatAuthStateService => authStatus: ${this.authStatus$.value}; fbUser`,
146
- // fbUser,
147
- // );
148
-
149
- const authUser = createSneatAuthUserFromFbUser(fbUser);
150
-
151
- const status = authUser
152
- ? AuthStatuses.authenticated
153
- : AuthStatuses.notAuthenticated;
154
- this.authStatus$.next(status);
155
- this.authUser$.next(authUser);
156
- this.authState$.next({
157
- ...this.authState$.value,
158
- user: authUser,
159
- status,
160
- });
161
- },
162
- error: (err) => {
163
- this.errorLogger.logError(
164
- err,
165
- 'failed to retrieve Firebase auth user information',
166
- );
167
- const current = this.authState$.value || {};
168
- this.authState$.next({
169
- ...current,
170
- err: `fbAuth.onAuthStateChanged() failed: ${err}`,
171
- });
172
- },
173
- });
174
- }
175
-
176
- public signOut(): Promise<void> {
177
- return this.fbAuth.signOut();
178
- }
179
-
180
- public signInWithToken(token: string): Promise<UserCredential> {
181
- return signInWithCustomToken(this.fbAuth, token);
182
- }
183
-
184
- public signInWithEmailLink(email: string): Observable<UserCredential> {
185
- return from(signInWithEmailLink(this.fbAuth, email));
186
- }
187
-
188
- private isSigningInWith?: AuthProviderName;
189
-
190
- private async signInOnNativeLayer(
191
- authProviderID: AuthProviderID,
192
- ): Promise<UserCredential> {
193
- let signInResult: SignInResult | undefined;
194
-
195
- const o: SignInWithOAuthOptions = { skipNativeAuth: true };
196
-
197
- switch (authProviderID) {
198
- case 'google.com':
199
- signInResult = await FirebaseAuthentication.signInWithGoogle(o);
200
- break;
201
- case 'apple.com':
202
- signInResult = await FirebaseAuthentication.signInWithApple(o);
203
- break;
204
- case 'facebook.com':
205
- signInResult = await FirebaseAuthentication.signInWithFacebook(o);
206
- break;
207
- case 'microsoft.com':
208
- signInResult = await FirebaseAuthentication.signInWithMicrosoft(o);
209
- break;
210
- default:
211
- return Promise.reject('unsupported auth provider: ' + authProviderID);
212
- }
213
- // console.log(
214
- // `SneatAuthStateService.signInWith(${authProviderID}) => signed in on native layer, authenticating in webview...`,
215
- // signInResult,
216
- // );
217
- // we need to authenticate on webview layer using the id token and nonce from signInResult
218
- const userCredential = await this.authenticateOnWebviewLayer(
219
- authProviderID,
220
- signInResult,
221
- );
222
-
223
- return Promise.resolve(userCredential);
224
- }
225
-
226
- private async authenticateOnWebviewLayer(
227
- authProviderID: AuthProviderID,
228
- signInResult: SignInResult,
229
- ): Promise<UserCredential> {
230
- const oauthProvider = new OAuthProvider(authProviderID);
231
- const credential = oauthProvider.credential({
232
- idToken: signInResult.credential?.idToken,
233
- accessToken: signInResult.credential?.accessToken,
234
- rawNonce: signInResult.credential?.nonce,
235
- });
236
- const auth = getAuth();
237
- const userCredential = await signInWithCredential(auth, credential);
238
-
239
- // Get a valid Firebase ID token that has a 'kid' header
240
- const _firebaseIdToken = await userCredential.user.getIdToken();
241
-
242
- return Promise.resolve(userCredential);
243
- }
244
-
245
- private async signInWithWebSDK(
246
- authProviderID: AuthProviderID,
247
- ): Promise<UserCredential> {
248
- const authProvider = getAuthProvider(authProviderID);
249
- const userCredential = await signInWithPopup(this.fbAuth, authProvider);
250
- return Promise.resolve(userCredential);
251
- }
252
-
253
- public async linkWith(
254
- authProviderID: AuthProviderID,
255
- ): Promise<UserCredential | undefined> {
256
- const authProvider = getAuthProvider(authProviderID);
257
- if (!this.fbAuth.currentUser) {
258
- return Promise.reject('no current user');
259
- }
260
- const userCredential = await linkWithPopup(
261
- this.fbAuth.currentUser,
262
- authProvider,
263
- );
264
- return Promise.resolve(userCredential);
265
- }
266
-
267
- public async unlinkAuthProvider(authProviderID: string): Promise<void> {
268
- const currentUser = this.fbAuth.currentUser;
269
- if (currentUser) {
270
- try {
271
- const updatedUser = await unlink(currentUser, authProviderID);
272
- const authUser = createSneatAuthUserFromFbUser(updatedUser);
273
- this.authUser$.next(authUser);
274
- return Promise.resolve();
275
- } catch (error) {
276
- return Promise.reject(
277
- `Failed to unlink ${authProviderID} account:` + error,
278
- );
279
- }
280
- } else {
281
- return Promise.reject('No user is currently signed in.');
282
- }
283
- }
284
-
285
- public async signInWith(
286
- authProviderID: AuthProviderID,
287
- ): Promise<UserCredential | undefined> {
288
- // console.log(
289
- // `SneatAuthStateService.signInWith(${authProviderID}), isSigningInWith=${this.isSigningInWith}, location.protocol=${location.protocol}`,
290
- // );
291
- this.analyticsService.logEvent('signingInWith', {
292
- provider: authProviderID,
293
- });
294
- try {
295
- if (this.isSigningInWith) {
296
- return Promise.reject(
297
- new Error(
298
- `a repeated call to SneatAuthStateService.signInWith(${authProviderID}) white previous sign in with ${this.isSigningInWith} is in progress`,
299
- ),
300
- );
301
- }
302
-
303
- let userCredential: UserCredential | undefined = undefined;
304
-
305
- if (Capacitor.isNativePlatform()) {
306
- userCredential = await this.signInOnNativeLayer(authProviderID);
307
- } else {
308
- userCredential = await this.signInWithWebSDK(authProviderID);
309
- }
310
-
311
- this.isSigningInWith = undefined;
312
- this.analyticsService.logEvent('signedInWith', {
313
- provider: authProviderID,
314
- });
315
- return Promise.resolve(userCredential);
316
- } catch (e) {
317
- this.isSigningInWith = undefined;
318
- return Promise.reject(e);
319
- }
320
- }
321
- }
322
-
323
- export type AuthProviderID =
324
- | 'apple.com'
325
- | 'google.com'
326
- | 'microsoft.com'
327
- | 'facebook.com'
328
- | 'github.com'
329
- | 'phone';
330
-
331
- export type AuthProviderName =
332
- | 'Google'
333
- | 'Apple'
334
- | 'Microsoft'
335
- | 'Facebook'
336
- | 'GitHub';
337
-
338
- function getAuthProvider(authProviderID: AuthProviderID): AuthProvider {
339
- switch (authProviderID) {
340
- case 'google.com':
341
- return new GoogleAuthProvider();
342
- case 'apple.com':
343
- return new OAuthProvider('apple.com');
344
- //
345
- // https://developer.apple.com/documentation/sign_in_with_apple/incorporating-sign-in-with-apple-into-other-platforms
346
- // (authProvider as OAuthProvider).setCustomParameters({
347
- // // // Localize the Apple authentication screen in current app locale.
348
- // locale: 'en', // TODO: set locale
349
- // });
350
- case 'microsoft.com':
351
- return new OAuthProvider('microsoft.com');
352
- case 'facebook.com': {
353
- const facebookAuthProvider = new FacebookAuthProvider();
354
- facebookAuthProvider.addScope('email');
355
- return facebookAuthProvider;
356
- }
357
- case 'github.com': {
358
- const githubAuthProvider = new GithubAuthProvider();
359
- githubAuthProvider.addScope('read:user');
360
- githubAuthProvider.addScope('user:email');
361
- return githubAuthProvider;
362
- }
363
- default:
364
- throw new Error('unsupported auth provider: ' + authProviderID);
365
- }
366
- }
367
-
368
- function createSneatAuthUserFromFbUser(
369
- fbUser: User | null,
370
- ): ISneatAuthUser | null {
371
- return (
372
- fbUser && {
373
- isAnonymous: fbUser.isAnonymous,
374
- emailVerified: fbUser.emailVerified,
375
- email: fbUser.email,
376
- uid: fbUser.uid,
377
- displayName: fbUser.displayName,
378
- phoneNumber: fbUser.phoneNumber,
379
- photoURL: fbUser.photoURL,
380
- providerId:
381
- fbUser.providerData?.length === 1 && fbUser.providerData[0]
382
- ? fbUser.providerData[0].providerId
383
- : fbUser.providerId,
384
- providerData: fbUser.providerData,
385
- }
386
- );
387
- }
@@ -1,39 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- ILoginEventsHandler,
4
- LoginEventsHandler,
5
- } from './sneat-auth.interface';
6
-
7
- describe('sneat-auth.interface', () => {
8
- describe('LoginEventsHandler', () => {
9
- it('should be an InjectionToken', () => {
10
- expect(LoginEventsHandler).toBeDefined();
11
- expect(LoginEventsHandler.toString()).toContain('ILoginEventsHandler');
12
- });
13
- });
14
-
15
- describe('ILoginEventsHandler interface', () => {
16
- it('should be implemented by objects with onLoggedIn method', () => {
17
- const handler: ILoginEventsHandler = {
18
- onLoggedIn: () => {
19
- // Implementation
20
- },
21
- };
22
-
23
- expect(handler).toBeDefined();
24
- expect(typeof handler.onLoggedIn).toBe('function');
25
- });
26
-
27
- it('should allow onLoggedIn to be called', () => {
28
- let called = false;
29
- const handler: ILoginEventsHandler = {
30
- onLoggedIn: () => {
31
- called = true;
32
- },
33
- };
34
-
35
- handler.onLoggedIn();
36
- expect(called).toBe(true);
37
- });
38
- });
39
- });
@@ -1,186 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
- import { SneatApiService } from '@sneat/api';
3
- import { ErrorLogger } from '@sneat/core';
4
- import { SneatAuthStateService } from './sneat-auth-state-service';
5
- import { TelegramAuthService } from './telegram-auth.service';
6
- import { describe, it, expect, beforeEach, vi } from 'vitest';
7
- import { of, throwError } from 'rxjs';
8
-
9
- describe('TelegramAuthService', () => {
10
- let service: TelegramAuthService;
11
- let sneatApiServiceMock: { postAsAnonymous: ReturnType<typeof vi.fn> };
12
- let authStateServiceMock: { signInWithToken: ReturnType<typeof vi.fn> };
13
- let errorLoggerMock: { logErrorHandler: ReturnType<typeof vi.fn> };
14
-
15
- beforeEach(() => {
16
- sneatApiServiceMock = {
17
- postAsAnonymous: vi.fn(),
18
- };
19
-
20
- authStateServiceMock = {
21
- signInWithToken: vi.fn().mockResolvedValue({}),
22
- };
23
-
24
- errorLoggerMock = {
25
- logErrorHandler: vi.fn().mockReturnValue((err: unknown) => {
26
- console.error(err);
27
- }),
28
- };
29
-
30
- TestBed.configureTestingModule({
31
- providers: [
32
- TelegramAuthService,
33
- {
34
- provide: ErrorLogger,
35
- useValue: errorLoggerMock,
36
- },
37
- {
38
- provide: SneatAuthStateService,
39
- useValue: authStateServiceMock,
40
- },
41
- {
42
- provide: SneatApiService,
43
- useValue: sneatApiServiceMock,
44
- },
45
- ],
46
- });
47
- service = TestBed.inject(TelegramAuthService);
48
- });
49
-
50
- it('should be created', () => {
51
- expect(service).toBeTruthy();
52
- });
53
-
54
- it('should not authenticate if Telegram WebApp is not available', () => {
55
- delete (window as unknown as { Telegram?: unknown }).Telegram;
56
-
57
- service.authenticateIfTelegramWebApp();
58
-
59
- expect(sneatApiServiceMock.postAsAnonymous).not.toHaveBeenCalled();
60
- });
61
-
62
- it('should not authenticate if Telegram WebApp initData is missing', () => {
63
- (
64
- window as unknown as { Telegram: { WebApp: { initData?: unknown } } }
65
- ).Telegram = {
66
- WebApp: {},
67
- };
68
-
69
- service.authenticateIfTelegramWebApp();
70
-
71
- expect(sneatApiServiceMock.postAsAnonymous).not.toHaveBeenCalled();
72
- });
73
-
74
- it('should authenticate with Telegram WebApp', () => {
75
- const mockInitData = 'test-init-data';
76
- const mockToken = 'firebase-token-123';
77
- const mockReady = vi.fn();
78
-
79
- (
80
- window as unknown as {
81
- Telegram: { WebApp: { initData: string; ready: () => void } };
82
- }
83
- ).Telegram = {
84
- WebApp: {
85
- initData: mockInitData,
86
- ready: mockReady,
87
- },
88
- };
89
-
90
- sneatApiServiceMock.postAsAnonymous.mockReturnValue(
91
- of({ token: mockToken }),
92
- );
93
-
94
- service.authenticateIfTelegramWebApp();
95
-
96
- expect(sneatApiServiceMock.postAsAnonymous).toHaveBeenCalledWith(
97
- 'auth/login-from-telegram-miniapp',
98
- mockInitData,
99
- );
100
- });
101
-
102
- it('should call signInWithToken on successful API response', async () => {
103
- const mockInitData = 'test-init-data';
104
- const mockToken = 'firebase-token-123';
105
- const mockReady = vi.fn();
106
-
107
- (
108
- window as unknown as {
109
- Telegram: { WebApp: { initData: string; ready: () => void } };
110
- }
111
- ).Telegram = {
112
- WebApp: {
113
- initData: mockInitData,
114
- ready: mockReady,
115
- },
116
- };
117
-
118
- sneatApiServiceMock.postAsAnonymous.mockReturnValue(
119
- of({ token: mockToken }),
120
- );
121
-
122
- service.authenticateIfTelegramWebApp();
123
-
124
- await new Promise((resolve) => setTimeout(resolve, 10));
125
-
126
- expect(authStateServiceMock.signInWithToken).toHaveBeenCalledWith(
127
- mockToken,
128
- );
129
- expect(mockReady).toHaveBeenCalled();
130
- });
131
-
132
- it('should handle API error gracefully', () => {
133
- const mockInitData = 'test-init-data';
134
-
135
- (
136
- window as unknown as { Telegram: { WebApp: { initData: string } } }
137
- ).Telegram = {
138
- WebApp: {
139
- initData: mockInitData,
140
- },
141
- };
142
-
143
- sneatApiServiceMock.postAsAnonymous.mockReturnValue(
144
- throwError(() => new Error('API Error')),
145
- );
146
-
147
- service.authenticateIfTelegramWebApp();
148
-
149
- expect(sneatApiServiceMock.postAsAnonymous).toHaveBeenCalled();
150
- expect(errorLoggerMock.logErrorHandler).toHaveBeenCalledWith(
151
- 'Failed to get Firebase auth token issued using telegram web-app credentials',
152
- );
153
- });
154
-
155
- it('should handle signInWithToken error gracefully', async () => {
156
- const mockInitData = 'test-init-data';
157
- const mockToken = 'firebase-token-123';
158
-
159
- (
160
- window as unknown as {
161
- Telegram: { WebApp: { initData: string; ready: () => void } };
162
- }
163
- ).Telegram = {
164
- WebApp: {
165
- initData: mockInitData,
166
- ready: vi.fn(),
167
- },
168
- };
169
-
170
- sneatApiServiceMock.postAsAnonymous.mockReturnValue(
171
- of({ token: mockToken }),
172
- );
173
-
174
- authStateServiceMock.signInWithToken.mockRejectedValue(
175
- new Error('Sign in error'),
176
- );
177
-
178
- service.authenticateIfTelegramWebApp();
179
-
180
- await new Promise((resolve) => setTimeout(resolve, 10));
181
-
182
- expect(errorLoggerMock.logErrorHandler).toHaveBeenCalledWith(
183
- 'Failed to sign-in to Firebase auth with a token issued for telegram web-app credentials',
184
- );
185
- });
186
- });
@@ -1,49 +0,0 @@
1
- import { Injectable, inject } from '@angular/core';
2
- import { SneatApiService } from '@sneat/api';
3
- import { SneatAuthStateService } from './sneat-auth-state-service';
4
- import { ErrorLogger, IErrorLogger } from '@sneat/core';
5
-
6
- @Injectable({
7
- providedIn: 'root',
8
- })
9
- export class TelegramAuthService {
10
- private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);
11
- private readonly authStateService = inject(SneatAuthStateService);
12
- private readonly sneatApiService = inject(SneatApiService);
13
-
14
- public authenticateIfTelegramWebApp() {
15
- const telegramWebApp = (
16
- window as unknown as {
17
- Telegram?: {
18
- WebApp?: {
19
- initData: unknown;
20
- ready: () => void;
21
- };
22
- };
23
- }
24
- ).Telegram?.WebApp;
25
- if (telegramWebApp?.initData) {
26
- this.sneatApiService
27
- .postAsAnonymous<{
28
- token: string;
29
- }>('auth/login-from-telegram-miniapp', telegramWebApp?.initData)
30
- .subscribe({
31
- next: (response) => {
32
- this.authStateService
33
- .signInWithToken(response.token)
34
- .then(() => {
35
- telegramWebApp.ready();
36
- })
37
- .catch(
38
- this.errorLogger.logErrorHandler(
39
- 'Failed to sign-in to Firebase auth with a token issued for telegram web-app credentials',
40
- ),
41
- );
42
- },
43
- error: this.errorLogger.logErrorHandler(
44
- 'Failed to get Firebase auth token issued using telegram web-app credentials',
45
- ),
46
- });
47
- }
48
- }
49
- }