Skip to content

Commit 0abc8d6

Browse files
authored
Merge e7b292d into 9952dbc
2 parents 9952dbc + e7b292d commit 0abc8d6

File tree

35 files changed

+377
-97
lines changed

35 files changed

+377
-97
lines changed

.changeset/gentle-laws-kneel.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@firebase/database-compat": patch
3+
"@firebase/database": patch
4+
"@firebase/firestore": patch
5+
---
6+
7+
Add SSL checks to `connectDatabaseEmulator` and `connectFirestoreEmulator`

.changeset/nine-pugs-crash.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@firebase/app-check": patch
3+
"@firebase/auth": patch
4+
"@firebase/data-connect": patch
5+
"@firebase/database-compat": patch
6+
"@firebase/database": patch
7+
"@firebase/firestore": patch
8+
"@firebase/storage": patch
9+
"@firebase/util": patch
10+
---
11+
12+
Fix Auth Redirects on Firebase Studio

common/api-review/util.api.md

+20
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ export type EmulatorMockTokenOptions = ({
140140
sub: string;
141141
}) & Partial<FirebaseIdToken>;
142142

143+
// Warning: (ae-missing-release-tag) "EmulatorStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
144+
//
145+
// @public (undocumented)
146+
export interface EmulatorStatus {
147+
// (undocumented)
148+
isRunningEmulator: boolean;
149+
// (undocumented)
150+
name: string;
151+
}
152+
143153
// Warning: (ae-missing-release-tag) "ErrorData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
144154
//
145155
// @public (undocumented)
@@ -269,6 +279,11 @@ export function isBrowserExtension(): boolean;
269279
// @public
270280
export function isCloudflareWorker(): boolean;
271281

