Skip to content

Commit 6d8b119

Browse files
committed
Merge branch 'main' into dl/genai
2 parents cc1726d + 080a90d commit 6d8b119

File tree

36 files changed

+374
-50
lines changed

36 files changed

+374
-50
lines changed

.changeset/nine-pugs-crash.md

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

common/api-review/storage.api.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class _FirebaseStorageImpl implements FirebaseStorage {
5858
constructor(
5959
app: FirebaseApp, _authProvider: Provider<FirebaseAuthInternalName>,
6060
_appCheckProvider: Provider<AppCheckInternalComponentName>,
61-
_url?: string | undefined, _firebaseVersion?: string | undefined);
61+
_url?: string | undefined, _firebaseVersion?: string | undefined, _isUsingEmulator?: boolean);
6262
readonly app: FirebaseApp;
6363
// (undocumented)
6464
readonly _appCheckProvider: Provider<AppCheckInternalComponentName>;
@@ -77,6 +77,8 @@ export class _FirebaseStorageImpl implements FirebaseStorage {
7777
_getAuthToken(): Promise<string | null>;
7878
get host(): string;
7979
set host(host: string);
80+
// (undocumented)
81+
_isUsingEmulator: boolean;
8082
// Warning: (ae-forgotten-export) The symbol "ConnectionType" needs to be exported by the entry point index.d.ts
8183
// Warning: (ae-forgotten-export) The symbol "RequestInfo" needs to be exported by the entry point index.d.ts
8284
// Warning: (ae-forgotten-export) The symbol "Connection" needs to be exported by the entry point index.d.ts

common/api-review/util.api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ export function ordinal(i: number): string;
398398
// @public (undocumented)
399399
export type PartialObserver<T> = Partial<Observer<T>>;
400400

401+
// @public
402+
export function pingServer(endpoint: string): Promise<boolean>;
403+
401404
// Warning: (ae-internal-missing-underscore) The name "promiseWithTimeout" should be prefixed with an underscore because the declaration is marked as @internal
402405
//
403406
// @internal

packages/auth/src/api/index.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ describe('api/_performApiRequest', () => {
6060
auth = await testAuth();
6161
});
6262

63+
afterEach(() => {
64+
sinon.restore();
65+
});
66+
6367
context('with regular requests', () => {
6468
beforeEach(mockFetch.setUp);
6569
afterEach(mockFetch.tearDown);
@@ -80,6 +84,26 @@ describe('api/_performApiRequest', () => {
8084
expect(mock.calls[0].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
8185
'testSDK/0.0.0'
8286
);
87+
expect(mock.calls[0].fullRequest?.credentials).to.be.undefined;
88+
});
89+
90+
it('should set credentials to "include" when using IDX and emulator', async () => {
91+
const mock = mockEndpoint(Endpoint.SIGN_UP, serverResponse);
92+
auth.emulatorConfig = {
93+
host: 'https://something.cloudworkstations.dev',
94+
protocol: '',
95+
port: 8,
96+
options: {
97+
disableWarnings: false
98+
}
99+
};
100+
await _performApiRequest<typeof request, typeof serverResponse>(
101+
auth,
102+
HttpMethod.POST,
103+
Endpoint.SIGN_UP,
104+
request
105+
);
106+
expect(mock.calls[0].fullRequest?.credentials).to.eq('include');
83107
});
84108

85109
it('should set the device language if available', async () => {

packages/auth/src/api/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
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+
} from '@firebase/util';
1924

2025
import { AuthErrorCode, NamedErrorParams } from '../core/errors';
2126
import {
@@ -177,6 +182,10 @@ export async function _performApiRequest<T, V>(
177182
fetchArgs.referrerPolicy = 'no-referrer';
178183
}
179184

185+
if (auth.emulatorConfig && isCloudWorkstation(auth.emulatorConfig.host)) {
186+
fetchArgs.credentials = 'include';
187+
}
188+
180189
return FetchProvider.fetch()(
181190
await _getFinalTarget(auth, auth.config.apiHost, path, query),
182191
fetchArgs

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

Lines changed: 6 additions & 1 deletion
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, pingServer } from '@firebase/util';
2222

2323
/**
2424
* Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production
@@ -100,6 +100,11 @@ export function connectAuthEmulator(
100100
if (!disableWarnings) {
101101
emitEmulatorWarning();
102102
}
103+
104+
// Workaround to get cookies in Firebase Studio
105+
if (isCloudWorkstation(host)) {
106+
void pingServer(`${protocol}//${host}:${port}`);
107+
}
103108
}
104109

105110
function extractProtocol(url: string): string {

packages/auth/test/helpers/mock_fetch.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface Call {
2222
request?: object | string;
2323
method?: string;
2424
headers: Headers;
25+
fullRequest?: RequestInit;
2526
}
2627

2728
export interface Route {
@@ -59,7 +60,8 @@ const fakeFetch: typeof fetch = (
5960
calls.push({
6061
request: requestBody,
6162
method: request?.method,
62-
headers
63+
headers,
64+
fullRequest: request
6365
});
6466

6567
return Promise.resolve(

packages/data-connect/src/api/DataConnect.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
import { AppCheckInternalComponentName } from '@firebase/app-check-interop-types';
2525
import { FirebaseAuthInternalName } from '@firebase/auth-interop-types';
2626
import { Provider } from '@firebase/component';
27+
import { isCloudWorkstation, pingServer } from '@firebase/util';
2728

2829
import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider';
2930
import { Code, DataConnectError } from '../core/error';
@@ -237,6 +238,10 @@ export function connectDataConnectEmulator(
237238
port?: number,
238239
sslEnabled = false
239240
): void {
241+
// Workaround to get cookies in Firebase Studio
242+
if (isCloudWorkstation(host)) {
243+
void pingServer(`https://${host}${port ? `:${port}` : ''}`);
244+
}
240245
dc.enableEmulator({ host, port, sslEnabled });
241246
}
242247

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

Lines changed: 12 additions & 6 deletions
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

Lines changed: 6 additions & 2 deletions
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)