diff --git a/packages/auth-interop-types/README.md b/packages/auth-interop-types/README.md new file mode 100644 index 00000000000..67ed6389e04 --- /dev/null +++ b/packages/auth-interop-types/README.md @@ -0,0 +1,3 @@ +# @firebase/auth-interop-types + +**This package is not intended for direct usage, and should only be used via the officially supported [firebase](https://www.npmjs.com/package/firebase) package.** diff --git a/packages/auth-interop-types/index.d.ts b/packages/auth-interop-types/index.d.ts new file mode 100644 index 00000000000..f529a240e52 --- /dev/null +++ b/packages/auth-interop-types/index.d.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2019 Google Inc. + * + * 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. + */ + +export interface FirebaseAuthTokenData { + accessToken: string; +} + +export interface FirebaseAuthInternal { + getToken(refreshToken?: boolean): Promise; + getUid(): string | null; + addAuthTokenListener(fn: (token: string | null) => void): void; + removeAuthTokenListener(fn: (token: string | null) => void): void; +} + +export type FirebaseAuthInternalName = 'auth-internal'; + +declare module '@firebase/component' { + interface NameServiceMapping { + 'auth-internal': FirebaseAuthInternal; + } +} diff --git a/packages/auth-interop-types/package.json b/packages/auth-interop-types/package.json new file mode 100644 index 00000000000..ee52ca167f6 --- /dev/null +++ b/packages/auth-interop-types/package.json @@ -0,0 +1,28 @@ +{ + "name": "@firebase/auth-interop-types", + "version": "0.1.0", + "description": "@firebase/auth interop Types", + "author": "Firebase (https://firebase.google.com/)", + "license": "Apache-2.0", + "scripts": { + "test": "tsc" + }, + "files": [ + "index.d.ts" + ], + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "0.x" + }, + "repository": { + "directory": "packages/auth-types", + "type": "git", + "url": "https://github.com/firebase/firebase-js-sdk.git" + }, + "bugs": { + "url": "https://github.com/firebase/firebase-js-sdk/issues" + }, + "devDependencies": { + "typescript": "3.6.4" + } +} diff --git a/packages/auth-interop-types/tsconfig.json b/packages/auth-interop-types/tsconfig.json new file mode 100644 index 00000000000..9a785433d90 --- /dev/null +++ b/packages/auth-interop-types/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../config/tsconfig.base.json", + "compilerOptions": { + "noEmit": true + }, + "exclude": [ + "dist/**/*" + ] +} diff --git a/packages/auth-types/index.d.ts b/packages/auth-types/index.d.ts index 954fb900bcd..85b2205705f 100644 --- a/packages/auth-types/index.d.ts +++ b/packages/auth-types/index.d.ts @@ -386,3 +386,9 @@ declare module '@firebase/app-types' { auth?(): FirebaseAuth; } } + +declare module '@firebase/component' { + interface NameServiceMapping { + 'auth': FirebaseAuth; + } +} diff --git a/packages/auth/src/exports_auth.js b/packages/auth/src/exports_auth.js index 97f5b6b1ae4..7b6ed725095 100644 --- a/packages/auth/src/exports_auth.js +++ b/packages/auth/src/exports_auth.js @@ -618,27 +618,10 @@ fireauth.exportlib.exportFunction( (function() { if (typeof firebase === 'undefined' || !firebase.INTERNAL || - !firebase.INTERNAL.registerService) { + !firebase.INTERNAL.registerComponent) { throw new Error('Cannot find the firebase namespace; be sure to include ' + 'firebase-app.js before this library.'); } else { - /** @type {!firebase.ServiceFactory} */ - var factory = function(app, extendApp) { - var auth = new fireauth.Auth(app); - extendApp({ - 'INTERNAL': { - // Extend app.INTERNAL.getUid. - 'getUid': goog.bind(auth.getUid, auth), - 'getToken': goog.bind(auth.getIdTokenInternal, auth), - 'addAuthTokenListener': - goog.bind(auth.addAuthTokenListenerInternal, auth), - 'removeAuthTokenListener': - goog.bind(auth.removeAuthTokenListenerInternal, auth) - } - }); - return auth; - }; - var namespace = { // Exports firebase.auth.ActionCodeInfo.Operation. 'ActionCodeInfo': { @@ -687,34 +670,45 @@ fireauth.exportlib.exportFunction( fireauth.exportlib.exportFunction(namespace, 'ActionCodeURL', fireauth.ActionCodeURL, []); - // Register Auth service with firebase.App. - firebase.INTERNAL.registerService( - fireauth.exportlib.AUTH_TYPE, - factory, - namespace, - // Initialize Auth when an App is created, so that tokens and Auth state - // listeners are available. - function (event, app) { - if (event === 'create') { - try { - app[fireauth.exportlib.AUTH_TYPE](); - } catch (e) { - // This is a silent operation in the background. If the auth - // initialization fails, it should not cause a fatal error. - // Instead when the developer tries to initialize again manually, - // the error will be thrown. - // One specific use case here is the initialization for the nodejs - // client when no API key is provided. This is commonly used - // for unauthenticated database access. - } - } - } - ); - + // Create auth components to register with firebase. + // Provides Auth public APIs. + const authComponent = { + 'name': fireauth.exportlib.AUTH_TYPE, + 'instanceFactory': function(container) { + var app = container['getProvider']('app')['getImmediate'](); + return new fireauth.Auth(app); + }, + 'multipleInstances': false, + 'serviceProps': namespace, + 'instantiationMode': 'LAZY', + 'type': 'PUBLIC' + }; + + // Provides Auth internal APIs. + const authInteropComponent = { + 'name': 'auth-internal', + 'instanceFactory': function(container) { + var auth = container['getProvider'](fireauth.exportlib.AUTH_TYPE)['getImmediate'](); + return { + 'getUid': goog.bind(auth.getUid, auth), + 'getToken': goog.bind(auth.getIdTokenInternal, auth), + 'addAuthTokenListener': + goog.bind(auth.addAuthTokenListenerInternal, auth), + 'removeAuthTokenListener': + goog.bind(auth.removeAuthTokenListenerInternal, auth) + }; + }, + 'multipleInstances': false, + 'instantiationMode': 'LAZY', + 'type': 'PRIVATE' + }; + + firebase.INTERNAL.registerComponent(authComponent); + firebase.INTERNAL.registerComponent(authInteropComponent); // Expose User as firebase.User. firebase.INTERNAL.extendNamespace({ 'User': fireauth.AuthUser }); } -})(); +})(); \ No newline at end of file diff --git a/packages/auth/test/auth_test.js b/packages/auth/test/auth_test.js index b8d6709a475..66785bef6e1 100644 --- a/packages/auth/test/auth_test.js +++ b/packages/auth/test/auth_test.js @@ -73,6 +73,8 @@ 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; @@ -1014,9 +1016,10 @@ function testGetUid_userSignedIn() { // 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(app1.INTERNAL.getUid()); + assertNull(authInternal1.getUid()); // Listen to Auth changes. var unsubscribe = auth1.onIdTokenChanged(function(currentUser) { // Unsubscribe of Auth state change listener. @@ -1024,7 +1027,7 @@ function testGetUid_userSignedIn() { // Logged in test user should be detected. // Confirm getUid() returns expected UID. assertEquals(accountInfo1['uid'], auth1.getUid()); - assertEquals(accountInfo1['uid'], app1.INTERNAL.getUid()); + assertEquals(accountInfo1['uid'], authInternal1.getUid()); goog.Timer.promise(10).then(function() { // Sign out. return auth1.signOut(); @@ -1033,7 +1036,7 @@ function testGetUid_userSignedIn() { }).then(function() { // getUid() should return null. assertNull(auth1.getUid()); - assertNull(app1.INTERNAL.getUid()); + assertNull(authInternal1.getUid()); asyncTestCase.signal(); }); }); @@ -1065,19 +1068,20 @@ function testGetUid_noUserSignedIn() { // 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(app1.INTERNAL.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'], app1.INTERNAL.getUid()); + assertEquals(accountInfo1['uid'], authInternal1.getUid()); asyncTestCase.signal(); }); }); @@ -1118,11 +1122,13 @@ function testNotifyAuthListeners() { config1['apiKey'] + ':' + appId1); currentUserStorageManager.setCurrentUser(user).then(function() { app1 = firebase.initializeApp(config1, appId1); - app1.INTERNAL.addAuthTokenListener(app1AuthTokenListener); auth1 = app1.auth(); + authInternal1 = app1.container.getProvider('auth-internal').getImmediate(); + authInternal1.addAuthTokenListener(app1AuthTokenListener); app2 = firebase.initializeApp(config2, appId2); - app2.INTERNAL.addAuthTokenListener(app2AuthTokenListener); 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()); @@ -1170,7 +1176,7 @@ function testNotifyAuthListeners() { app1AuthTokenListener.reset(); listener2.reset(); auth1.removeAuthTokenListener(listener2); - app1.INTERNAL.removeAuthTokenListener(app1AuthTokenListener); + authInternal1.removeAuthTokenListener(app1AuthTokenListener); // Force token change. currentAccessToken = 'accessToken3'; auth1['currentUser'].getIdToken().then(function(token) { @@ -10077,7 +10083,7 @@ function testAuth_proactiveTokenRefresh_multipleUsers() { auth1 = app1.auth(); var subscriber = function(token) {}; // Simulate Firebase service added. - app1.INTERNAL.addAuthTokenListener(subscriber); + app1.container.getProvider('auth-internal').getImmediate().addAuthTokenListener(subscriber); // Simulate user1 signed in. auth1.signInWithIdTokenResponse(expectedTokenResponse).then(function() { // Current user should be set to user1. @@ -10155,7 +10161,7 @@ function testAuth_proactiveTokenRefresh_firebaseServiceAddedAfterSignIn() { assertEquals( 0, fireauth.AuthUser.prototype.startProactiveRefresh.getCallCount()); // Simulate Firebase service added. - app1.INTERNAL.addAuthTokenListener(subscriber); + app1.container.getProvider('auth-internal').getImmediate().addAuthTokenListener(subscriber); // Confirm proactive refresh started on that user. assertEquals( 1, fireauth.AuthUser.prototype.startProactiveRefresh.getCallCount()); @@ -10199,10 +10205,11 @@ function testAuth_proactiveTokenRefresh_firebaseServiceRemovedAfterSignIn() { auth1 = app1.auth(); var subscriber = function(token) {}; // Simulate Firebase service added. - app1.INTERNAL.addAuthTokenListener(subscriber); + authInternal1 = app1.container.getProvider('auth-internal').getImmediate(); + authInternal1.addAuthTokenListener(subscriber); // Add same listener again to check that removing it once will ensure the // proactive refresh is stopped. - app1.INTERNAL.addAuthTokenListener(subscriber); + authInternal1.addAuthTokenListener(subscriber); // Simulate user signed in. auth1.signInWithIdTokenResponse(expectedTokenResponse).then(function() { // Current user should be set to user1. @@ -10214,7 +10221,7 @@ function testAuth_proactiveTokenRefresh_firebaseServiceRemovedAfterSignIn() { assertEquals( 0, fireauth.AuthUser.prototype.stopProactiveRefresh.getCallCount()); // Simulate Firebase service removed. - app1.INTERNAL.removeAuthTokenListener(subscriber); + authInternal1.removeAuthTokenListener(subscriber); // Confirm proactive refresh stopped on that user. assertEquals( 1, fireauth.AuthUser.prototype.stopProactiveRefresh.getCallCount()); diff --git a/packages/firebase/externs/firebase-app-internal-externs.js b/packages/firebase/externs/firebase-app-internal-externs.js index fc1ceba92c1..c900b2d6b33 100644 --- a/packages/firebase/externs/firebase-app-internal-externs.js +++ b/packages/firebase/externs/firebase-app-internal-externs.js @@ -20,21 +20,9 @@ */ /** - * @param {string} name Service name - * @param {!firebase.ServiceFactory} createService - * @param {Object=} serviceProperties - * @param {(function(string, !firebase.app.App): void)=} appHook - * @param {boolean=} allowMultipleInstances Whether the service registered - * supports multiple instances on the same app. - * @return {firebase.ServiceNamespace} - */ -firebase.INTERNAL.registerService = function( - name, - createService, - serviceProperties, - appHook, - allowMultipleInstances -) {}; + * @param {!firebase.FirebaseComponent} + */ +firebase.INTERNAL.registerComponent = function(component) {}; /** @param {!Object} props */ firebase.INTERNAL.extendNamespace = function(props) {};