voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
1,550 lines (1,444 loc) • 435 kB
JavaScript
/**
* @license
* Copyright 2017 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.
*/
/**
* @fileoverview Tests for auth.js
*/
goog.provide('fireauth.AuthTest');
goog.require('fireauth.ActionCodeInfo');
goog.require('fireauth.ActionCodeSettings');
goog.require('fireauth.Auth');
goog.require('fireauth.AuthCredential');
goog.require('fireauth.AuthError');
goog.require('fireauth.AuthErrorWithCredential');
goog.require('fireauth.AuthEvent');
goog.require('fireauth.AuthEventManager');
goog.require('fireauth.AuthSettings');
goog.require('fireauth.AuthUser');
goog.require('fireauth.EmailAuthProvider');
goog.require('fireauth.GoogleAuthProvider');
goog.require('fireauth.MultiFactorAssertion');
goog.require('fireauth.MultiFactorInfo');
goog.require('fireauth.MultiFactorSession');
goog.require('fireauth.PhoneAuthProvider');
goog.require('fireauth.RpcHandler');
goog.require('fireauth.SAMLAuthProvider');
goog.require('fireauth.StsTokenManager');
goog.require('fireauth.UserEventType');
goog.require('fireauth.authStorage');
goog.require('fireauth.authenum.Error');
goog.require('fireauth.common.testHelper');
goog.require('fireauth.constants');
goog.require('fireauth.deprecation');
/** @suppress {extraRequire} Needed for firebase.app().auth() */
goog.require('fireauth.exports');
goog.require('fireauth.idp');
goog.require('fireauth.iframeclient.IfcHandler');
goog.require('fireauth.object');
goog.require('fireauth.storage.MockStorage');
goog.require('fireauth.storage.PendingRedirectManager');
goog.require('fireauth.storage.RedirectUserManager');
goog.require('fireauth.storage.UserManager');
goog.require('fireauth.util');
goog.require('goog.Promise');
goog.require('goog.Timer');
goog.require('goog.Uri');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.testing.AsyncTestCase');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.MockControl');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.events');
goog.require('goog.testing.events.Event');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
goog.setTestOnly('fireauth.AuthTest');
var appId1 = 'appId1';
var appId2 = 'appId2';
var auth1 = null;
var auth2 = null;
var authInternal1 = null;
var authInternal2 = null;
var app1 = null;
var app2 = null;
var authUi1 = null;
var authUi2 = null;
var config1 = null;
var config2 = null;
var config3 = null;
var rpcHandler = null;
var token = null;
var accountInfo = {
'uid': '14584746072031976743',
'email': 'uid123@fake.com',
'displayName': 'John Doe',
'photoURL': 'http://abs.twimg.com/sticky/default_profile_images/' +
'default_profile_3_normal.png',
'emailVerified': true
};
var accountInfoWithTenantId = {
'uid': '14584746072031976743',
'email': 'uid123@fake.com',
'displayName': 'John Doe',
'photoURL': 'http://abs.twimg.com/sticky/default_profile_images/' +
'default_profile_3_normal.png',
'emailVerified': true,
'tenantId': '123456789012'
};
// accountInfo in the format of a getAccountInfo response.
var getAccountInfoResponse = {
'users': [{
'localId': '14584746072031976743',
'email': 'uid123@fake.com',
'emailVerified': true,
'displayName': 'John Doe',
'providerUserInfo': [],
'photoUrl': 'http://abs.twimg.com/sticky/default_profile_images/' +
'default_profile_3_normal.png',
'passwordUpdatedAt': 0.0,
'disabled': false
}]
};
// A sample JWT, along with its decoded contents.
var idTokenGmail = {
data: {
sub: '679',
aud: '204241631686',
provider_id: 'gmail.com',
email: 'test123456@gmail.com',
federated_id: 'https://www.google.com/accounts/123456789'
}
};
var expectedTokenResponse;
var expectedTokenResponse2;
var expectedTokenResponse3;
var expectedTokenResponse4;
var expectedTokenResponseWithIdPData;
var expectedAdditionalUserInfo;
var expectedGoogleCredential;
var expectedSamlTokenResponseWithIdPData;
var expectedSamlAdditionalUserInfo;
var now = Date.now();
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
var mockControl;
var ignoreArgument;
var stubs = new goog.testing.PropertyReplacer();
var angular = {};
var currentUserStorageManager;
var redirectUserStorageManager;
var timeoutDelay = 30000;
var clock;
var actionCodeSettings = {
'url': 'https://www.example.com/?state=abc',
'iOS': {
'bundleId': 'com.example.ios'
},
'android': {
'packageName': 'com.example.android',
'installApp': true,
'minimumVersion': '12'
},
'handleCodeInApp': true,
'dynamicLinkDomain': 'example.page.link'
};
var mockLocalStorage;
var mockSessionStorage;
var jwt1;
var jwt2;
var jwt3;
var multiFactorErrorServerResponse;
var multiFactorTokenResponse;
var multiFactorGetAccountInfoResponse;
function setUp() {
// Create new mock storages for persistent and temporary storage before each
// test.
mockLocalStorage = new fireauth.storage.MockStorage();
mockSessionStorage = new fireauth.storage.MockStorage();
// Disable Auth event manager for testing unless needed.
fireauth.AuthEventManager.ENABLED = false;
// Assume origin is a valid one.
simulateWhitelistedOrigin();
// Simulate tab can run in background.
stubs.replace(
fireauth.util,
'runsInBackground',
function() {
return true;
});
// In case the tests are run from an iframe.
stubs.replace(
fireauth.util,
'isIframe',
function() {
return false;
});
// Called on init state when a user is logged in.
stubs.replace(
fireauth.AuthUser.prototype,
'reload',
function() {
return goog.Promise.resolve();
});
stubs.replace(
Date,
'now',
function() {
return now;
});
stubs.replace(
fireauth.util,
'getCurrentUrl',
function() {return 'http://localhost';});
initializeMockStorage();
jwt1 = fireauth.common.testHelper.createMockJwt(
{'group': '1'}, now + 3600 * 1000);
jwt2 = fireauth.common.testHelper.createMockJwt(
{'group': '2'}, now + 3600 * 1000);
jwt3 = fireauth.common.testHelper.createMockJwt(
{'group': '3'}, now + 3600 * 1000);
idTokenGmail.data.exp = now / 1000 + 3600;
idTokenGmail.jwt =
fireauth.common.testHelper.createMockJwt(idTokenGmail.data);
// Initialize App and Auth instances.
config1 = {
apiKey: 'apiKey1'
};
config2 = {
apiKey: 'apiKey2'
};
config3 = {
'apiKey': 'API_KEY',
'authDomain': 'subdomain.firebaseapp.com',
'appName': 'appId1'
};
// Same as config3 but with a different authDomain.
config4 = {
'apiKey': 'API_KEY',
'authDomain': 'subdomain2.firebaseapp.com',
'appName': 'appId1'
};
expectedTokenResponse = {
'idToken': jwt1,
'refreshToken': 'REFRESH_TOKEN',
'expiresIn': '3600'
};
expectedTokenResponse2 = {
'idToken': jwt2,
'refreshToken': 'REFRESH_TOKEN2',
'expiresIn': '3600'
};
expectedTokenResponse3 = {
'idToken': jwt3,
'refreshToken': 'REFRESH_TOKEN3',
'expiresIn': '3600'
};
expectedTokenResponse4 = {
// Sample ID token with provider password and email user@example.com.
'idToken': fireauth.common.testHelper.createMockJwt({
'iss': 'https://securetoken.google.com/12345678',
'picture': 'https://plus.google.com/abcdefghijklmnopqrstu',
'aud': '12345678',
'auth_time': 1510357622,
'sub': 'abcdefghijklmnopqrstu',
'iat': 1510357622,
'exp': now / 1000 + 3600,
'email': 'user@example.com',
'email_verified': true,
'firebase': {
'identities': {
'email': [
'user@example.com'
]
},
'sign_in_provider': 'password'
}
}),
'refreshToken': 'REFRESH_TOKEN4',
'expiresIn': '3600'
};
expectedTokenResponseWithIdPData = {
'idToken': jwt1,
'refreshToken': 'REFRESH_TOKEN',
'expiresIn': '3600',
// Credential returned.
'providerId': 'google.com',
'oauthAccessToken': 'googleAccessToken',
'oauthIdToken': 'googleIdToken',
'oauthExpireIn': 3600,
// Additional user info data.
'rawUserInfo': '{"kind":"plus#person","displayName":"John Doe","na' +
'me":{"givenName":"John","familyName":"Doe"}}'
};
expectedAdditionalUserInfo = {
'profile': {
'kind': 'plus#person',
'displayName': 'John Doe',
'name': {
'givenName': 'John',
'familyName': 'Doe'
}
},
'providerId': 'google.com',
'isNewUser': false
};
expectedGoogleCredential = fireauth.GoogleAuthProvider.credential(
'googleIdToken', 'googleAccessToken');
expectedSamlTokenResponseWithIdPData = {
'idToken': jwt1,
'refreshToken': 'REFRESH_TOKEN',
'expiresIn': '3600',
'providerId': 'saml.provider',
// Additional user info data.
'rawUserInfo': '{"kind":"plus#person","displayName":"John Doe","na' +
'me":{"givenName":"John","familyName":"Doe"}}'
};
expectedSamlAdditionalUserInfo = {
'profile': {
'kind': 'plus#person',
'displayName': 'John Doe',
'name': {
'givenName': 'John',
'familyName': 'Doe'
}
},
'providerId': 'saml.provider',
'isNewUser': false
};
multiFactorTokenResponse = {
'idToken': fireauth.common.testHelper.createMockJwt({
'sub': 'defaultUserId',
'firebase': {
'sign_in_provider': 'password',
'sign_in_second_factor': 'phone'
}
}),
'refreshToken': 'MULTI_FACTOR_REFRESH_TOKEN'
};
multiFactorErrorServerResponse = {
'mfaInfo': [
{
'mfaEnrollmentId': 'ENROLLMENT_UID1',
'enrolledAt': new Date(now).toISOString(),
'phoneInfo': '+*******1234'
},
{
'mfaEnrollmentId': 'ENROLLMENT_UID2',
'displayName': 'Spouse phone number',
'enrolledAt': new Date(now).toISOString(),
'phoneInfo': '+*******6789'
}
],
'mfaPendingCredential': 'PENDING_CREDENTIAL',
// Credential returned.
'providerId': 'google.com',
'oauthAccessToken': 'googleAccessToken',
'oauthIdToken': 'googleIdToken',
'oauthExpireIn': 3600,
// Additional user info data.
'rawUserInfo': '{"kind":"plus#person","displayName":"John Doe",' +
'"name":{"givenName":"John","familyName":"Doe"}}'
};
multiFactorGetAccountInfoResponse = {
'users': [{
'localId': 'defaultUserId',
'email': 'user@default.com',
'emailVerified': true,
'displayName': 'defaultDisplayName',
'providerUserInfo': [],
'photoUrl': 'https://www.default.com/default/default.png',
'passwordUpdatedAt': 0.0,
'disabled': false,
'lastLoginAt': '1506050282000',
'createdAt': '1506050282000',
'mfaInfo': [
{
'mfaEnrollmentId': 'ENROLLMENT_UID1',
'enrolledAt': new Date(now).toISOString(),
'phoneInfo': '+16505551234'
},
{
'mfaEnrollmentId': 'ENROLLMENT_UID2',
'displayName': 'Spouse phone number',
'enrolledAt': new Date(now).toISOString(),
'phoneInfo': '+16505556789'
}
]
}]
};
rpcHandler = new fireauth.RpcHandler('apiKey1');
token = new fireauth.StsTokenManager(rpcHandler);
token.setRefreshToken('refreshToken');
token.setAccessToken(jwt1);
token.setExpiresIn(3600);
ignoreArgument = goog.testing.mockmatchers.ignoreArgument;
mockControl = new goog.testing.MockControl();
mockControl.$resetAll();
}
function tearDown() {
// Delete all Firebase apps created
var promises = [];
for (var i = 0; i < firebase.apps.length; i++) {
promises.push(firebase.apps[i].delete());
}
if (promises.length) {
// Wait for all Firebase apps to be deleted.
asyncTestCase.waitForSignals(1);
goog.Promise.all(promises).then(function() {
// Dispose clock then. Disposing before will throw an error in IE 11.
goog.dispose(clock);
asyncTestCase.signal();
});
if (clock) {
// Some IE browsers like IE 11, native promise hangs if this is not called
// when clock is mocked.
// app.delete() will hang (it uses the native Promise).
clock.tick();
}
} else if (clock) {
// No Firebase apps created, dispose clock immediately.
goog.dispose(clock);
}
fireauth.AuthEventManager.manager_ = {};
window.localStorage.clear();
window.sessionStorage.clear();
rpcHandler = null;
token = null;
if (auth1) {
auth1.delete();
}
auth1 = null;
if (auth2) {
auth2.delete();
}
auth2 = null;
app1 = null;
app2 = null;
config1 = null;
config2 = null;
config3 = null;
multiFactorErrorServerResponse = null;
multiFactorTokenResponse = null;
multiFactorGetAccountInfoResponse = null;
stubs.reset();
try {
mockControl.$verifyAll();
} finally {
mockControl.$tearDown();
}
fireauth.authStorage.Manager.clear();
currentUserStorageManager = null;
redirectUserStorageManager = null;
if (goog.global.document) {
fireauth.util.onDomReady().then(function () {
var el = goog.global.document.querySelector('.firebase-emulator-warning');
if (el) {
el.parentNode.removeChild(el);
}
});
}
}
function testInitializeApp_noApiKey() {
var expectedError = new fireauth.AuthError(
fireauth.authenum.Error.INVALID_API_KEY);
// Initialize app without an API key.
var appWithoutApiKey = firebase.initializeApp({}, 'appWithoutApiKey');
// Initialization without API key should not throw an Auth error.
// Only on Auth explicit initialization will the error be visibly thrown.
try {
appWithoutApiKey.auth();
fail('Auth initialization should fail due to missing api key.');
} catch (e) {
fireauth.common.testHelper.assertErrorEquals(expectedError, e);
}
}
/**
* Assert the Auth token listener is called once.
* @param {!fireauth.Auth} auth The Auth instance.
*/
function assertAuthTokenListenerCalledOnce(auth) {
var calls = 0;
asyncTestCase.waitForSignals(1);
auth.addAuthTokenListener(function(token) {
// Should only trigger once after init state.
calls++;
assertEquals(1, calls);
asyncTestCase.signal();
});
}
/** Initializes mock storages. */
function initializeMockStorage() {
// Simulate tab can run in background.
stubs.replace(
fireauth.util,
'runsInBackground',
function() {
return true;
});
fireauth.common.testHelper.installMockStorages(
stubs, mockLocalStorage, mockSessionStorage);
}
/**
* Simulates current origin is whitelisted for popups and redirects.
*/
function simulateWhitelistedOrigin() {
// Assume origin is a valid one.
stubs.replace(
fireauth.RpcHandler.prototype,
'getAuthorizedDomains',
function() {
var uri = goog.Uri.parse(fireauth.util.getCurrentUrl());
var domain = uri.getDomain();
return goog.Promise.resolve([domain]);
});
}
/**
* Asserts that two users are equivalent. Plain assertObjectEquals may not work
* as the expiration time may sometimes be off by a second. This takes that into
* account.
* @param {!fireauth.AuthUser} expected
* @param {!fireauth.AuthUser} actual
*/
function assertUserEquals(expected, actual) {
fireauth.common.testHelper.assertUserEqualsInWithDiffApikey(expected, actual);
}
function testAuth_noApiKey() {
try {
app1 = firebase.initializeApp({}, appId1);
app1.auth();
fail('Should have thrown an error!');
} catch (e) {
fireauth.common.testHelper.assertErrorEquals(
new fireauth.AuthError(fireauth.authenum.Error.INVALID_API_KEY), e);
}
}
function testToJson_noUser() {
// Test toJSON with no user signed in.
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
var authPlainObject = {
'apiKey': config1['apiKey'],
'authDomain': config1['authDomain'],
'appName': appId1,
'currentUser': null
};
assertObjectEquals(authPlainObject, auth1.toJSON());
// Make sure JSON.stringify works and uses underlying toJSON.
assertEquals(JSON.stringify(auth1), JSON.stringify(auth1.toJSON()));
}
function testToJson_withUser() {
// Test toJSON with a user signed in.
stubs.reset();
fireauth.AuthEventManager.ENABLED = false;
initializeMockStorage();
// Simulate available token.
stubs.replace(
fireauth.AuthUser.prototype,
'reload',
function() {
// Reload will be called on init.
return goog.Promise.resolve();
});
stubs.replace(
Date,
'now',
function() {
return now;
});
asyncTestCase.waitForSignals(1);
app1 = firebase.initializeApp(config1, appId1);
var user1 = new fireauth.AuthUser(
config3, expectedTokenResponse, accountInfo);
var storageKey = fireauth.util.createStorageKey(config1['apiKey'], appId1);
var authPlainObject = {
'apiKey': config1['apiKey'],
'authDomain': config1['authDomain'],
'appName': appId1,
'currentUser': user1.toJSON()
};
currentUserStorageManager = new fireauth.storage.UserManager(storageKey);
// Simulate logged in user, save to storage, it will be picked up on init
// Auth state.
currentUserStorageManager.setCurrentUser(user1).then(function() {
auth1 = app1.auth();
auth1.onIdTokenChanged(function(user) {
assertObjectEquals(authPlainObject, auth1.toJSON());
// Make sure JSON.stringify works and uses underlying toJSON.
assertEquals(JSON.stringify(auth1), JSON.stringify(auth1.toJSON()));
asyncTestCase.signal();
});
});
}
function testGetStorageKey() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
assertEquals(config1['apiKey'] + ':' + appId1, auth1.getStorageKey());
}
function testAuth_initListeners_disabled() {
// Test with init listener disabled.
app1 = firebase.initializeApp(config1, appId1);
app2 = firebase.initializeApp(config2, appId2);
auth1 = app1.auth();
auth2 = app2.auth();
assertObjectEquals(app1, auth1.app_());
assertObjectEquals(app2, auth2.app_());
}
function testApp() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
assertObjectEquals(app1, auth1.app_());
}
function testGetRpcHandler() {
app1 = firebase.initializeApp(config1, appId1);
app2 = firebase.initializeApp(config2, appId2);
auth1 = app1.auth();
auth2 = app2.auth();
assertNotNull(auth1.getRpcHandler());
assertNotNull(auth2.getRpcHandler());
assertEquals('apiKey1', auth1.getRpcHandler().getApiKey());
assertEquals('apiKey2', auth2.getRpcHandler().getApiKey());
}
function testAuth_rpcHandlerEndpoints() {
// Confirm expected endpoint config passed to underlying RPC handler.
var endpoint = fireauth.constants.Endpoint.STAGING;
var endpointConfig = {
'firebaseEndpoint': endpoint.firebaseAuthEndpoint,
'secureTokenEndpoint': endpoint.secureTokenEndpoint
};
stubs.replace(
fireauth.constants,
'getEndpointConfig',
function(opt_id) {
return endpointConfig;
});
var rpcHandler = mockControl.createStrictMock(fireauth.RpcHandler);
var rpcHandlerConstructor = mockControl.createConstructorMock(
fireauth, 'RpcHandler');
rpcHandlerConstructor(config1['apiKey'], endpointConfig, ignoreArgument)
.$returns(rpcHandler);
mockControl.$replayAll();
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
}
function testAuth_rpcHandlerEndpoints_tenantId() {
// Confirm expected endpoint config passed to underlying RPC handler.
var endpoint = fireauth.constants.Endpoint.STAGING;
var endpointConfig = {
'firebaseEndpoint': endpoint.firebaseAuthEndpoint,
'secureTokenEndpoint': endpoint.secureTokenEndpoint
};
stubs.replace(
fireauth.constants,
'getEndpointConfig',
function(opt_id) {
return endpointConfig;
});
var rpcHandler = mockControl.createStrictMock(fireauth.RpcHandler);
var rpcHandlerConstructor = mockControl.createConstructorMock(
fireauth, 'RpcHandler');
rpcHandlerConstructor(config1['apiKey'], endpointConfig, ignoreArgument)
.$returns(rpcHandler);
// Tenant ID of RPC handler should be updated.
rpcHandler.updateTenantId('TENANT_ID').$once();
mockControl.$replayAll();
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Sets the tenant ID on Auth instance.
auth1.tenantId = 'TENANT_ID';
}
function testCurrentUser() {
app1 = firebase.initializeApp(config3, appId1);
auth1 = app1.auth();
var user = new fireauth.AuthUser(
config3, expectedTokenResponse, accountInfo);
auth1.setCurrentUser_(user);
assertUserEquals(user, auth1['currentUser']);
}
function testAuthSettings() {
app1 = firebase.initializeApp(config3, appId1);
auth1 = app1.auth();
assertTrue(auth1['settings'] instanceof fireauth.AuthSettings);
assertFalse(auth1['settings']['appVerificationDisabledForTesting']);
auth1['settings']['appVerificationDisabledForTesting'] = true;
assertTrue(auth1['settings']['appVerificationDisabledForTesting']);
// Confirm validation is applied.
var error = assertThrows(function() {
auth1['settings']['appVerificationDisabledForTesting'] = 'invalid';
});
var expectedError = new fireauth.AuthError(
fireauth.authenum.Error.ARGUMENT_ERROR,
'appVerificationDisabledForTesting failed: ' +
'"appVerificationDisabledForTesting" must be a boolean.');
fireauth.common.testHelper.assertErrorEquals(expectedError, error);
// Confirm settings is a read-only property.
auth1['settings'] = null;
assertTrue(auth1['settings'] instanceof fireauth.AuthSettings);
}
function testLogFramework() {
// Helper function to get the client version for the test.
var getVersion = function(frameworks) {
return fireauth.util.getClientVersion(
fireauth.util.ClientImplementation.JSCORE, firebase.SDK_VERSION,
frameworks);
};
// Pipe through all framework IDs.
stubs.replace(
fireauth.util,
'getFrameworkIds',
function(providedFrameworks) {
return providedFrameworks;
});
// Listen to all client version update calls on RpcHandler.
stubs.replace(
fireauth.RpcHandler.prototype,
'updateClientVersion',
goog.testing.recordFunction());
var handler = goog.testing.recordFunction();
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Listen to all frameworkChanged events dispatched by the Auth instance.
goog.events.listen(
auth1,
fireauth.constants.AuthEventType.FRAMEWORK_CHANGED,
handler);
assertArrayEquals([], auth1.getFramework());
assertEquals(0, handler.getCallCount());
assertEquals(
0, fireauth.RpcHandler.prototype.updateClientVersion.getCallCount());
// Add version and confirm event triggered and client version updated in
// RpcHandler.
auth1.logFramework('angularfire');
assertArrayEquals(['angularfire'], auth1.getFramework());
assertEquals(1, handler.getCallCount());
assertArrayEquals(
['angularfire'], handler.getLastCall().getArgument(0).frameworks);
assertEquals(
1, fireauth.RpcHandler.prototype.updateClientVersion.getCallCount());
assertEquals(
getVersion(['angularfire']),
fireauth.RpcHandler.prototype.updateClientVersion.getLastCall()
.getArgument(0));
// Add another version and confirm event triggered and client version updated
// in RpcHandler.
auth1.logFramework('firebaseui');
assertArrayEquals(['angularfire', 'firebaseui'], auth1.getFramework());
assertEquals(2, handler.getCallCount());
assertArrayEquals(
['angularfire', 'firebaseui'],
handler.getLastCall().getArgument(0).frameworks);
assertEquals(
2, fireauth.RpcHandler.prototype.updateClientVersion.getCallCount());
assertEquals(
getVersion(['angularfire', 'firebaseui']),
fireauth.RpcHandler.prototype.updateClientVersion.getLastCall()
.getArgument(0));
}
function testInternalLogFramework() {
// Record all calls to logFramework.
stubs.replace(
fireauth.Auth.prototype,
'logFramework',
goog.testing.recordFunction());
// Confirm INTERNAL.logFramework calls logFramework.
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
assertEquals(0, fireauth.Auth.prototype.logFramework.getCallCount());
auth1.INTERNAL.logFramework('firebaseui');
assertEquals(1, fireauth.Auth.prototype.logFramework.getCallCount());
assertEquals(
'firebaseui',
fireauth.Auth.prototype.logFramework.getLastCall().getArgument(0));
}
function testUseDeviceLanguage() {
// Listen to all custom locale header calls on RpcHandler.
stubs.replace(
fireauth.RpcHandler.prototype,
'updateCustomLocaleHeader',
goog.testing.recordFunction());
var handler = goog.testing.recordFunction();
stubs.replace(fireauth.util, 'getUserLanguage', function() {
return 'de';
});
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Listen to all languageCodeChanged events dispatched by the Auth instance.
goog.events.listen(
auth1,
fireauth.constants.AuthEventType.LANGUAGE_CODE_CHANGED,
handler);
assertNull(auth1.languageCode);
assertEquals(0, handler.getCallCount());
assertEquals(
0, fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getCallCount());
// Update to English and confirm event triggered and custom locale updated in
// RpcHandler.
auth1.languageCode = 'en';
assertEquals('en', auth1.languageCode);
assertEquals(1, handler.getCallCount());
assertEquals('en', handler.getLastCall().getArgument(0).languageCode);
assertEquals(
1, fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getCallCount());
assertEquals(
'en',
fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getLastCall()
.getArgument(0));
// Update to device language and confirm event triggered and custom locale
// updated in RpcHandler.
auth1.useDeviceLanguage();
assertEquals('de', auth1.languageCode);
assertEquals(2, handler.getCallCount());
assertEquals('de', handler.getLastCall().getArgument(0).languageCode);
assertEquals(
2, fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getCallCount());
assertEquals(
'de',
fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getLastCall()
.getArgument(0));
// Developer should still be able to set the language code.
// Update to French and confirm event triggered and custom locale updated in
// RpcHandler.
auth1.languageCode = 'fr';
assertEquals('fr', auth1.languageCode);
assertEquals(3, handler.getCallCount());
assertEquals('fr', handler.getLastCall().getArgument(0).languageCode);
assertEquals(
3, fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getCallCount());
assertEquals(
'fr',
fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getLastCall()
.getArgument(0));
// Switch back to device language.
auth1.useDeviceLanguage();
assertEquals('de', auth1.languageCode);
assertEquals(4, handler.getCallCount());
assertEquals('de', handler.getLastCall().getArgument(0).languageCode);
assertEquals(
4, fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getCallCount());
assertEquals(
'de',
fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getLastCall()
.getArgument(0));
// Changing to the same language should not trigger any change.
auth1.languageCode = 'de';
assertEquals(4, handler.getCallCount());
assertEquals(
4, fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getCallCount());
// Update to null and confirm event triggered and custom locale updated in
// RpcHandler.
auth1.languageCode = null;
assertNull(auth1.languageCode);
assertEquals(5, handler.getCallCount());
assertNull(handler.getLastCall().getArgument(0).languageCode);
assertEquals(
5, fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getCallCount());
assertNull(
fireauth.RpcHandler.prototype.updateCustomLocaleHeader.getLastCall()
.getArgument(0));
}
function testUseEmulator() {
// Listen to emulator config calls on RpcHandler.
stubs.replace(
fireauth.RpcHandler.prototype,
'updateEmulatorConfig',
goog.testing.recordFunction());
stubs.replace(
fireauth.util,
'consoleInfo',
goog.testing.recordFunction());
var handler = goog.testing.recordFunction();
stubs.replace(
fireauth.AuthSettings.prototype,
'setAppVerificationDisabledForTesting',
goog.testing.recordFunction());
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Listen to all emulatorConfigChange events dispatched by the Auth instance.
goog.events.listen(
auth1,
fireauth.constants.AuthEventType.EMULATOR_CONFIG_CHANGED,
handler);
assertUndefined(fireauth.constants.emulatorConfig);
assertEquals(0, handler.getCallCount());
assertEquals(
0, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertEquals(0, fireauth.util.consoleInfo.getCallCount());
assertEquals(
0,
fireauth.AuthSettings.prototype.setAppVerificationDisabledForTesting.
getCallCount());
// Update the emulator config.
auth1.useEmulator('http://emulator.test.domain:1234');
assertObjectEquals(
{
protocol: 'http',
host: 'emulator.test.domain',
port: 1234,
options: {disableWarnings: false},
},
auth1.emulatorConfig);
// Should notify the RPC handler.
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertObjectEquals(
{
url: 'http://emulator.test.domain:1234',
disableWarnings: false,
},
fireauth.RpcHandler.prototype.updateEmulatorConfig.getLastCall()
.getArgument(0)
);
// Should emit a console warning and a banner.
assertEquals(1, fireauth.util.consoleInfo.getCallCount());
if (goog.global.document) {
asyncTestCase.waitForSignals(1);
fireauth.util.onDomReady().then(() => {
const el =
goog.global.document.querySelector('.firebase-emulator-warning');
assertNotNull(el);
asyncTestCase.signal();
});
}
// Should disable App verification.
assertEquals(
true,
fireauth.AuthSettings.prototype.setAppVerificationDisabledForTesting.
getLastCall().getArgument(0));
// Update to the same config should not trigger event again.
auth1.useEmulator('http://emulator.test.domain:1234');
assertObjectEquals(
{
protocol: 'http',
host: 'emulator.test.domain',
port: 1234,
options: {disableWarnings: false},
},
auth1.emulatorConfig);
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertEquals(1, fireauth.util.consoleInfo.getCallCount());
// Updating to different config should still not trigger event.
auth1.useEmulator('http://emulator.other.domain:9876');
assertObjectEquals(
{
protocol: 'http',
host: 'emulator.test.domain',
port: 1234,
options: {disableWarnings: false},
},
auth1.emulatorConfig);
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
}
function testUseEmulator_withDisableWarnings() {
// Listen to emulator config calls on RpcHandler.
stubs.replace(
fireauth.RpcHandler.prototype, 'updateEmulatorConfig',
goog.testing.recordFunction());
stubs.replace(fireauth.util, 'consoleInfo', goog.testing.recordFunction());
const handler = goog.testing.recordFunction();
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Listen to all emulatorConfigChange events dispatched by the Auth instance.
goog.events.listen(
auth1, fireauth.constants.AuthEventType.EMULATOR_CONFIG_CHANGED, handler);
assertUndefined(fireauth.constants.emulatorConfig);
assertEquals(0, handler.getCallCount());
assertEquals(
0, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertEquals(0, fireauth.util.consoleInfo.getCallCount());
// Update the emulator config.
auth1.useEmulator(
'http://emulator.test.domain:1234', {disableWarnings: true});
assertObjectEquals(
{
protocol: 'http',
host: 'emulator.test.domain',
port: 1234,
options: {disableWarnings: true},
},
auth1.emulatorConfig);
// Should notify the RPC handler.
assertEquals(
1, fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertObjectEquals(
{
url: 'http://emulator.test.domain:1234',
disableWarnings: true,
},
fireauth.RpcHandler.prototype.updateEmulatorConfig.getLastCall()
.getArgument(0));
// Should emit a console info but not a banner.
assertEquals(1, fireauth.util.consoleInfo.getCallCount());
if (goog.global.document) {
asyncTestCase.waitForSignals(1);
fireauth.util.onDomReady().then(() => {
const el =
goog.global.document.querySelector('.firebase-emulator-warning');
assertNull(el);
asyncTestCase.signal();
});
}
}
function testEmulatorConfig() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Update the emulator config.
auth1.useEmulator(
'http://emulator.test.domain:1234', {disableWarnings: true});
assertObjectEquals(
{
protocol: 'http',
host: 'emulator.test.domain',
port: 1234,
options: {disableWarnings: true},
},
auth1.emulatorConfig);
}
/**
* Asserts that the port is correctly set to null if no port supplied.
*/
function testEmulatorConfig_noPortSpecified() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Update the emulator config.
auth1.useEmulator('http://emulator.test.domain');
assertObjectEquals(
{
protocol: 'http',
host: 'emulator.test.domain',
port: null,
options: {disableWarnings: false},
},
auth1.emulatorConfig);
}
/**
* Asserts that the port is correctly assigned 0 if specifically set to 0 for
* some reason. Also checks https protocol.
*/
function testEmulatorConfig_portZeroAndHttpsSpecified() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Update the emulator config.
auth1.useEmulator('https://emulator.test.domain:0');
assertObjectEquals(
{
protocol: 'https',
host: 'emulator.test.domain',
port: 0,
options: {disableWarnings: false},
},
auth1.emulatorConfig);
}
/**
* Asserts that the function returns null if useEmulator is not called.
*/
function testEmulatorConfig_nullIfNoEmulatorConfig() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
assertNull(auth1.emulatorConfig);
}
function testGetSetTenantId() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
// Tenant ID should be initialized to null.
assertNull(auth1.tenantId);
assertNull(auth1.getRpcHandler().getTenantId());
// Updating tenant ID on Auth should also update the tenant ID of RPC handler.
auth1.tenantId = 'TENANT_ID1';
assertEquals('TENANT_ID1', auth1.tenantId);
assertEquals('TENANT_ID1', auth1.getRpcHandler().getTenantId());
// Reset tenant ID to null.
auth1.tenantId = null;
assertNull(auth1.tenantId);
assertNull(auth1.getRpcHandler().getTenantId());
// Test getter and setter.
auth1.setTenantId('TENANT_ID2');
assertEquals('TENANT_ID2', auth1.getTenantId());
assertEquals('TENANT_ID2', auth1.tenantId);
auth1.tenantId = null;
assertNull(auth1.getTenantId());
assertNull(auth1.tenantId);
}
/**
* Test Auth state listeners triggered on listener add even when initial state
* is null. However it will only first trigger when state is resolved.
*/
function testAddAuthTokenListener_initialNullState() {
var user = new fireauth.AuthUser(config1, expectedTokenResponse, accountInfo);
stubs.reset();
// Simulate no state returned.
stubs.replace(
fireauth.storage.UserManager.prototype,
'getCurrentUser',
function() {
return goog.Promise.resolve(null);
});
initializeMockStorage();
// Suppress addStateChangeListener.
stubs.replace(
fireauth.storage.UserManager.prototype,
'addCurrentUserChangeListener',
function(listener) {});
stubs.replace(
fireauth.StsTokenManager.prototype,
'getToken',
function(opt_forceRefresh) {
// Generate new token on next call to trigger listeners.
return goog.Promise.resolve({
accessToken: jwt2,
refreshToken: 'refreshToken'
});
});
var listener1 = mockControl.createFunctionMock('listener1');
var listener2 = mockControl.createFunctionMock('listener2');
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
listener1(null).$does(function() {
// Should be triggered after state is resolved.
assertEquals(0, marker);
// Increment marker.
marker++;
auth1.addAuthTokenListener(listener2);
});
listener2(null).$does(function() {
// Should be triggered after listener2 is added.
assertEquals(1, marker);
// Increment marker.
marker++;
// Auth state change notification should also trigger immediately now.
// Simulate Auth event to trigger both listeners.
auth1.setCurrentUser_(user);
user.getIdToken();
});
listener1(jwt2).$does(function() {
// Marker should confirm listener triggered after notifyAuthListeners_.
assertEquals(2, marker);
asyncTestCase.signal();
});
listener2(jwt2).$does(function() {
// Marker should confirm listener triggered after notifyAuthListeners_.
assertEquals(2, marker);
asyncTestCase.signal();
});
mockControl.$replayAll();
// Wait for last 2 expected listener calls.
asyncTestCase.waitForSignals(2);
// Keep track of what is triggering the events.
var marker = 0;
// Test listeners called when state first determined.
auth1.addAuthTokenListener(listener1);
}
/**
* Test Auth state listeners triggered on listener add even when initial state
* is not null (signed in user). However it will only first trigger when state
* is resolved.
*/
function testAddAuthTokenListener_initialValidState() {
var user = new fireauth.AuthUser(config1, expectedTokenResponse, accountInfo);
stubs.reset();
// Simulate valid state returned.
stubs.replace(
fireauth.storage.UserManager.prototype,
'getCurrentUser',
function() {
return goog.Promise.resolve(user);
});
initializeMockStorage();
// Suppress addStateChangeListener.
stubs.replace(
fireauth.storage.UserManager.prototype,
'addCurrentUserChangeListener',
function(listener) {});
// Simulate available token.
stubs.replace(
fireauth.AuthUser.prototype,
'reload',
function() {
// Internally calls Auth user listeners.
return goog.Promise.resolve();
});
var currentAccessToken = jwt1;
stubs.replace(
fireauth.StsTokenManager.prototype,
'getToken',
function(opt_forceRefresh) {
// Generate new token on next call to trigger listeners.
return goog.Promise.resolve({
accessToken: currentAccessToken,
refreshToken: 'refreshToken'
});
});
// Keep track of what is triggering the events.
var marker = 0;
var listener1 = mockControl.createFunctionMock('listener1');
var listener2 = mockControl.createFunctionMock('listener2');
app1 = firebase.initializeApp(config3, appId1);
auth1 = app1.auth();
listener1(jwt1).$does(function() {
// Should be triggered after state is resolved.
assertEquals(0, marker);
marker++;
// Now that state is determined, adding a new listener should resolve
// immediately.
auth1.addAuthTokenListener(listener2);
});
listener2(jwt1).$does(function() {
// Should be triggered after listener2 is added.
assertEquals(1, marker);
// Increment marker.
marker++;
// Auth state change notification should also trigger immediately now.
// Simulate Auth event via getIdToken refresh to trigger both listeners.
currentAccessToken = 'newAccessToken';
user.getIdToken();
});
listener1('newAccessToken').$does(function() {
// Marker should confirm listener triggered after notifyAuthListeners_.
assertEquals(2, marker);
asyncTestCase.signal();
});
listener2('newAccessToken').$does(function() {
// Marker should confirm listener triggered after notifyAuthListeners_.
assertEquals(2, marker);
asyncTestCase.signal();
});
mockControl.$replayAll();
// Wait for last 2 expected listener calls.
asyncTestCase.waitForSignals(2);
// Test listeners called when state first determined.
auth1.addAuthTokenListener(listener1);
}
function testGetUid_userSignedIn() {
// Test getUid() on Auth instance and app instance with user previously
// signed in.
var accountInfo1 = {'uid': '1234'};
asyncTestCase.waitForSignals(1);
// Get current user storage manager.
var storageKey = fireauth.util.createStorageKey(config1['apiKey'], appId1);
currentUserStorageManager = new fireauth.storage.UserManager(storageKey);
// Create test user instance.
var user =
new fireauth.AuthUser(config1, expectedTokenResponse, accountInfo1);
// Save test user. This will be loaded on Auth init.
currentUserStorageManager.setCurrentUser(user).then(function() {
// Initialize App and Auth.
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
authInternal1 = app1.container.getProvider('auth-internal').getImmediate();
// Initially getUid() should return null;
assertNull(auth1.getUid());
assertNull(authInternal1.getUid());
// Listen to Auth changes.
var unsubscribe = auth1.onIdTokenChanged(function(currentUser) {
// Unsubscribe of Auth state change listener.
unsubscribe();
// Logged in test user should be detected.
// Confirm getUid() returns expected UID.
assertEquals(accountInfo1['uid'], auth1.getUid());
assertEquals(accountInfo1['uid'], authInternal1.getUid());
goog.Timer.promise(10).then(function() {
// Sign out.
return auth1.signOut();
}).then(function() {
return goog.Timer.promise(10);
}).then(function() {
// getUid() should return null.
assertNull(auth1.getUid());
assertNull(authInternal1.getUid());
asyncTestCase.signal();
});
});
});
}
function testGetUid_noUserSignedIn() {
// Test getUid() on Auth instance and App instance with no user previously
// signed in and new user signs in.
var accountInfo1 = {'uid': '1234'};
stubs.replace(
fireauth.AuthUser,
'initializeFromIdTokenResponse',
function(options, idTokenResponse) {
return goog.Promise.resolve(user);
});
// Simulate successful RpcHandler verifyPassword resolving with expected
// token response.
stubs.replace(
fireauth.RpcHandler.prototype,
'verifyPassword', function(email, password) {
// Return tokens for test user.
return goog.Promise.resolve(expectedTokenResponse);
});
asyncTestCase.waitForSignals(1);
var user =
new fireauth.AuthUser(config1, expectedTokenResponse, accountInfo1);
// Initialize App and Auth.
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
authInternal1 = app1.container.getProvider('auth-internal').getImmediate();
// Listen to Auth changes.
var unsubscribe = auth1.onIdTokenChanged(function(currentUser) {
// Unsubscribe of Auth state change listener.
unsubscribe();
// Initially getUid() should return null;
assertNull(auth1.getUid());
assertNull(authInternal1.getUid());
// Sign in with email and password.
auth1.signInWithEmailAndPassword('user@example.com', 'password')
.then(function(userCredential) {
// getUid() should return the test user UID.
assertEquals(accountInfo1['uid'], auth1.getUid());
assertEquals(accountInfo1['uid'], authInternal1.getUid());
asyncTestCase.signal();
});
});
}
function testNotifyAuthListeners() {
// Simulate available token.
stubs.replace(
fireauth.StsTokenManager.prototype,
'getToken',
function(opt_forceRefresh) {
return goog.Promise.resolve({
accessToken: currentAccessToken,
refreshToken: 'refreshToken'
});
});
// User reloaded.
stubs.replace(
fireauth.AuthUser.prototype,
'reload',
function() {
return goog.Promise.resolve();
});
var currentAccessToken = 'accessToken1';
var app1AuthTokenListener = goog.testing.recordFunction();
var app2AuthTokenListener = goog.testing.recordFunction();
var user = new fireauth.AuthUser(
config1, expectedTokenResponse, accountInfo);
var listener1 = goog.testing.recordFunction();
var listener2 = goog.testing.recordFunction();
var listener3 = goog.testing.recordFunction();
asyncTestCase.waitForSignals(2);
// Set current user on auth1.
currentUserStorageManager = new fireauth.storage.UserManager(
config1['apiKey'] + ':' + appId1);
currentUserStorageManager.setCurrentUser(user).then(function() {
app1 = firebase.initializeApp(config1, appId1);
auth1 = app1.auth();
authInternal1 = app1.container.getProvider('auth-internal').getImmediate();
authInternal1.addAuthTokenListener(app1AuthTokenListener);
app2 = firebase.initializeApp(config2, appId2);
auth2 = app2.auth();
authInternal2 = app2.container.getProvider('auth-internal').getImmediate();
authInternal2.addAuthTokenListener(app2AuthTokenListener);
// Confirm all listeners reset.
assertEquals(0, listener1.getCallCount());
assertEquals(0, listener2.getCallCount());
assertEquals(0, listener3.getCallCount());
assertEquals(0, app1AuthTokenListener.getCallCount());
assertEquals(0, app2AuthTokenListener.getCallCount());
auth1.addAuthTokenListener(listener1);
auth1.addAuthTokenListener(listener2);
auth2.addAuthTokenListener(listener3);
// Wait for state to be ready on auth1.
var unsubscribe = auth1.onIdTokenChanged(function(currentUser) {
unsubscribe();
// Listener 1 and 2 triggered.
assertEquals(1, listener1.getCallCount());
assertEquals(listener1.getCallCount(), listener2.getCallCount());
// First trigger on init state.
assertEquals(
listener1.getCallCount(),
app1AuthTokenListener.getCallCount());
assertEquals(
'accessToken1',
app1AuthTokenListener.getLastCall().getArgument(0));
// Remove first listener and reset.
auth1.removeAuthTokenListener(listener1);
listener1.reset();
listener2.reset();
app1AuthTokenListener.reset();
// Force token change.
currentAccessToken = 'accessToken2';
// Trigger getIdToken to force refresh and Auth token change.
auth1['currentUser'].getIdToken().then(function(token) {
assertEquals('accessToken2', token);
assertEquals(0, listener1.getCallCount());
// Only listener2 triggered.
assertEquals(1, listener2.getCallCount());
// Second trigger.
assertEquals(
1,
app1AuthTokenListener.getCallCount());
assertEquals(
'accessToken2',
app1AuthTokenListener.getLastCall().getArgument(0));
// Remove remaining listeners and reset.
app1AuthTokenListener.reset();
listener2.reset();
auth1.removeAuthTokenListener(listener2);
authInternal1.removeAuthTokenListener(app1AuthTokenListener);
// Force token change.
currentAccessToken = 'accessToken3';
auth1['currentUser'].getIdToken().then(function(token) {
assertEquals('accessToken3', token);
// No listeners triggered anymore since they are all unsubscribed.
assertEquals(0, app1AuthTokenListener.getCallCount());
assertEquals(0, listener1.getCallCount());
assertEquals(0, listener2.getCallCount());
asyncTestCase.signal();
});
});
});
// Wait for state to be ready on auth2.
auth2.onIdTokenChanged(function(currentUser) {
// auth2 listener triggered on init with null state once.
assertEquals(1, listener3.getCallCount());
assertEquals(
1,
app2AuthTokenListener.getCallCount());
assertNull(
app2AuthTokenListener.getLastCall().getArgument(0));
assertNull(currentUser);
asyncTestCase.signal();
});
});
}
/**
* Tests the notifications made to observers defined through the public API,
* when calling the notifyAuthListeners.
*/
function testNotifyAuthStateObservers() {
stubs.reset();
// Simulate available token.
var counter = 0;
stubs.replace(
fireauth.StsTokenManager.prototype,
'getToken',
function(opt_forceRefresh) {
// Generate new token on each call.
counter++;
return goog.Promise.resolve({
accessToken: 'accessToken' + counter.toString(),
refreshToken: 'refreshToken'
});
});
// Simulate user logged in.
stubs.replace(
fireauth.storage.UserManager.prototype,
'getCurrentUser',
function() {
retur