Skip to content

Commit 29a9bb6

Browse files
committed
Add bare-bones Cordova configuration / popup-redirect resolver
1 parent 8788d50 commit 29a9bb6

File tree

14 files changed

+388
-28
lines changed

14 files changed

+388
-28
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* This is the file that people using React Native will actually import. You
20+
* should only include this file if you have something specific about your
21+
* implementation that mandates having a separate entrypoint. Otherwise you can
22+
* just use index.ts
23+
*/
24+
25+
import { FirebaseApp } from '@firebase/app-types-exp';
26+
import { Auth } from '@firebase/auth-types-exp';
27+
import { indexedDBLocalPersistence } from './src/platform_browser/persistence/indexed_db';
28+
29+
import { initializeAuth } from './src';
30+
import { registerAuth } from './src/core/auth/register';
31+
import { ClientPlatform } from './src/core/util/version';
32+
33+
// Core functionality shared by all clients
34+
export * from './src';
35+
36+
// Cordova also supports indexedDB / browserSession / browserLocal
37+
export {indexedDBLocalPersistence} from './src/platform_browser/persistence/indexed_db';
38+
export {browserLocalPersistence} from './src/platform_browser/persistence/local_storage';
39+
export {browserSessionPersistence} from './src/platform_browser/persistence/session_storage';
40+
41+
export {cordovaPopupRedirectResolver} from './src/platform_cordova/popup_redirect';
42+
export {signInWithRedirect} from './src/platform_cordova/strategies/redirect';
43+
44+
import {cordovaPopupRedirectResolver} from './src/platform_cordova/popup_redirect';
45+
46+
export function getAuth(app: FirebaseApp): Auth {
47+
return initializeAuth(app, {
48+
persistence: indexedDBLocalPersistence,
49+
popupRedirectResolver: cordovaPopupRedirectResolver,
50+
});
51+
}
52+
53+
registerAuth(ClientPlatform.CORDOVA);

