@@ -18,6 +18,7 @@ import { Auth } from '../../model/public_types';
18
18
import { AuthErrorCode } from '../errors' ;
19
19
import { _assert } from '../util/assert' ;
20
20
import { _castAuth } from './auth_impl' ;
21
+ import { deepEqual } from '@firebase/util' ;
21
22
22
23
/**
23
24
* Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production
@@ -47,12 +48,6 @@ export function connectAuthEmulator(
47
48
options ?: { disableWarnings : boolean }
48
49
) : void {
49
50
const authInternal = _castAuth ( auth ) ;
50
- _assert (
51
- authInternal . _canInitEmulator ,
52
- authInternal ,
53
- AuthErrorCode . EMULATOR_CONFIG_FAILED
54
- ) ;
55
-
56
51
_assert (
57
52
/ ^ h t t p s ? : \/ \/ / . test ( url ) ,
58
53
authInternal ,
@@ -66,15 +61,42 @@ export function connectAuthEmulator(
66
61
const portStr = port === null ? '' : `:${ port } ` ;
67
62
68
63
// Always replace path with "/" (even if input url had no path at all, or had a different one).
69
- authInternal . config . emulator = { url : `${ protocol } //${ host } ${ portStr } /` } ;
70
- authInternal . settings . appVerificationDisabledForTesting = true ;
71
- authInternal . emulatorConfig = Object . freeze ( {
64
+ const emulator = { url : `${ protocol } //${ host } ${ portStr } /` } ;
65
+ const emulatorConfig = Object . freeze ( {
72
66
host,
73
67
port,
74
68
protocol : protocol . replace ( ':' , '' ) ,
75
69
options : Object . freeze ( { disableWarnings } )
76
70
} ) ;
77
71
72
+ // There are a few scenarios to guard against if the Auth instance has already started:
73
+ if ( ! authInternal . _canInitEmulator ) {
74
+ // Applications may not initialize the emulator for the first time if Auth has already started
75
+ // to make network requests.
76
+ _assert (
77
+ authInternal . config . emulator && authInternal . emulatorConfig ,
78
+ authInternal ,
79
+ AuthErrorCode . EMULATOR_CONFIG_FAILED
80
+ ) ;
81
+
82
+ // Applications may not alter the configuration of the emulator (aka pass a different config)
83
+ // once Auth has started to make network requests.
84
+ _assert (
85
+ deepEqual ( emulator , authInternal . config . emulator ) &&
86
+ deepEqual ( emulatorConfig , authInternal . emulatorConfig ) ,
87
+ authInternal ,
88
+ AuthErrorCode . EMULATOR_CONFIG_FAILED
89
+ ) ;
90
+
91
+ // It's valid, however, to invoke connectAuthEmulator() after Auth has started making
92
+ // connections, so long as the config matches the existing config. This results in a no-op.
93
+ return ;
94
+ }
95
+
96
+ authInternal . config . emulator = emulator ;
97
+ authInternal . emulatorConfig = emulatorConfig ;
98
+ authInternal . settings . appVerificationDisabledForTesting = true ;
99
+
78
100
if ( ! disableWarnings ) {
79
101
emitEmulatorWarning ( ) ;
80
102
}
0 commit comments