Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Auth] Add gmpid header #5799

Merged
merged 2 commits into from Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/poor-files-learn.md
@@ -0,0 +1,5 @@
---
"@firebase/auth": patch
---

Add X-Firebase-gmpid header to requests
23 changes: 22 additions & 1 deletion packages/auth/src/api/authentication/token.test.ts
Expand Up @@ -15,6 +15,7 @@
* limitations under the License.
*/

import * as sinon from 'sinon';
import { expect, use } from 'chai';
import chaiAsPromised from 'chai-as-promised';

Expand All @@ -41,7 +42,10 @@ describe('requestStsToken', () => {
fetch.setUp();
});

afterEach(fetch.tearDown);
afterEach(() => {
fetch.tearDown();
sinon.restore();
});

it('should POST to the correct endpoint', async () => {
const mock = fetch.mock(endpoint, {
Expand Down Expand Up @@ -90,6 +94,23 @@ describe('requestStsToken', () => {
);
});

it('should include whatever headers come from auth impl', async () => {
sinon.stub(auth, '_getAdditionalHeaders').returns(Promise.resolve({
'look-at-me-im-a-header': 'header-value',
'anotherheader': 'header-value-2',
}));

const mock = fetch.mock(endpoint, {
'access_token': 'new-access-token',
'expires_in': '3600',
'refresh_token': 'new-refresh-token'
});
await requestStsToken(auth, 'old-refresh-token');

expect(mock.calls[0].headers.get('look-at-me-im-a-header')).to.eq('header-value');
expect(mock.calls[0].headers.get('anotherheader')).to.eq('header-value-2');
});

it('should handle errors', async () => {
const mock = fetch.mock(
endpoint,
Expand Down
13 changes: 7 additions & 6 deletions packages/auth/src/api/authentication/token.ts
Expand Up @@ -22,7 +22,8 @@ import { querystring } from '@firebase/util';
import {
_getFinalTarget,
_performFetchWithErrorHandling,
HttpMethod
HttpMethod,
HttpHeader
} from '../index';
import { FetchProvider } from '../../core/util/fetch_provider';
import { Auth } from '../../model/public_types';
Expand Down Expand Up @@ -52,7 +53,7 @@ export async function requestStsToken(
const response = await _performFetchWithErrorHandling<RequestStsTokenServerResponse>(
auth,
{},
() => {
async () => {
const body = querystring({
'grant_type': 'refresh_token',
'refresh_token': refreshToken
Expand All @@ -65,12 +66,12 @@ export async function requestStsToken(
`key=${apiKey}`
);

const headers = await (auth as AuthInternal)._getAdditionalHeaders();
headers[HttpHeader.CONTENT_TYPE] = 'application/x-www-form-urlencoded';

return FetchProvider.fetch()(url, {
method: HttpMethod.POST,
headers: {
'X-Client-Version': (auth as AuthInternal)._getSdkClientVersion(),
'Content-Type': 'application/x-www-form-urlencoded'
},
headers,
body
});
}
Expand Down
15 changes: 15 additions & 0 deletions packages/auth/src/api/index.test.ts
Expand Up @@ -94,6 +94,21 @@ describe('api/_performApiRequest', () => {
);
});

it('should include whatever headers the auth impl attaches', async () => {
sinon.stub(auth, '_getAdditionalHeaders').returns(Promise.resolve({
'look-at-me-im-a-header': 'header-value',
'anotherheader': 'header-value-2',
}));

const mock = mockEndpoint(Endpoint.SIGN_UP, serverResponse);
await _performApiRequest<
typeof request,
typeof serverResponse
>(auth, HttpMethod.POST, Endpoint.SIGN_UP, request);
expect(mock.calls[0].headers.get('look-at-me-im-a-header')).to.eq('header-value');
expect(mock.calls[0].headers.get('anotherheader')).to.eq('header-value-2');
});

it('should set the framework in clientVersion if logged', async () => {
auth._logFramework('Mythical');
const mock = mockEndpoint(Endpoint.SIGN_UP, serverResponse);
Expand Down
15 changes: 6 additions & 9 deletions packages/auth/src/api/index.ts
Expand Up @@ -36,7 +36,8 @@ export const enum HttpMethod {
export const enum HttpHeader {
CONTENT_TYPE = 'Content-Type',
X_FIREBASE_LOCALE = 'X-Firebase-Locale',
X_CLIENT_VERSION = 'X-Client-Version'
X_CLIENT_VERSION = 'X-Client-Version',
X_FIREBASE_GMPID = 'X-Firebase-gmpid'
}

export const enum Endpoint {
Expand Down Expand Up @@ -84,7 +85,7 @@ export async function _performApiRequest<T, V>(
request?: T,
customErrorMap: Partial<ServerErrorMap<ServerError>> = {}
): Promise<V> {
return _performFetchWithErrorHandling(auth, customErrorMap, () => {
return _performFetchWithErrorHandling(auth, customErrorMap, async () => {
let body = {};
let params = {};
if (request) {
Expand All @@ -102,15 +103,11 @@ export async function _performApiRequest<T, V>(
...params
}).slice(1);

const headers = new (FetchProvider.headers())();
headers.set(HttpHeader.CONTENT_TYPE, 'application/json');
headers.set(
HttpHeader.X_CLIENT_VERSION,
(auth as AuthInternal)._getSdkClientVersion()
);
const headers = await (auth as AuthInternal)._getAdditionalHeaders();
headers[HttpHeader.CONTENT_TYPE] = 'application/json';

if (auth.languageCode) {
headers.set(HttpHeader.X_FIREBASE_LOCALE, auth.languageCode);
headers[HttpHeader.X_FIREBASE_LOCALE] = auth.languageCode;
}

return FetchProvider.fetch()(
Expand Down
16 changes: 16 additions & 0 deletions packages/auth/src/core/auth/auth_impl.test.ts
Expand Up @@ -460,4 +460,20 @@ describe('core/auth/auth_impl', () => {
expect(spy).not.to.have.been.called;
});
});

context ('#_getAdditionalHeaders', () => {
it('always adds the client version', async () => {
expect(await auth._getAdditionalHeaders()).to.eql({
'X-Client-Version': 'v',
});
});

it('adds the gmp app ID if available', async () => {
auth.app.options.appId = 'app-id';
expect(await auth._getAdditionalHeaders()).to.eql({
'X-Client-Version': 'v',
'X-Firebase-gmpid': 'app-id',
});
});
});
});
12 changes: 10 additions & 2 deletions packages/auth/src/core/auth/auth_impl.ts
Expand Up @@ -58,6 +58,7 @@ import { _assert } from '../util/assert';
import { _getInstance } from '../util/instantiator';
import { _getUserLanguage } from '../util/navigator';
import { _getClientVersion } from '../util/version';
import { HttpHeader } from '../../api';

interface AsyncAction {
(): Promise<void>;
Expand Down Expand Up @@ -577,8 +578,15 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
_getFrameworks(): readonly string[] {
return this.frameworks;
}
_getSdkClientVersion(): string {
return this.clientVersion;
async _getAdditionalHeaders(): Promise<Record<string, string>> {
// Additional headers on every request
const headers: Record<string, string> = {
[HttpHeader.X_CLIENT_VERSION]: this.clientVersion,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this.clientVersion always non-empty and non-null? If unsure, please add a check. Empty header values may break emulators)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just moved code (the prior revision also always sends it). It's also always populated

};
if (this.app.options.appId) {
headers[HttpHeader.X_FIREBASE_GMPID] = this.app.options.appId;
}
return headers;
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/model/auth.ts
Expand Up @@ -81,7 +81,7 @@ export interface AuthInternal extends Auth {
_getPersistence(): string;
_logFramework(framework: string): void;
_getFrameworks(): readonly string[];
_getSdkClientVersion(): string;
_getAdditionalHeaders(): Promise<Record<string, string>>;

readonly name: AppName;
readonly config: ConfigInternal;
Expand Down