voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
305 lines (282 loc) • 9.62 kB
text/typescript
/**
* @license
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect } from 'chai';
import { OperationType, ProviderId } from '../../model/public_types';
import { IdTokenResponse, IdTokenResponseKind } from '../../model/id_token';
import {
_fromIdTokenResponse,
getAdditionalUserInfo
} from './additional_user_info';
import { base64Encode } from '@firebase/util';
import { UserCredentialImpl } from './user_credential_impl';
import { AuthInternal } from '../../model/auth';
import { UserInternal, UserCredentialInternal } from '../../model/user';
import { testAuth, testUser } from '../../../test/helpers/mock_auth';
import { makeJWT } from '../../../test/helpers/jwt';
describe('core/user/additional_user_info', () => {
const userProfileWithLogin: Record<string, unknown> = {
login: 'scott',
friends: [],
netWorth: 5.0
};
const rawUserInfoWithLogin = JSON.stringify(userProfileWithLogin);
const userProfileNoLogin: Record<string, unknown> = { sample: 'data' };
const rawUserInfoNoLogin = JSON.stringify(userProfileNoLogin);
describe('_fromIdTokenResponse', () => {
describe('parses federated IDP response tokens', () => {
it('for FacebookAdditionalUserInfo', () => {
const idResponse = idTokenResponse({
providerId: ProviderId.FACEBOOK,
rawUserInfo: rawUserInfoWithLogin
});
const {
isNewUser,
providerId,
username,
profile
} = _fromIdTokenResponse(idResponse)!;
expect(isNewUser).to.be.false;
expect(providerId).to.eq(ProviderId.FACEBOOK);
expect(username).to.be.undefined;
expect(profile).to.eql(userProfileWithLogin);
});
it('for GithubAdditionalUserInfo', () => {
const idResponse = idTokenResponse({
providerId: ProviderId.GITHUB,
rawUserInfo: rawUserInfoWithLogin
});
const {
isNewUser,
providerId,
username,
profile
} = _fromIdTokenResponse(idResponse)!;
expect(isNewUser).to.be.false;
expect(providerId).to.eq(ProviderId.GITHUB);
expect(username).to.eq('scott');
expect(profile).to.eql(userProfileWithLogin);
});
it('for GoogleAdditionalUserInfo', () => {
const idResponse = idTokenResponse({
providerId: ProviderId.GOOGLE,
rawUserInfo: rawUserInfoWithLogin
});
const {
isNewUser,
providerId,
username,
profile
} = _fromIdTokenResponse(idResponse)!;
expect(isNewUser).to.be.false;
expect(providerId).to.eq(ProviderId.GOOGLE);
expect(username).to.be.undefined;
expect(profile).to.eql(userProfileWithLogin);
});
it('for TwitterAdditionalUserInfo', () => {
const idResponse = idTokenResponse({
providerId: ProviderId.TWITTER,
rawUserInfo: rawUserInfoNoLogin,
screenName: 'scott'
});
const {
isNewUser,
providerId,
username,
profile
} = _fromIdTokenResponse(idResponse)!;
expect(isNewUser).to.be.false;
expect(providerId).to.eq(ProviderId.TWITTER);
expect(username).to.eq('scott');
expect(profile).to.eql(userProfileNoLogin);
});
});
describe('parses profile data', () => {
it('for valid JSON', () => {
const idResponse = idTokenResponse({
providerId: ProviderId.FACEBOOK,
rawUserInfo: rawUserInfoWithLogin
});
expect(_fromIdTokenResponse(idResponse)!.profile).to.eql(
userProfileWithLogin
);
});
it('for missing JSON', () => {
const idResponse = idTokenResponse({ providerId: ProviderId.FACEBOOK });
expect(_fromIdTokenResponse(idResponse)!.profile).to.be.empty;
});
});
describe('determines new-user status', () => {
it('for new users by token response', () => {
const idResponse = idTokenResponse({
providerId: ProviderId.FACEBOOK,
isNewUser: true
});
expect(_fromIdTokenResponse(idResponse)!.isNewUser).to.be.true;
});
it('for new users by toolkit response kind', () => {
const idResponse = idTokenResponse({
providerId: ProviderId.FACEBOOK,
kind: IdTokenResponseKind.SignupNewUser
});
expect(_fromIdTokenResponse(idResponse)!.isNewUser).to.be.true;
});
it('for old users', () => {
const idResponse = idTokenResponse({ providerId: ProviderId.FACEBOOK });
expect(_fromIdTokenResponse(idResponse)!.isNewUser).to.be.false;
});
});
describe('creates generic AdditionalUserInfo', () => {
it('for custom auth', () => {
const idResponse = idTokenResponse({
rawUserInfo: rawUserInfoWithLogin,
idToken: makeJWT({
firebase: {
'sign_in_provider': 'custom'
}
})
});
const {
isNewUser,
providerId,
username,
profile
} = _fromIdTokenResponse(idResponse)!;
expect(isNewUser).to.be.false;
expect(providerId).to.be.null;
expect(username).to.be.undefined;
expect(profile).to.eq(profile);
});
it('for anonymous auth', () => {
const idResponse = idTokenResponse({
rawUserInfo: rawUserInfoWithLogin,
idToken: makeJWT({
firebase: {
'sign_in_provider': 'anonymous'
}
})
});
const {
isNewUser,
providerId,
username,
profile
} = _fromIdTokenResponse(idResponse)!;
expect(isNewUser).to.be.false;
expect(providerId).to.be.null;
expect(username).to.be.undefined;
expect(profile).to.eq(profile);
});
it('for missing provider IDs in response but not in token', () => {
const idToken =
'algorithm.' +
base64Encode(
JSON.stringify({
'firebase': { 'sign_in_provider': 'facebook.com' }
})
) +
'.signature';
const {
isNewUser,
providerId,
username,
profile
} = _fromIdTokenResponse(
idTokenResponse({ rawUserInfo: rawUserInfoWithLogin, idToken })
)!;
expect(isNewUser).to.be.false;
expect(providerId).to.eq(ProviderId.FACEBOOK);
expect(username).to.be.undefined;
expect(profile).to.eq(profile);
});
});
describe('returns null', () => {
it('for missing provider IDs', () => {
const idResponse = idTokenResponse({});
expect(_fromIdTokenResponse(idResponse)).to.be.null;
});
});
});
describe('getAdditionalUserInfo()', () => {
let auth: AuthInternal;
let user: UserInternal;
let cred: UserCredentialInternal;
beforeEach(async () => {
auth = await testAuth();
user = testUser(auth, 'uid');
cred = new UserCredentialImpl({
user,
providerId: null,
operationType: OperationType.SIGN_IN
});
});
it('calls through to _fromIdTokenResponse', () => {
cred._tokenResponse = idTokenResponse({
providerId: ProviderId.ANONYMOUS,
rawUserInfo: rawUserInfoWithLogin
});
const {
isNewUser,
providerId,
username,
profile
} = getAdditionalUserInfo(cred)!;
expect(isNewUser).to.be.false;
expect(providerId).to.be.null;
expect(username).to.be.undefined;
expect(profile).to.eq(profile);
});
it('calls through to _fromIdTokenResponse preserving isNewUser', () => {
cred._tokenResponse = idTokenResponse({
providerId: ProviderId.ANONYMOUS,
rawUserInfo: rawUserInfoWithLogin,
isNewUser: true
});
const {
isNewUser,
providerId,
username,
profile
} = getAdditionalUserInfo(cred)!;
expect(isNewUser).to.be.true;
expect(providerId).to.be.null;
expect(username).to.be.undefined;
expect(profile).to.eq(profile);
});
it('returns bespoke info if existing anonymous user', () => {
// Note that _tokenResponse is not set on cred
((user as unknown) as Record<string, unknown>).isAnonymous = true;
const { isNewUser, providerId, profile } = getAdditionalUserInfo(cred)!;
expect(isNewUser).to.be.false;
expect(providerId).to.be.null;
expect(profile).to.eq(profile);
});
it('returns null if not anonymous', () => {
// Note that _tokenResponse is not set on cred
expect(getAdditionalUserInfo(cred)).to.be.null;
});
});
});
function idTokenResponse(partial: Partial<IdTokenResponse>): IdTokenResponse {
return {
idToken: 'id-token',
refreshToken: 'refresh-token',
expiresIn: 'expires-in',
localId: 'local-id',
kind: IdTokenResponseKind.CreateAuthUri,
...partial
};
}