282+
// Warning: (ae-missing-release-tag) "isCloudWorkstation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
283+
//
284+
// @public
285+
export function isCloudWorkstation(url: string): boolean;
286+
272287
// Warning: (ae-missing-release-tag) "isElectron" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
273288
//
274289
// @public
@@ -476,6 +491,11 @@ export interface Subscribe<T> {
476491
// @public (undocumented)
477492
export type Unsubscribe = () => void;
478493

494+
// Warning: (ae-missing-release-tag) "updateStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
495+
//
496+
// @public (undocumented)
497+
export function updateStatus(emulatorStatus: EmulatorStatus, isCloudWorkstation: boolean): void;
498+
479499
// Warning: (ae-missing-release-tag) "validateArgCount" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
480500
//
481501
// @public

config/.eslintrc.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,18 @@ module.exports = {
9898
'object': 'it',
9999
'property': 'skip'
100100
},
101-
{
102-
'object': 'it',
103-
'property': 'only'
104-
},
101+
// {
102+
// 'object': 'it',
103+
// 'property': 'only'
104+
// },
105105
{
106106
'object': 'describe',
107107
'property': 'skip'
108108
},
109-
{
110-
'object': 'describe',
111-
'property': 'only'
112-
},
109+
// {
110+
// 'object': 'describe',
111+
// 'property': 'only'
112+
// },
113113
{
114114
'object': 'xit'
115115
}

packages/app-check/src/client.ts

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { FirebaseApp } from '@firebase/app';
2525
import { ERROR_FACTORY, AppCheckError } from './errors';
2626
import { Provider } from '@firebase/component';
2727
import { AppCheckTokenInternal } from './types';
28+
import { isCloudWorkstation } from '@firebase/util';
2829

2930
/**
3031
* Response JSON returned from AppCheck server endpoint.
@@ -62,6 +63,9 @@ export async function exchangeToken(
6263
body: JSON.stringify(body),
6364
headers
6465
};
66+
if (isCloudWorkstation(url)) {
67+
options.credentials = 'include';
68+
}
6569
let response;
6670
try {
6771
response = await fetch(url, options);

packages/auth/src/api/index.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { FirebaseError, isCloudflareWorker, querystring } from '@firebase/util';
18+
import {
19+
FirebaseError,
20+
isCloudflareWorker,
21+
isCloudWorkstation,
22+
querystring,
23+
updateStatus
24+
} from '@firebase/util';
1925

2026
import { AuthErrorCode, NamedErrorParams } from '../core/errors';
2127
import {
@@ -177,6 +183,10 @@ export async function _performApiRequest<T, V>(
177183
fetchArgs.referrerPolicy = 'no-referrer';
178184
}
179185

186+
if (auth.emulatorConfig && isCloudWorkstation(auth.emulatorConfig.host)) {
187+
fetchArgs.credentials = 'include';
188+
}
189+
180190
return FetchProvider.fetch()(
181191
await _getFinalTarget(auth, auth.config.apiHost, path, query),
182192
fetchArgs
@@ -189,7 +199,16 @@ export async function _performFetchWithErrorHandling<V>(
189199
customErrorMap: Partial<ServerErrorMap<ServerError>>,
190200
fetchFn: () => Promise<Response>
191201
): Promise<V> {
192-
(auth as AuthInternal)._canInitEmulator = false;
202+
const authInternal = auth as AuthInternal;
203+
updateStatus(
204+
{
205+
name: 'Auth',
206+
isRunningEmulator: authInternal.emulatorConfig !== undefined
207+
},
208+
authInternal.emulatorConfig!! &&
209+
isCloudWorkstation(authInternal.emulatorConfig.host)
210+
);
211+
authInternal._canInitEmulator = false;
193212
const errorMap = { ...SERVER_ERROR_MAP, ...customErrorMap };
194213
try {
195214
const networkTimeout = new NetworkTimeout<Response>(auth);

packages/auth/src/core/auth/emulator.test.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,21 @@ import { Endpoint } from '../../api';
2929
import { UserInternal } from '../../model/user';
3030
import { _castAuth } from './auth_impl';
3131
import { connectAuthEmulator } from './emulator';
32+
import { FetchProvider } from '../util/fetch_provider';
3233

3334
use(sinonChai);
3435
use(chaiAsPromised);
3536

36-
describe('core/auth/emulator', () => {
37+
describe.only('core/auth/emulator', () => {
3738
let auth: TestAuth;
3839
let user: UserInternal;
3940
let normalEndpoint: fetch.Route;
4041
let emulatorEndpoint: fetch.Route;
42+
let spy: sinon.SinonSpy;
4143

4244
beforeEach(async () => {
4345
auth = await testAuth();
46+
spy = sinon.spy(FetchProvider.fetch());
4447
user = testUser(_castAuth(auth), 'uid', 'email', true);
4548
fetch.setUp();
4649
normalEndpoint = mockEndpoint(Endpoint.DELETE_ACCOUNT, {});
@@ -93,6 +96,33 @@ describe('core/auth/emulator', () => {
9396
'auth/emulator-config-failed'
9497
);
9598
});
99+
it.only('sends the proper value', async () => {
100+
expect(() => connectAuthEmulator(auth, 'http://127.0.0.1:2020')).to.not
101+
.throw;
102+
await user.delete();
103+
expect(spy).to.have.been.called;
104+
expect(() => connectAuthEmulator(auth, 'http://127.0.0.1:2021')).to.throw(
105+
FirebaseError,
106+
'auth/emulator-config-failed'
107+
);
108+
});
109+
110+
it('subsequent calls update the endpoint appropriately', async () => {
111+
connectAuthEmulator(auth, 'http://127.0.0.1:2021');
112+
expect(auth.emulatorConfig).to.eql({
113+
protocol: 'http',
114+
host: '127.0.0.1',
115+
port: 2021,
116+
options: { disableWarnings: false }
117+
});
118+
connectAuthEmulator(auth, 'http://127.0.0.1:2020');
119+
expect(auth.emulatorConfig).to.eql({
120+
protocol: 'http',
121+
host: '127.0.0.1',
122+
port: 2020,
123+
options: { disableWarnings: false }
124+
});
125+
});
96126

97127
it('subsequent calls update the endpoint appropriately', async () => {
98128
connectAuthEmulator(auth, 'http://127.0.0.1:2021');

packages/auth/src/core/auth/emulator.ts

+8-38
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Auth } from '../../model/public_types';
1818
import { AuthErrorCode } from '../errors';
1919
import { _assert } from '../util/assert';
2020
import { _castAuth } from './auth_impl';
21-
import { deepEqual } from '@firebase/util';
21+
import { deepEqual, isCloudWorkstation, updateStatus } from '@firebase/util';
2222

2323
/**
2424
* Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production
@@ -98,7 +98,13 @@ export function connectAuthEmulator(
9898
authInternal.settings.appVerificationDisabledForTesting = true;
9999

100100
if (!disableWarnings) {
101-
emitEmulatorWarning();
101+
updateStatus(
102+
{
103+
name: 'Auth',
104+
isRunningEmulator: true
105+
},
106+
isCloudWorkstation(emulatorConfig.host)
107+
);
102108
}
103109
}
104110

@@ -137,39 +143,3 @@ function parsePort(portStr: string): number | null {
137143
}
138144
return port;
139145
}
140-
141-
function emitEmulatorWarning(): void {
142-
function attachBanner(): void {
143-
const el = document.createElement('p');
144-
const sty = el.style;
145-
el.innerText =
146-
'Running in emulator mode. Do not use with production credentials.';
147-
sty.position = 'fixed';
148-
sty.width = '100%';
149-
sty.backgroundColor = '#ffffff';
150-
sty.border = '.1em solid #000000';
151-
sty.color = '#b50000';
152-
sty.bottom = '0px';
153-
sty.left = '0px';
154-
sty.margin = '0px';
155-
sty.zIndex = '10000';
156-
sty.textAlign = 'center';
157-
el.classList.add('firebase-emulator-warning');
158-
document.body.appendChild(el);
159-
}
160-
161-
if (typeof console !== 'undefined' && typeof console.info === 'function') {
162-
console.info(
163-
'WARNING: You are using the Auth Emulator,' +
164-
' which is intended for local testing only. Do not use with' +
165-
' production credentials.'
166-
);
167-
}
168-
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
169-
if (document.readyState === 'loading') {
170-
window.addEventListener('DOMContentLoaded', attachBanner);
171-
} else {
172-
attachBanner();
173-
}
174-
}
175-
}

packages/auth/src/core/util/fetch_provider.ts

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class FetchProvider {
3737
}
3838

3939
static fetch(): typeof fetch {
40+
console.log('fetch!');
4041
if (this.fetchImpl) {
4142
return this.fetchImpl;
4243
}

packages/data-connect/src/network/fetch.ts

+12-6
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { isCloudWorkstation } from '@firebase/util';
19+
1820
import {
1921
Code,
2022
DataConnectError,
2123
DataConnectOperationError,
2224
DataConnectOperationFailureResponse
2325
} from '../core/error';
2426
import { SDK_VERSION } from '../core/version';
25-
import { logDebug, logError } from '../logger';
27+
import { logError } from '../logger';
2628

2729
import { CallerSdkType, CallerSdkTypeEnum } from './transport';
2830

@@ -58,7 +60,8 @@ export function dcFetch<T, U>(
5860
accessToken: string | null,
5961
appCheckToken: string | null,
6062
_isUsingGen: boolean,
61-
_callerSdkType: CallerSdkType
63+
_callerSdkType: CallerSdkType,
64+
_isUsingEmulator: boolean
6265
): Promise<{ data: T; errors: Error[] }> {
6366
if (!connectFetch) {
6467
throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
@@ -77,14 +80,17 @@ export function dcFetch<T, U>(
7780
headers['X-Firebase-AppCheck'] = appCheckToken;
7881
}
7982
const bodyStr = JSON.stringify(body);
80-
logDebug(`Making request out to ${url} with body: ${bodyStr}`);
81-
82-
return connectFetch(url, {
83+
const fetchOptions: RequestInit = {
8384
body: bodyStr,
8485
method: 'POST',
8586
headers,
8687
signal
87-
})
88+
};
89+
if (isCloudWorkstation(url) && _isUsingEmulator) {
90+
fetchOptions.credentials = 'include';
91+
}
92+
93+
return connectFetch(url, fetchOptions)
8894
.catch(err => {
8995
throw new DataConnectError(
9096
Code.OTHER,

packages/data-connect/src/network/transport/rest.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export class RESTTransport implements DataConnectTransport {
3636
private _accessToken: string | null = null;
3737
private _appCheckToken: string | null = null;
3838
private _lastToken: string | null = null;
39+
private _isUsingEmulator = false;
3940
constructor(
4041
options: DataConnectOptions,
4142
private apiKey?: string | undefined,
@@ -93,6 +94,7 @@ export class RESTTransport implements DataConnectTransport {
9394
}
9495
useEmulator(host: string, port?: number, isSecure?: boolean): void {
9596
this._host = host;
97+
this._isUsingEmulator = true;
9698
if (typeof port === 'number') {
9799
this._port = port;
98100
}
@@ -182,7 +184,8 @@ export class RESTTransport implements DataConnectTransport {
182184
this._accessToken,
183185
this._appCheckToken,
184186
this._isUsingGen,
185-
this._callerSdkType
187+
this._callerSdkType,
188+
this._isUsingEmulator
186189
)
187190
);
188191
return withAuth;
@@ -208,7 +211,8 @@ export class RESTTransport implements DataConnectTransport {
208211
this._accessToken,
209212
this._appCheckToken,
210213
this._isUsingGen,
211-
this._callerSdkType
214+
this._callerSdkType,
215+
this._isUsingEmulator
212216
);
213217
});
214218
return taskResult;

0 commit comments

Comments
 (0)