Skip to content

Support logging frameworks. #4693

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

Merged
merged 13 commits into from
Apr 2, 2021
Merged
2 changes: 1 addition & 1 deletion packages-exp/auth-compat-exp/src/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('auth compat', () => {
app = { options: { apiKey: 'api-key' } } as FirebaseApp;
underlyingAuth = new exp.AuthImpl(app, {
apiKey: 'api-key'
} as exp.Config);
} as exp.ConfigInternal);
sinon.stub(underlyingAuth, '_initializeWithPersistence');

providerStub = sinon.createStubInstance(Provider);
Expand Down
2 changes: 1 addition & 1 deletion packages-exp/auth-compat-exp/src/popup_redirect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('popup_redirect/CompatPopupRedirectResolver', () => {
const app = { options: { apiKey: 'api-key' } } as FirebaseApp;
auth = new exp.AuthImpl(app, {
apiKey: 'api-key'
} as exp.Config);
} as exp.ConfigInternal);
});

afterEach(() => {
Expand Down
2 changes: 1 addition & 1 deletion packages-exp/auth-exp/internal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export {
AuthEventType
} from '../src/model/popup_redirect';
export { UserCredentialInternal, UserParameters } from '../src/model/user';
export { AuthInternal } from '../src/model/auth';
export { AuthInternal, ConfigInternal } from '../src/model/auth';
export { DefaultConfig, AuthImpl, _castAuth } from '../src/core/auth/auth_impl';

export { ClientPlatform, _getClientVersion } from '../src/core/util/version';
Expand Down
26 changes: 25 additions & 1 deletion packages-exp/auth-exp/src/api/authentication/token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
import { expect, use } from 'chai';
import * as chaiAsPromised from 'chai-as-promised';

import { FirebaseError, querystringDecode } from '@firebase/util';
import { FirebaseError, getUA, querystringDecode } from '@firebase/util';

import { HttpHeader } from '../';
import { testAuth, TestAuth } from '../../../test/helpers/mock_auth';
import * as fetch from '../../../test/helpers/mock_fetch';
import { ServerError } from '../errors';
import { Endpoint, requestStsToken } from './token';
import { SDK_VERSION } from '@firebase/app-exp';
import { _getBrowserName } from '../../core/util/browser';

use(chaiAsPromised);

Expand Down Expand Up @@ -66,6 +68,28 @@ describe('requestStsToken', () => {
);
});

it('should set the framework in clientVersion if logged', async () => {
const mock = fetch.mock(endpoint, {
'access_token': 'new-access-token',
'expires_in': '3600',
'refresh_token': 'new-refresh-token'
});

auth._logFramework('Mythical');
await requestStsToken(auth, 'old-refresh-token');
expect(mock.calls[0].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Mythical`
);

// If a new framework is logged, the client version header should change as well.
auth._logFramework('Magical');
await requestStsToken(auth, 'old-refresh-token');
expect(mock.calls[1].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
// frameworks should be sorted alphabetically
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Magical,Mythical`
);
});

it('should handle errors', async () => {
const mock = fetch.mock(
endpoint,
Expand Down
5 changes: 3 additions & 2 deletions packages-exp/auth-exp/src/api/authentication/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '../index';
import { FetchProvider } from '../../core/util/fetch_provider';
import { Auth } from '../../model/public_types';
import { AuthInternal } from '../../model/auth';

export const enum Endpoint {
TOKEN = '/v1/token'
Expand Down Expand Up @@ -56,7 +57,7 @@ export async function requestStsToken(
'grant_type': 'refresh_token',
'refresh_token': refreshToken
}).slice(1);
const { tokenApiHost, apiKey, sdkClientVersion } = auth.config;
const { tokenApiHost, apiKey } = auth.config;
const url = _getFinalTarget(
auth,
tokenApiHost,
Expand All @@ -67,7 +68,7 @@ export async function requestStsToken(
return FetchProvider.fetch()(url, {
method: HttpMethod.POST,
headers: {
'X-Client-Version': sdkClientVersion,
'X-Client-Version': (auth as AuthInternal)._getSdkClientVersion(),
'Content-Type': 'application/x-www-form-urlencoded'
},
body
Expand Down
29 changes: 28 additions & 1 deletion packages-exp/auth-exp/src/api/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import * as sinon from 'sinon';
import { useFakeTimers } from 'sinon';
import * as sinonChai from 'sinon-chai';

import { FirebaseError } from '@firebase/util';
import { FirebaseError, getUA } from '@firebase/util';

import { mockEndpoint } from '../../test/helpers/api/helper';
import { testAuth, TestAuth } from '../../test/helpers/mock_auth';
Expand All @@ -38,6 +38,8 @@ import {
_addTidIfNecessary
} from './';
import { ServerError } from './errors';
import { SDK_VERSION } from '@firebase/app-exp';
import { _getBrowserName } from '../core/util/browser';

use(sinonChai);
use(chaiAsPromised);
Expand Down Expand Up @@ -92,6 +94,31 @@ describe('api/_performApiRequest', () => {
);
});

it('should set the framework in clientVersion if logged', async () => {
auth._logFramework('Mythical');
const mock = mockEndpoint(Endpoint.SIGN_UP, serverResponse);
const response = await _performApiRequest<
typeof request,
typeof serverResponse
>(auth, HttpMethod.POST, Endpoint.SIGN_UP, request);
expect(response).to.eql(serverResponse);
expect(mock.calls[0].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Mythical`
);

// If a new framework is logged, the client version header should change as well.
auth._logFramework('Magical');
const response2 = await _performApiRequest<
typeof request,
typeof serverResponse
>(auth, HttpMethod.POST, Endpoint.SIGN_UP, request);
expect(response2).to.eql(serverResponse);
expect(mock.calls[1].headers!.get(HttpHeader.X_CLIENT_VERSION)).to.eq(
// frameworks should be sorted alphabetically
`${_getBrowserName(getUA())}/JsCore/${SDK_VERSION}/Magical,Mythical`
);
});

it('should translate server errors to auth errors', async () => {
const mock = mockEndpoint(
Endpoint.SIGN_UP,
Expand Down
9 changes: 6 additions & 3 deletions packages-exp/auth-exp/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { Delay } from '../core/util/delay';
import { _emulatorUrl } from '../core/util/emulator';
import { FetchProvider } from '../core/util/fetch_provider';
import { Auth } from '../model/public_types';
import { AuthInternal } from '../model/auth';
import { AuthInternal, ConfigInternal } from '../model/auth';
import { IdTokenResponse, TaggedWithTokenResponse } from '../model/id_token';
import { IdTokenMfaResponse } from './authentication/mfa';
import { SERVER_ERROR_MAP, ServerError, ServerErrorMap } from './errors';
Expand Down Expand Up @@ -104,7 +104,10 @@ export async function _performApiRequest<T, V>(

const headers = new (FetchProvider.headers())();
headers.set(HttpHeader.CONTENT_TYPE, 'application/json');
headers.set(HttpHeader.X_CLIENT_VERSION, auth.config.sdkClientVersion);
headers.set(
HttpHeader.X_CLIENT_VERSION,
(auth as AuthInternal)._getSdkClientVersion()
);

if (auth.languageCode) {
headers.set(HttpHeader.X_FIREBASE_LOCALE, auth.languageCode);
Expand Down Expand Up @@ -209,7 +212,7 @@ export function _getFinalTarget(
return `${auth.config.apiScheme}://${base}`;
}

return _emulatorUrl(auth.config, base);
return _emulatorUrl(auth.config as ConfigInternal, base);
}

class NetworkTimeout<T> {
Expand Down
3 changes: 3 additions & 0 deletions packages-exp/auth-exp/src/core/auth/auth_impl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import * as navigator from '../util/navigator';
import * as reload from '../user/reload';
import { AuthImpl, DefaultConfig } from './auth_impl';
import { _initializeAuthInstance } from './initialize';
import { ClientPlatform } from '../util/version';

use(sinonChai);
use(chaiAsPromised);
Expand All @@ -57,6 +58,7 @@ describe('core/auth/auth_impl', () => {
apiHost: DefaultConfig.API_HOST,
apiScheme: DefaultConfig.API_SCHEME,
tokenApiHost: DefaultConfig.TOKEN_API_HOST,
clientPlatform: ClientPlatform.BROWSER,
sdkClientVersion: 'v'
});

Expand Down Expand Up @@ -434,6 +436,7 @@ describe('core/auth/auth_impl', () => {
apiHost: DefaultConfig.API_HOST,
apiScheme: DefaultConfig.API_SCHEME,
tokenApiHost: DefaultConfig.TOKEN_API_HOST,
clientPlatform: ClientPlatform.BROWSER,
sdkClientVersion: 'v'
});

Expand Down
25 changes: 25 additions & 0 deletions packages-exp/auth-exp/src/core/auth/auth_impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { _reloadWithoutSaving } from '../user/reload';
import { _assert } from '../util/assert';
import { _getInstance } from '../util/instantiator';
import { _getUserLanguage } from '../util/navigator';
import { _getClientVersion } from '../util/version';

interface AsyncAction {
(): Promise<void>;
Expand Down Expand Up @@ -107,6 +108,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
public readonly config: ConfigInternal
) {
this.name = app.name;
this.clientVersion = config.sdkClientVersion;
}

_initializeWithPersistence(
Expand Down Expand Up @@ -556,6 +558,29 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
_assert(this.persistenceManager, this, AuthErrorCode.INTERNAL_ERROR);
return this.persistenceManager;
}

private frameworks: string[] = [];
private clientVersion: string;
_logFramework(framework: string): void {
if (this.frameworks.includes(framework)) {
return;
}
this.frameworks.push(framework);

// Sort alphabetically so that "FirebaseCore-web,FirebaseUI-web" and
// "FirebaseUI-web,FirebaseCore-web" aren't viewed as different.
this.frameworks.sort();
this.clientVersion = _getClientVersion(
this.config.clientPlatform,
this._getFrameworks()
);
}
_getFrameworks(): readonly string[] {
return this.frameworks;
}
_getSdkClientVersion(): string {
return this.clientVersion;
}
}

/**
Expand Down
6 changes: 5 additions & 1 deletion packages-exp/auth-exp/src/core/auth/initialize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,19 @@ describe('core/auth/initialize', () => {
await auth._initializationPromise;

expect(auth.name).to.eq(fakeApp.name);
const expectedClientPlatform = isNode()
? ClientPlatform.NODE
: ClientPlatform.BROWSER;
const expectedSdkClientVersion = _getClientVersion(
isNode() ? ClientPlatform.NODE : ClientPlatform.BROWSER
expectedClientPlatform
);

expect(auth.config).to.eql({
apiHost: 'identitytoolkit.googleapis.com',
apiKey: 'fake-key',
apiScheme: 'https',
authDomain: 'fake-auth-domain',
clientPlatform: expectedClientPlatform,
sdkClientVersion: expectedSdkClientVersion,
tokenApiHost: 'securetoken.googleapis.com'
});
Expand Down
6 changes: 4 additions & 2 deletions packages-exp/auth-exp/src/core/auth/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/

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

import { version } from '../../../package.json';
Expand All @@ -25,6 +24,8 @@ import { _assert } from '../util/assert';
import { _getClientVersion, ClientPlatform } from '../util/version';
import { _castAuth, AuthImpl, DefaultConfig } from './auth_impl';
import { AuthInterop } from './firebase_internal';
import { ConfigInternal } from '../../model/auth';
import { Dependencies } from '../../model/public_types';
import { _initializeAuthInstance } from './initialize';

export const enum _ComponentName {
Expand Down Expand Up @@ -59,9 +60,10 @@ export function registerAuth(clientPlatform: ClientPlatform): void {
const { apiKey, authDomain } = app.options;
return (app => {
_assert(apiKey, AuthErrorCode.INVALID_API_KEY, { appName: app.name });
const config: Config = {
const config: ConfigInternal = {
apiKey,
authDomain,
clientPlatform,
apiHost: DefaultConfig.API_HOST,
tokenApiHost: DefaultConfig.TOKEN_API_HOST,
apiScheme: DefaultConfig.API_SCHEME,
Expand Down
10 changes: 8 additions & 2 deletions packages-exp/auth-exp/src/core/util/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ const enum ClientFramework {
*
* TODO: This should be set on the Auth object during initialization
*/
export function _getClientVersion(clientPlatform: ClientPlatform): string {
export function _getClientVersion(
clientPlatform: ClientPlatform,
frameworks: readonly string[] = []
): string {
let reportedPlatform: string;
switch (clientPlatform) {
case ClientPlatform.BROWSER:
Expand All @@ -60,5 +63,8 @@ export function _getClientVersion(clientPlatform: ClientPlatform): string {
default:
reportedPlatform = clientPlatform;
}
return `${reportedPlatform}/${ClientImplementation.CORE}/${SDK_VERSION}/${ClientFramework.DEFAULT}`;
const reportedFrameworks = frameworks.length
? frameworks.join(',')
: ClientFramework.DEFAULT;
return `${reportedPlatform}/${ClientImplementation.CORE}/${SDK_VERSION}/${reportedFrameworks}`;
}
9 changes: 9 additions & 0 deletions packages-exp/auth-exp/src/model/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { AuthErrorCode, AuthErrorParams } from '../core/errors';

import { PopupRedirectResolverInternal } from './popup_redirect';
import { UserInternal } from './user';
import { ClientPlatform } from '../core/util/version';

export type AppName = string;
export type ApiKey = string;
Expand All @@ -40,6 +41,11 @@ export interface ConfigInternal extends Config {
emulator?: {
url: string;
};

/**
* @readonly
*/
clientPlatform: ClientPlatform;
}

export interface AuthInternal extends Auth {
Expand All @@ -64,6 +70,9 @@ export interface AuthInternal extends Auth {
_startProactiveRefresh(): void;
_stopProactiveRefresh(): void;
_getPersistence(): string;
_logFramework(framework: string): void;
_getFrameworks(): readonly string[];
_getSdkClientVersion(): string;

readonly name: AppName;
readonly config: ConfigInternal;
Expand Down
19 changes: 19 additions & 0 deletions packages-exp/auth-exp/src/platform_browser/iframe/iframe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,25 @@ describe('platform_browser/iframe/iframe', () => {
expect(iframeSettings.dontclear).to.be.true;
});

it('sets a single framework if logged', async () => {
auth._logFramework('Magical');
await _openIframe(auth);
expect(iframeSettings.url).to.eq(
`https://${TEST_AUTH_DOMAIN}/__/auth/iframe?apiKey=${TEST_KEY}&appName=test-app&v=${SDK_VERSION}&fw=Magical`
);
});

it('sets multiple frameworks comma-separated if logged', async () => {
auth._logFramework('Mythical');
auth._logFramework('Magical');
auth._logFramework('Magical'); // Duplicate, should be ignored
await _openIframe(auth);
expect(iframeSettings.url).to.eq(
// fw should be a comma-separated list sorted alphabetically:
`https://${TEST_AUTH_DOMAIN}/__/auth/iframe?apiKey=${TEST_KEY}&appName=test-app&v=${SDK_VERSION}&fw=Magical%2CMythical`
);
});

context('on load callback', () => {
let iframe: sinon.SinonStubbedInstance<gapi.iframes.Iframe>;
let clearTimeoutStub: sinon.SinonStub;
Expand Down
Loading