Skip to content

Commit a4b794b

Browse files
authored
Support logging frameworks. (#4693)
* Support logging frameworks. * Fix typo. * Add fw parameter to iframe. * Fix linter. * Update register.ts * Update register.ts * Fix URL encoding. * Add ConfigInternal to auth-compat. * Fix type error again.
1 parent c14e052 commit a4b794b

File tree

16 files changed

+158
-19
lines changed

16 files changed

+158
-19
lines changed

packages-exp/auth-compat-exp/src/auth.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe('auth compat', () => {
3838
app = { options: { apiKey: 'api-key' } } as FirebaseApp;
3939
underlyingAuth = new exp.AuthImpl(app, {
4040
apiKey: 'api-key'
41-
} as exp.Config);
41+
} as exp.ConfigInternal);
4242
sinon.stub(underlyingAuth, '_initializeWithPersistence');
4343

4444
providerStub = sinon.createStubInstance(Provider);

packages-exp/auth-compat-exp/src/popup_redirect.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('popup_redirect/CompatPopupRedirectResolver', () => {
3434
const app = { options: { apiKey: 'api-key' } } as FirebaseApp;
3535
auth = new exp.AuthImpl(app, {
3636
apiKey: 'api-key'
37-
} as exp.Config);
37+
} as exp.ConfigInternal);
3838
});
3939

