Skip to content

Commit 8fe55d9

Browse files
authored
Add a standard OAuthCredential and a GoogleAuthProvider (#3498)
* Add a standard OAuthCredential and a GoogleAuthProvider * Formatting * Signature updates * Formatting * Update w/ demo * PR feedback plus a bugfix * Formatting
1 parent a84cb52 commit 8fe55d9

File tree

10 files changed

+562
-32
lines changed

10 files changed

+562
-32
lines changed

packages-exp/auth-exp/demo/rollup.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717
import resolve from '@rollup/plugin-node-resolve';
18-
import { terser } from "rollup-plugin-terser";
18+
import { terser } from 'rollup-plugin-terser';
1919
import strip from '@rollup/plugin-strip';
2020
import typescriptPlugin from 'rollup-plugin-typescript2';
2121
import typescript from 'typescript';

packages-exp/auth-exp/demo/src/index.js

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
verifyPasswordResetCode,
5656
getMultiFactorResolver,
5757
OAuthProvider,
58+
GoogleAuthProvider,
5859
signInWithPopup,
5960
linkWithPopup,
6061
reauthenticateWithPopup,
@@ -1214,20 +1215,20 @@ function onPopupRedirectSamlProviderClick() {
12141215
*/
12151216
function onPopupRedirectProviderClick(_event) {
12161217
const providerId = $(event.currentTarget).data('provider');
1217-
const provider = null;
1218+
let provider = null;
12181219
switch (providerId) {
1219-
// case 'google.com':
1220-
// provider = new GoogleAuthProvider();
1221-
// break;
1222-
// case 'facebook.com':
1223-
// provider = new FacebookAuthProvider();
1224-
// break;
1225-
// case 'github.com':
1226-
// provider = new GithubAuthProvider();
1227-
// break;
1228-
// case 'twitter.com':
1229-
// provider = new TwitterAuthProvider();
1230-
// break;
1220+
case 'google.com':
1221+
provider = new GoogleAuthProvider();
1222+
break;
1223+
// case 'facebook.com':
1224+
// provider = new FacebookAuthProvider();
1225+
// break;
1226+
// case 'github.com':
1227+
// provider = new GithubAuthProvider();
1228+
// break;
1229+
// case 'twitter.com':
1230+
// provider = new TwitterAuthProvider();
1231+
// break;
12311232
default:
12321233
return;
12331234
}

packages-exp/auth-exp/index.webworker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,16 @@ export function signOut(auth: externs.Auth): Promise<void> {
5151
return auth.signOut();
5252
}
5353

54+
// core/credentials
55+
export { OAuthCredential } from './src/core/credentials/oauth';
56+
5457
// core/persistence
5558
export { inMemoryPersistence } from './src/core/persistence/in_memory';
5659
export { indexedDBLocalPersistence } from './src/core/persistence/indexed_db';
5760

5861
// core/providers
5962
export { EmailAuthProvider } from './src/core/providers/email';
63+
export { GoogleAuthProvider } from './src/core/providers/google';
6064
export { OAuthProvider } from './src/core/providers/oauth';
6165
export { PhoneAuthProvider } from './src/core/providers/phone';
6266

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/**
2+
* @license
3+
* Copyright 2020 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 { expect } from 'chai';
19+
20+
import { ProviderId, SignInMethod } from '@firebase/auth-types-exp';
21+
22+
import { mockEndpoint } from '../../../test/helpers/api/helper';
23+
import { TEST_ID_TOKEN_RESPONSE } from '../../../test/helpers/id_token_response';
24+
import { testAuth } from '../../../test/helpers/mock_auth';
25+
import * as fetch from '../../../test/helpers/mock_fetch';
26+
import { Endpoint } from '../../api';
27+
import { SignInWithIdpRequest } from '../../api/authentication/idp';
28+
import { Auth } from '../../model/auth';
29+
import { OAuthCredential, OAuthCredentialParams } from './oauth';
30+
31+
const BASE_PARAMS: OAuthCredentialParams = {
32+
providerId: ProviderId.GOOGLE,
33+
signInMethod: SignInMethod.GOOGLE
34+
};
35+
36+
describe('src/core/credentials/oauth', () => {
37+
let auth: Auth;
38+
let signInWithIdp: fetch.Route;
39+
40+
beforeEach(async () => {
41+
auth = await testAuth();
42+
fetch.setUp();
43+
44+
signInWithIdp = mockEndpoint(Endpoint.SIGN_IN_WITH_IDP, {
45+
...TEST_ID_TOKEN_RESPONSE
46+
});
47+
});
48+
49+
afterEach(() => {
50+
fetch.tearDown();
51+
});
52+
53+
context('_fromParams', () => {
54+
it('sets the idToken and accessToken', () => {
55+
const cred = OAuthCredential._fromParams({
56+
...BASE_PARAMS,
57+
idToken: 'id-token',
58+
accessToken: 'access-token'
59+
});
60+
61+
expect(cred.idToken).to.eq('id-token');
62+
expect(cred.accessToken).to.eq('access-token');
63+
});
64+
65+
it('sets the nonce only if pendingToken is missing', () => {
66+
const cred = OAuthCredential._fromParams({
67+
...BASE_PARAMS,
68+
idToken: 'id-token',
69+
accessToken: 'access-token',
70+
nonce: 'nonce'
71+
});
72+
73+
expect(cred.nonce).to.eq('nonce');
74+
});
75+
76+
it('ignores the nonce if pendingToken set', () => {
77+
const cred = OAuthCredential._fromParams({
78+
...BASE_PARAMS,
79+
nonce: 'nonce',
80+
idToken: 'id-token',
81+
accessToken: 'access-token',
82+
pendingToken: 'pending-token'
83+
});
84+
85+
expect(cred.nonce).to.be.undefined;
86+
});
87+
88+
it('handles oauth1 and oauth with token secret', () => {
89+
const cred = OAuthCredential._fromParams({
90+
...BASE_PARAMS,
91+
oauthToken: 'oauth-token',
92+
oauthTokenSecret: 'oauth-token-secret'
93+
});
94+
95+
expect(cred.accessToken).to.eq('oauth-token');
96+
expect(cred.secret).to.eq('oauth-token-secret');
97+
});
98+
});
99+
100+
context('#toJSON', () => {
101+
it('packs up everything', () => {
102+
const cred = OAuthCredential._fromParams({
103+
...BASE_PARAMS,
104+
idToken: 'id-token',
105+
accessToken: 'access-token',
106+
pendingToken: 'pending-token'
107+
});
108+
109+
expect(cred.toJSON()).to.eql({
110+
...BASE_PARAMS,
111+
idToken: 'id-token',
112+
accessToken: 'access-token',
113+
pendingToken: 'pending-token',
114+
secret: undefined,
115+
nonce: undefined
116+
});
117+
});
118+
});
119+
120+
context('fromJSON', () => {
121+
it('builds the new object correctly', () => {
122+
const cred = OAuthCredential.fromJSON({
123+
...BASE_PARAMS,
124+
idToken: 'id-token',
125+
accessToken: 'access-token',
126+
pendingToken: 'pending-token'
127+
});
128+
129+
expect(cred).to.be.instanceOf(OAuthCredential);
130+
expect(cred!.idToken).to.eq('id-token');
131+
expect(cred!.accessToken).to.eq('access-token');
132+
expect(cred!.providerId).to.eq(BASE_PARAMS.providerId);
133+
expect(cred!.signInMethod).to.eq(BASE_PARAMS.signInMethod);
134+
});
135+
});
136+
137+
context('#makeRequest', () => {
138+
it('sets all the fields in a querystring if using nonce', async () => {
139+
await OAuthCredential._fromParams({
140+
...BASE_PARAMS,
141+
idToken: 'id-token',
142+
accessToken: 'access-token',
143+
nonce: 'nonce'
144+
})._getIdTokenResponse(auth);
145+
146+
const { postBody, ...rest } = signInWithIdp.calls[0]
147+
.request as SignInWithIdpRequest;
148+
expect(rest.requestUri).to.eq('http://localhost');
149+
expect(rest.returnSecureToken).to.be.true;
150+
expect(postBody).to.contain('id_token=id-token');
151+
expect(postBody).to.contain('access_token=access-token');
152+
expect(postBody).to.contain('nonce=nonce');
153+
expect(postBody).to.contain('providerId=google.com');
154+
});
155+
156+
it('if pendingToken is present, post body is not set', async () => {
157+
await OAuthCredential._fromParams({
158+
...BASE_PARAMS,
159+
idToken: 'id-token',
160+
accessToken: 'access-token',
161+
nonce: 'nonce',
162+
pendingToken: 'pending-token'
163+
})._getIdTokenResponse(auth);
164+
165+
const request = signInWithIdp.calls[0].request as SignInWithIdpRequest;
166+
expect(request.requestUri).to.eq('http://localhost');
167+
expect(request.returnSecureToken).to.be.true;
168+
expect(request.pendingToken).to.eq('pending-token');
169+
expect(request.postBody).to.be.null;
170+
});
171+
});
172+
173+
context('internal methods', () => {
174+
let cred: OAuthCredential;
175+
176+
beforeEach(() => {
177+
cred = OAuthCredential._fromParams({
178+
...BASE_PARAMS,
179+
idToken: 'id-token',
180+
accessToken: 'access-token'
181+
});
182+
});
183+
184+
it('_getIdTokenResponse calls through correctly', async () => {
185+
await cred._getIdTokenResponse(auth);
186+
187+
const request = signInWithIdp.calls[0].request as SignInWithIdpRequest;
188+
expect(typeof request.postBody).to.eq('string');
189+
});
190+
191+
it('_linkToIdToken sets the idToken field on the request', async () => {
192+
await cred._linkToIdToken(auth, 'new-id-token');
193+
const request = signInWithIdp.calls[0].request as SignInWithIdpRequest;
194+
expect(typeof request.postBody).to.eq('string');
195+
expect(request.idToken).to.eq('new-id-token');
196+
});
197+
198+
it('_getReauthenticationResolver sets autoCreate to false', async () => {
199+
await cred._getReauthenticationResolver(auth);
200+
const request = signInWithIdp.calls[0].request as SignInWithIdpRequest;
201+
expect(typeof request.postBody).to.eq('string');
202+
expect(request.autoCreate).to.be.false;
203+
});
204+
});
205+
});

0 commit comments

Comments
 (0)