packages-exp/auth-exp/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"main": "dist/node/index.js",
88
"react-native": "dist/rn/index.js",
99
"browser": "dist/esm5/index.js",
10+
"cordova": "dist/cordova/index.esm5.js",
1011
"module": "dist/esm5/index.js",
1112
"esm2017": "dist/esm2017/index.js",
1213
"webworker": "dist/index.webworker.esm5.js",
@@ -26,7 +27,7 @@
2627
"test:browser:integration": "karma start --single-run --integration",
2728
"test:browser:debug": "karma start --auto-watch",
2829
"test:browser:unit:debug": "karma start --auto-watch --unit",
29-
"test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'src/!(platform_browser|platform_react_native)/**/*.test.ts' --file index.node.ts --config ../../config/mocharc.node.js",
30+
"test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'src/!(platform_browser|platform_react_native|platform_cordova)/**/*.test.ts' --file index.node.ts --config ../../config/mocharc.node.js",
3031
"api-report": "api-extractor run --local --verbose",
3132
"predoc": "node ../../scripts/exp/remove-exp.js temp",
3233
"doc": "api-documenter markdown --input temp --output docs",

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,17 @@ export class AuthImpl implements Auth, _FirebaseService {
101101
persistenceHierarchy: Persistence[],
102102
popupRedirectResolver?: externs.PopupRedirectResolver
103103
): Promise<void> {
104+
if (popupRedirectResolver) {
105+
this._popupRedirectResolver = _getInstance(popupRedirectResolver);
106+
}
107+
104108
// Have to check for app deletion throughout initialization (after each
105109
// promise resolution)
106110
this._initializationPromise = this.queue(async () => {
107111
if (this._deleted) {
108112
return;
109113
}
110114

111-
if (popupRedirectResolver) {
112-
this._popupRedirectResolver = _getInstance(popupRedirectResolver);
113-
}
114-
115115
this.persistenceManager = await PersistenceUserManager.create(
116116
this,
117117
persistenceHierarchy

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ function getVersionForPlatform(
4141
return 'rn';
4242
case ClientPlatform.WORKER:
4343
return 'webworker';
44+
case ClientPlatform.CORDOVA:
45+
return 'cordova';
4446
default:
4547
return undefined;
4648
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,10 @@ export interface AuthErrorParams extends GenericAuthErrorParams {
402402
appName: AppName;
403403
serverResponse: IdTokenMfaResponse;
404404
};
405+
[AuthErrorCode.INVALID_CORDOVA_CONFIGURATION]: {
406+
appName: AppName;
407+
missingPlugin?: string;
408+
}
405409
}
406410

407411
export const _DEFAULT_AUTH_ERROR_FACTORY = new ErrorFactory<
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as externs from '@firebase/auth-types-exp';
19+
import { Auth } from '../../model/auth';
20+
import { PopupRedirectResolver } from '../../model/popup_redirect';
21+
import { AuthErrorCode } from '../errors';
22+
import { _assert } from './assert';
23+
import { _getInstance } from './instantiator';
24+
25+
/**
26+
* Chooses a popup/redirect resolver to use. This prefers the override (which
27+
* is directly passed in), and falls back to the property set on the auth
28+
* object. If neither are available, this function errors w/ an argument error.
29+
*
30+
* @internal
31+
*/
32+
export function _withDefaultResolver(
33+
auth: Auth,
34+
resolverOverride: externs.PopupRedirectResolver | undefined
35+
): PopupRedirectResolver {
36+
if (resolverOverride) {
37+
return _getInstance(resolverOverride);
38+
}
39+
40+
_assert(auth._popupRedirectResolver, auth, AuthErrorCode.ARGUMENT_ERROR);
41+
42+
return auth._popupRedirectResolver;
43+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const enum ClientPlatform {
2727
BROWSER = 'Browser',
2828
NODE = 'Node',
2929
REACT_NATIVE = 'ReactNative',
30+
CORDOVA = 'Cordova',
3031
WORKER = 'Worker'
3132
}
3233

packages-exp/auth-exp/src/platform_browser/popup_redirect.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import { SDK_VERSION } from '@firebase/app-exp';
1919
import * as externs from '@firebase/auth-types-exp';
2020
import { isEmpty, querystring } from '@firebase/util';
21-
import { _getInstance } from '../core/util/instantiator';
2221

2322
import { AuthEventManager } from '../core/auth/auth_event_manager';
2423
import { AuthErrorCode } from '../core/errors';
@@ -72,26 +71,6 @@ interface ManagerOrPromise {
7271
promise?: Promise<EventManager>;
7372
}
7473

75-
/**
76-
* Chooses a popup/redirect resolver to use. This prefers the override (which
77-
* is directly passed in), and falls back to the property set on the auth
78-
* object. If neither are available, this function errors w/ an argument error.
79-
*
80-
* @internal
81-
*/
82-
export function _withDefaultResolver(
83-
auth: Auth,
84-
resolverOverride: externs.PopupRedirectResolver | undefined
85-
): PopupRedirectResolver {
86-
if (resolverOverride) {
87-
return _getInstance(resolverOverride);
88-
}
89-
90-
_assert(auth._popupRedirectResolver, auth, AuthErrorCode.ARGUMENT_ERROR);
91-
92-
return auth._popupRedirectResolver;
93-
}
94-
9574
class BrowserPopupRedirectResolver implements PopupRedirectResolver {
9675
private readonly eventManagers: Record<string, ManagerOrPromise> = {};
9776
private readonly iframes: Record<string, gapi.iframes.Iframe> = {};

packages-exp/auth-exp/src/platform_browser/strategies/popup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
PopupRedirectResolver
3030
} from '../../model/popup_redirect';
3131
import { User } from '../../model/user';
32-
import { _withDefaultResolver } from '../popup_redirect';
32+
import { _withDefaultResolver } from '../../core/util/resolver';
3333
import { AuthPopup } from '../util/popup';
3434
import { AbstractPopupRedirectOperation } from './abstract_popup_redirect_operation';
3535

packages-exp/auth-exp/src/platform_browser/strategies/redirect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
PopupRedirectResolver
3131
} from '../../model/popup_redirect';
3232
import { User, UserCredential } from '../../model/user';
33-
import { _withDefaultResolver } from '../popup_redirect';
33+
import { _withDefaultResolver } from '../../core/util/resolver';
3434
import { AbstractPopupRedirectOperation } from './abstract_popup_redirect_operation';
3535

3636
/**
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// For some reason, the linter doesn't recognize that these are used elsewhere
19+
// in the SDK
20+
/* eslint-disable @typescript-eslint/no-unused-vars */
21+
22+
declare namespace cordova.plugins.browsertab {
23+
function isAvailable(cb: (available: boolean) => void): void;
24+
function openUrl(url: string): void;
25+
}
26+
27+
declare namespace cordova.InAppBrowser {
28+
function open(url: string, target: string, options: string): unknown;
29+
}
30+
31+
declare namespace universalLinks {
32+
function subscribe(n: null, cb: (event: unknown) => void): void;
33+
}
34+
35+
declare namespace BuildInfo {
36+
const packageName: string;
37+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* @license
3+
* Copyright 2021 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as chaiAsPromised from 'chai-as-promised';
19+
import * as sinonChai from 'sinon-chai';
20+
import { expect, use } from 'chai';
21+
import { testAuth, TestAuth } from '../../test/helpers/mock_auth';
22+
import { SingletonInstantiator } from '../core/util/instantiator';
23+
import { AuthEventType, PopupRedirectResolver } from '../model/popup_redirect';
24+
import { cordovaPopupRedirectResolver } from './popup_redirect';
25+
import { GoogleAuthProvider } from '../core/providers/google';
26+
import { FirebaseError } from '@firebase/util';
27+
28+
use(chaiAsPromised);
29+
use(sinonChai);
30+
31+
describe('platform_cordova/popup_redirect', () => {
32+
let auth: TestAuth;
33+
let resolver: PopupRedirectResolver;
34+
35+
beforeEach(async () => {
36+
auth = await testAuth();
37+
resolver = new (cordovaPopupRedirectResolver as SingletonInstantiator<
38+
PopupRedirectResolver
39+
>)();
40+
});
41+
42+
describe('_openRedirect plugin checks', () => {
43+
// TODO: Rest of the tests go here
44+
it('does not reject if all plugins installed', () => {
45+
setExpectedPluginsButMissing([]);
46+
expect(() => resolver._openRedirect(auth, new GoogleAuthProvider(), AuthEventType.SIGN_IN_VIA_REDIRECT))
47+
.not.to.throw;
48+
});
49+
50+
it('rejects if universal links is missing', () => {
51+
setExpectedPluginsButMissing(['universalLinks.subscribe']);
52+
expect(() => resolver._openRedirect(auth, new GoogleAuthProvider(), AuthEventType.SIGN_IN_VIA_REDIRECT))
53+
.to.throw(FirebaseError, 'auth/invalid-cordova-configuration');
54+
});
55+
56+
it('rejects if build info is missing', () => {
57+
setExpectedPluginsButMissing(['BuildInfo.packageName']);
58+
expect(() => resolver._openRedirect(auth, new GoogleAuthProvider(), AuthEventType.SIGN_IN_VIA_REDIRECT))
59+
.to.throw(FirebaseError, 'auth/invalid-cordova-configuration');
60+
});
61+
62+
it('rejects if browsertab openUrl', () => {
63+
setExpectedPluginsButMissing(['cordova.plugins.browsertab.openUrl']);
64+
expect(() => resolver._openRedirect(auth, new GoogleAuthProvider(), AuthEventType.SIGN_IN_VIA_REDIRECT))
65+
.to.throw(FirebaseError, 'auth/invalid-cordova-configuration');
66+
});
67+
68+
it('rejects if InAppBrowser is missing', () => {
69+
setExpectedPluginsButMissing(['cordova.InAppBrowser.open']);
70+
expect(() => resolver._openRedirect(auth, new GoogleAuthProvider(), AuthEventType.SIGN_IN_VIA_REDIRECT))
71+
.to.throw(FirebaseError, 'auth/invalid-cordova-configuration');
72+
});
73+
});
74+
});
75+
76+
function setExpectedPluginsButMissing(missingPlugins: string[]): void {
77+
// eslint-disable @typescript-eslint/no-any We don't want a strictly typed window
78+
const win: any = window;
79+
// Eventually these will be replaced with mocks
80+
win.cordova = {
81+
plugins: {
82+
browsertab: {
83+
isAvailable: () => {},
84+
openUrl: () => {},
85+
}
86+
},
87+
InAppBrowser: {
88+
open: () => {},
89+
},
90+
};
91+
win.universalLinks = {
92+
subscribe: () => {},
93+
};
94+
win.BuildInfo = {
95+
packageName: 'com.name.package',
96+
};
97+
98+
for (const missing of missingPlugins) {
99+
const parts = missing.split('.');
100+
const last = parts.pop()!;
101+
// eslint-disable @typescript-eslint/no-any We're just traversing an object map
102+
let obj: any = win;
103+
for (const p of parts) {
104+
obj = obj[p];
105+
}
106+
delete obj[last];
107+
}
108+
}

0 commit comments

Comments
 (0)