|
16 | 16 | */
|
17 | 17 |
|
18 | 18 | import {
|
19 |
| - AuthEvent, |
20 |
| - AuthEventConsumer, |
21 |
| - EventManager |
| 19 | + AuthEvent, AuthEventConsumer, AuthEventType, EventManager |
22 | 20 | } from '../../model/popup_redirect';
|
23 | 21 | import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../errors';
|
24 | 22 |
|
25 | 23 | export class AuthEventManager implements EventManager {
|
26 | 24 | private readonly consumers: Set<AuthEventConsumer> = new Set();
|
| 25 | + private queuedRedirectEvent: AuthEvent | null = null; |
| 26 | + private hasHandledPotentialRedirect = false; |
27 | 27 |
|
28 | 28 | constructor(private readonly appName: string) {}
|
29 | 29 |
|
30 | 30 | registerConsumer(authEventConsumer: AuthEventConsumer): void {
|
31 | 31 | this.consumers.add(authEventConsumer);
|
| 32 | + |
| 33 | + if (this.queuedRedirectEvent && this.isEventForConsumer(this.queuedRedirectEvent, authEventConsumer)) { |
| 34 | + this.sendToConsumer(this.queuedRedirectEvent, authEventConsumer); |
| 35 | + this.queuedRedirectEvent = null; |
| 36 | + } |
32 | 37 | }
|
33 | 38 |
|
34 | 39 | unregisterConsumer(authEventConsumer: AuthEventConsumer): void {
|
35 | 40 | this.consumers.delete(authEventConsumer);
|
36 | 41 | }
|
37 | 42 |
|
38 |
| - onEvent(event: AuthEvent): void { |
| 43 | + onEvent(event: AuthEvent): boolean { |
| 44 | + let handled = false; |
39 | 45 | this.consumers.forEach(consumer => {
|
40 |
| - if ( |
41 |
| - consumer.filter === event.type && |
42 |
| - consumer.isMatchingEvent(event.eventId) |
43 |
| - ) { |
44 |
| - if (event.error) { |
45 |
| - console.error('ERROR'); |
46 |
| - const code = |
47 |
| - (event.error.code?.split('auth/')[1] as AuthErrorCode) || |
48 |
| - AuthErrorCode.INTERNAL_ERROR; |
49 |
| - consumer.onError( |
50 |
| - AUTH_ERROR_FACTORY.create(code, { |
51 |
| - appName: this.appName |
52 |
| - }) |
53 |
| - ); |
54 |
| - } else { |
55 |
| - consumer.onAuthEvent(event); |
56 |
| - } |
| 46 | + if (this.isEventForConsumer(event, consumer)) { |
| 47 | + handled = true; |
| 48 | + this.sendToConsumer(event, consumer); |
57 | 49 | }
|
58 | 50 | });
|
| 51 | + |
| 52 | + // The redirect event is always available immediately, unlike popup |
| 53 | + // events (which happen in the normal app lifetime). Since the user |
| 54 | + // may open the iframe (through a popup method) before getRedirectResult() |
| 55 | + // is called, we need to queue up the redirect event so the user has access |
| 56 | + // to it later. On the other hand, if we get a "unknown" auth event with |
| 57 | + // the message "no-auth-event", we know there will never be a redirect event |
| 58 | + // for this session. |
| 59 | + if (event.type === AuthEventType.UNKNOWN && event.error?.code === `auth/${AuthErrorCode.NO_AUTH_EVENT}`) { |
| 60 | + this.hasHandledPotentialRedirect = true; |
| 61 | + return true; |
| 62 | + |
| 63 | + } else if (!handled && isRedirectEvent(event.type) && !this.hasHandledPotentialRedirect) { |
| 64 | + this.queuedRedirectEvent = event; |
| 65 | + handled = true; |
| 66 | + } |
| 67 | + |
| 68 | + this.hasHandledPotentialRedirect = this.hasHandledPotentialRedirect || isRedirectEvent(event.type); |
| 69 | + |
| 70 | + return handled; |
| 71 | + } |
| 72 | + |
| 73 | + private sendToConsumer(event: AuthEvent, consumer: AuthEventConsumer): void { |
| 74 | + if (event.error) { |
| 75 | + console.error('ERROR'); |
| 76 | + const code = |
| 77 | + (event.error.code?.split('auth/')[1] as AuthErrorCode) || |
| 78 | + AuthErrorCode.INTERNAL_ERROR; |
| 79 | + consumer.onError( |
| 80 | + AUTH_ERROR_FACTORY.create(code, { |
| 81 | + appName: this.appName |
| 82 | + }) |
| 83 | + ); |
| 84 | + } else { |
| 85 | + consumer.onAuthEvent(event); |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + private isEventForConsumer(event: AuthEvent, consumer: AuthEventConsumer): boolean { |
| 90 | + const eventIdMatches = consumer.eventId === null || (!!event.eventId && event.eventId === consumer.eventId); |
| 91 | + return consumer.filter.includes(event.type) && eventIdMatches; |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +function isRedirectEvent(type: AuthEventType): boolean { |
| 96 | + switch (type) { |
| 97 | + case AuthEventType.SIGN_IN_VIA_REDIRECT: |
| 98 | + case AuthEventType.LINK_VIA_REDIRECT: |
| 99 | + case AuthEventType.REAUTH_VIA_REDIRECT: |
| 100 | + return true; |
| 101 | + default: |
| 102 | + return false; |
59 | 103 | }
|
60 | 104 | }
|
0 commit comments