4040
afterEach(() => {

packages-exp/auth-exp/internal/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export {
3232
AuthEventType
3333
} from '../src/model/popup_redirect';
3434
export { UserCredentialInternal, UserParameters } from '../src/model/user';
35-
export { AuthInternal } from '../src/model/auth';
35+
export { AuthInternal, ConfigInternal } from '../src/model/auth';
3636
export { DefaultConfig, AuthImpl, _castAuth } from '../src/core/auth/auth_impl';
3737

3838
export { ClientPlatform, _getClientVersion } from '../src/core/util/version';

packages-exp/auth-exp/src/api/authentication/token.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
import { expect, use } from 'chai';
1919
import * as chaiAsPromised from 'chai-as-promised';
2020

21-
import { FirebaseError, querystringDecode } from '@firebase/util';
21+
import { FirebaseError, getUA, querystringDecode } from '@firebase/util';
2222

2323
import { HttpHeader } from '../';
2424
import { testAuth, TestAuth } from '../../../test/helpers/mock_auth';
2525
import * as fetch from '../../../test/helpers/mock_fetch';
2626
import { ServerError } from '../errors';
2727
import { Endpoint, requestStsToken } from './token';
28+
import { SDK_VERSION } from '@firebase/app-exp';
29+
import { _getBrowserName } from '../../core/util/browser';
2830

2931
use(chaiAsPromised);
3032

@@ -66,6 +68,28 @@ describe('requestStsToken', () => {
6668
);
6769
});
6870

71+
it('should set the framework in clientVersion if logged', async () => {
72+
const mock = fetch.mock(endpoint, {
73+
'access_token': 'new-access-token',
74+
'expires_in': '3600',
75+
'refresh_token': 'new-refresh-token'
76+
});
77+
78+
auth._logFramework('Mythical');
79+
await requestStsToken(auth, 'old-refresh-token');
80+
expect(mock.calls[0].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
81+
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Mythical`
82+
);
83+
84+
// If a new framework is logged, the client version header should change as well.
85+
auth._logFramework('Magical');
86+
await requestStsToken(auth, 'old-refresh-token');
87+
expect(mock.calls[1].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
88+
// frameworks should be sorted alphabetically
89+
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Magical,Mythical`
90+
);
91+
});
92+
6993
it('should handle errors', async () => {
7094
const mock = fetch.mock(
7195
endpoint,

packages-exp/auth-exp/src/api/authentication/token.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from '../index';
2727
import { FetchProvider } from '../../core/util/fetch_provider';
2828
import { Auth } from '../../model/public_types';
29+
import { AuthInternal } from '../../model/auth';
2930

3031
export const enum Endpoint {
3132
TOKEN = '/v1/token'
@@ -56,7 +57,7 @@ export async function requestStsToken(
5657
'grant_type': 'refresh_token',
5758
'refresh_token': refreshToken
5859
}).slice(1);
59-
const { tokenApiHost, apiKey, sdkClientVersion } = auth.config;
60+
const { tokenApiHost, apiKey } = auth.config;
6061
const url = _getFinalTarget(
6162
auth,
6263
tokenApiHost,
@@ -67,7 +68,7 @@ export async function requestStsToken(
6768
return FetchProvider.fetch()(url, {
6869
method: HttpMethod.POST,
6970
headers: {
70-
'X-Client-Version': sdkClientVersion,
71+
'X-Client-Version': (auth as AuthInternal)._getSdkClientVersion(),
7172
'Content-Type': 'application/x-www-form-urlencoded'
7273
},
7374
body

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import * as sinon from 'sinon';
2121
import { useFakeTimers } from 'sinon';
2222
import * as sinonChai from 'sinon-chai';
2323

24-
import { FirebaseError } from '@firebase/util';
24+
import { FirebaseError, getUA } from '@firebase/util';
2525

2626
import { mockEndpoint } from '../../test/helpers/api/helper';
2727
import { testAuth, TestAuth } from '../../test/helpers/mock_auth';
@@ -38,6 +38,8 @@ import {
3838
_addTidIfNecessary
3939
} from './';
4040
import { ServerError } from './errors';
41+
import { SDK_VERSION } from '@firebase/app-exp';
42+
import { _getBrowserName } from '../core/util/browser';
4143

4244
use(sinonChai);
4345
use(chaiAsPromised);
@@ -92,6 +94,31 @@ describe('api/_performApiRequest', () => {
9294
);
9395
});
9496

97+
it('should set the framework in clientVersion if logged', async () => {
98+
auth._logFramework('Mythical');
99+
const mock = mockEndpoint(Endpoint.SIGN_UP, serverResponse);
100+
const response = await _performApiRequest<
101+
typeof request,
102+
typeof serverResponse
103+
>(auth, HttpMethod.POST, Endpoint.SIGN_UP, request);
104+
expect(response).to.eql(serverResponse);
105+
expect(mock.calls[0].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
106+
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Mythical`
107+
);
108+
109+
// If a new framework is logged, the client version header should change as well.
110+
auth._logFramework('Magical');
111+
const response2 = await _performApiRequest<
112+
typeof request,
113+
typeof serverResponse
114+
>(auth, HttpMethod.POST, Endpoint.SIGN_UP, request);
115+
expect(response2).to.eql(serverResponse);
116+
expect(mock.calls[1].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
117+
// frameworks should be sorted alphabetically
118+
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Magical,Mythical`
119+
);
120+
});
121+
95122
it('should translate server errors to auth errors', async () => {
96123
const mock = mockEndpoint(
97124
Endpoint.SIGN_UP,

packages-exp/auth-exp/src/api/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { Delay } from '../core/util/delay';
2323
import { _emulatorUrl } from '../core/util/emulator';
2424
import { FetchProvider } from '../core/util/fetch_provider';
2525
import { Auth } from '../model/public_types';
26-
import { AuthInternal } from '../model/auth';
26+
import { AuthInternal, ConfigInternal } from '../model/auth';
2727
import { IdTokenResponse, TaggedWithTokenResponse } from '../model/id_token';
2828
import { IdTokenMfaResponse } from './authentication/mfa';
2929
import { SERVER_ERROR_MAP, ServerError, ServerErrorMap } from './errors';
@@ -104,7 +104,10 @@ export async function _performApiRequest<T, V>(
104104

105105
const headers = new (FetchProvider.headers())();
106106
headers.set(HttpHeader.CONTENT_TYPE, 'application/json');
107-
headers.set(HttpHeader.X_CLIENT_VERSION, auth.config.sdkClientVersion);
107+
headers.set(
108+
HttpHeader.X_CLIENT_VERSION,
109+
(auth as AuthInternal)._getSdkClientVersion()
110+
);
108111

109112
if (auth.languageCode) {
110113
headers.set(HttpHeader.X_FIREBASE_LOCALE, auth.languageCode);
@@ -209,7 +212,7 @@ export function _getFinalTarget(
209212
return `${auth.config.apiScheme}://${base}`;
210213
}
211214

212-
return _emulatorUrl(auth.config, base);
215+
return _emulatorUrl(auth.config as ConfigInternal, base);
213216
}
214217

215218
class NetworkTimeout<T> {

packages-exp/auth-exp/src/core/auth/auth_impl.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import * as navigator from '../util/navigator';
3333
import * as reload from '../user/reload';
3434
import { AuthImpl, DefaultConfig } from './auth_impl';
3535
import { _initializeAuthInstance } from './initialize';
36+
import { ClientPlatform } from '../util/version';
3637

3738
use(sinonChai);
3839
use(chaiAsPromised);
@@ -57,6 +58,7 @@ describe('core/auth/auth_impl', () => {
5758
apiHost: DefaultConfig.API_HOST,
5859
apiScheme: DefaultConfig.API_SCHEME,
5960
tokenApiHost: DefaultConfig.TOKEN_API_HOST,
61+
clientPlatform: ClientPlatform.BROWSER,
6062
sdkClientVersion: 'v'
6163
});
6264

@@ -434,6 +436,7 @@ describe('core/auth/auth_impl', () => {
434436
apiHost: DefaultConfig.API_HOST,
435437
apiScheme: DefaultConfig.API_SCHEME,
436438
tokenApiHost: DefaultConfig.TOKEN_API_HOST,
439+
clientPlatform: ClientPlatform.BROWSER,
437440
sdkClientVersion: 'v'
438441
});
439442

packages-exp/auth-exp/src/core/auth/auth_impl.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { _reloadWithoutSaving } from '../user/reload';
5757
import { _assert } from '../util/assert';
5858
import { _getInstance } from '../util/instantiator';
5959
import { _getUserLanguage } from '../util/navigator';
60+
import { _getClientVersion } from '../util/version';
6061

6162
interface AsyncAction {
6263
(): Promise<void>;
@@ -107,6 +108,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
107108
public readonly config: ConfigInternal
108109
) {
109110
this.name = app.name;
111+
this.clientVersion = config.sdkClientVersion;
110112
}
111113

112114
_initializeWithPersistence(
@@ -556,6 +558,29 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
556558
_assert(this.persistenceManager, this, AuthErrorCode.INTERNAL_ERROR);
557559
return this.persistenceManager;
558560
}
561+
562+
private frameworks: string[] = [];
563+
private clientVersion: string;
564+
_logFramework(framework: string): void {
565+
if (this.frameworks.includes(framework)) {
566+
return;
567+
}
568+
this.frameworks.push(framework);
569+
570+
// Sort alphabetically so that "FirebaseCore-web,FirebaseUI-web" and
571+
// "FirebaseUI-web,FirebaseCore-web" aren't viewed as different.
572+
this.frameworks.sort();
573+
this.clientVersion = _getClientVersion(
574+
this.config.clientPlatform,
575+
this._getFrameworks()
576+
);
577+
}
578+
_getFrameworks(): readonly string[] {
579+
return this.frameworks;
580+
}
581+
_getSdkClientVersion(): string {
582+
return this.clientVersion;
583+
}
559584
}
560585

561586
/**

packages-exp/auth-exp/src/core/auth/initialize.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,19 @@ describe('core/auth/initialize', () => {
151151
await auth._initializationPromise;
152152

153153
expect(auth.name).to.eq(fakeApp.name);
154+
const expectedClientPlatform = isNode()
155+
? ClientPlatform.NODE
156+
: ClientPlatform.BROWSER;
154157
const expectedSdkClientVersion = _getClientVersion(
155-
isNode() ? ClientPlatform.NODE : ClientPlatform.BROWSER
158+
expectedClientPlatform
156159
);
157160

158161
expect(auth.config).to.eql({
159162
apiHost: 'identitytoolkit.googleapis.com',
160163
apiKey: 'fake-key',
161164
apiScheme: 'https',
162165
authDomain: 'fake-auth-domain',
166+
clientPlatform: expectedClientPlatform,
163167
sdkClientVersion: expectedSdkClientVersion,
164168
tokenApiHost: 'securetoken.googleapis.com'
165169
});

packages-exp/auth-exp/src/core/auth/register.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717

1818
import { _registerComponent, registerVersion } from '@firebase/app-exp';
19-
import { Config, Dependencies } from '../../model/public_types';
2019
import { Component, ComponentType } from '@firebase/component';
2120

2221
import { version } from '../../../package.json';
@@ -25,6 +24,8 @@ import { _assert } from '../util/assert';
2524
import { _getClientVersion, ClientPlatform } from '../util/version';
2625
import { _castAuth, AuthImpl, DefaultConfig } from './auth_impl';
2726
import { AuthInterop } from './firebase_internal';
27+
import { ConfigInternal } from '../../model/auth';
28+
import { Dependencies } from '../../model/public_types';
2829
import { _initializeAuthInstance } from './initialize';
2930

3031
export const enum _ComponentName {
@@ -59,9 +60,10 @@ export function registerAuth(clientPlatform: ClientPlatform): void {
5960
const { apiKey, authDomain } = app.options;
6061
return (app => {
6162
_assert(apiKey, AuthErrorCode.INVALID_API_KEY, { appName: app.name });
62-
const config: Config = {
63+
const config: ConfigInternal = {
6364
apiKey,
6465
authDomain,
66+
clientPlatform,
6567
apiHost: DefaultConfig.API_HOST,
6668
tokenApiHost: DefaultConfig.TOKEN_API_HOST,
6769
apiScheme: DefaultConfig.API_SCHEME,

packages-exp/auth-exp/src/core/util/version.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ const enum ClientFramework {
4444
*
4545
* TODO: This should be set on the Auth object during initialization
4646
*/
47-
export function _getClientVersion(clientPlatform: ClientPlatform): string {
47+
export function _getClientVersion(
48+
clientPlatform: ClientPlatform,
49+
frameworks: readonly string[] = []
50+
): string {
4851
let reportedPlatform: string;
4952
switch (clientPlatform) {
5053
case ClientPlatform.BROWSER:
@@ -60,5 +63,8 @@ export function _getClientVersion(clientPlatform: ClientPlatform): string {
6063
default:
6164
reportedPlatform = clientPlatform;
6265
}
63-
return `${reportedPlatform}/${ClientImplementation.CORE}/${SDK_VERSION}/${ClientFramework.DEFAULT}`;
66+
const reportedFrameworks = frameworks.length
67+
? frameworks.join(',')
68+
: ClientFramework.DEFAULT;
69+
return `${reportedPlatform}/${ClientImplementation.CORE}/${SDK_VERSION}/${reportedFrameworks}`;
6470
}

packages-exp/auth-exp/src/model/auth.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { AuthErrorCode, AuthErrorParams } from '../core/errors';
2828

2929
import { PopupRedirectResolverInternal } from './popup_redirect';
3030
import { UserInternal } from './user';
31+
import { ClientPlatform } from '../core/util/version';
3132

3233
export type AppName = string;
3334
export type ApiKey = string;
@@ -40,6 +41,11 @@ export interface ConfigInternal extends Config {
4041
emulator?: {
4142
url: string;
4243
};
44+
45+
/**
46+
* @readonly
47+
*/
48+
clientPlatform: ClientPlatform;
4349
}
4450

4551
export interface AuthInternal extends Auth {
@@ -64,6 +70,9 @@ export interface AuthInternal extends Auth {
6470
_startProactiveRefresh(): void;
6571
_stopProactiveRefresh(): void;
6672
_getPersistence(): string;
73+
_logFramework(framework: string): void;
74+
_getFrameworks(): readonly string[];
75+
_getSdkClientVersion(): string;
6776

6877
readonly name: AppName;
6978
readonly config: ConfigInternal;

packages-exp/auth-exp/src/platform_browser/iframe/iframe.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@ describe('platform_browser/iframe/iframe', () => {
8989
expect(iframeSettings.dontclear).to.be.true;
9090
});
9191

92+
it('sets a single framework if logged', async () => {
93+
auth._logFramework('Magical');
94+
await _openIframe(auth);
95+
expect(iframeSettings.url).to.eq(
96+
`https://${TEST_AUTH_DOMAIN}/__/auth/iframe?apiKey=${TEST_KEY}&appName=test-app&v=${SDK_VERSION}&fw=Magical`
97+
);
98+
});
99+
100+
it('sets multiple frameworks comma-separated if logged', async () => {
101+
auth._logFramework('Mythical');
102+
auth._logFramework('Magical');
103+
auth._logFramework('Magical'); // Duplicate, should be ignored
104+
await _openIframe(auth);
105+
expect(iframeSettings.url).to.eq(
106+
// fw should be a comma-separated list sorted alphabetically:
107+
`https://${TEST_AUTH_DOMAIN}/__/auth/iframe?apiKey=${TEST_KEY}&appName=test-app&v=${SDK_VERSION}&fw=Magical%2CMythical`
108+
);
109+
});
110+
92111
context('on load callback', () => {
93112
let iframe: sinon.SinonStubbedInstance<gapi.iframes.Iframe>;
94113
let clearTimeoutStub: sinon.SinonStub;

0 commit comments

Comments
 (0)