Skip to content

Commit 136bb73

Browse files
Merge main into release
2 parents b204e71 + 080a90d commit 136bb73

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+602
-65
lines changed

.changeset/nice-plants-thank.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@firebase/database-compat": patch
3+
"@firebase/database": patch
4+
"@firebase/firestore": patch
5+
"@firebase/functions": patch
6+
"@firebase/storage": patch
7+
"@firebase/util": patch
8+
---
9+
10+
Auto Enable SSL for Firebase Studio

.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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ export function isBrowserExtension(): boolean;
269269
// @public
270270
export function isCloudflareWorker(): boolean;
271271

272+
// @public
273+
export function isCloudWorkstation(host: string): boolean;
274+
272275
// Warning: (ae-missing-release-tag) "isElectron" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
273276
//
274277
// @public
@@ -395,6 +398,9 @@ export function ordinal(i: number): string;
395398
// @public (undocumented)
396399
export type PartialObserver<T> = Partial<Observer<T>>;
397400

401+
// @public
402+
export function pingServer(endpoint: string): Promise<boolean>;
403+
398404
// Warning: (ae-internal-missing-underscore) The name "promiseWithTimeout" should be prefixed with an underscore because the declaration is marked as @internal
399405
//
400406
// @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,

0 commit comments

Comments
 (0)