Skip to content

Commit b7da2a3

Browse files
committed
Big refactor of reauth / link + basic idp tasks
1 parent a575f2d commit b7da2a3

16 files changed

+840
-317
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
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 } from '@firebase/auth-types-exp';
21+
22+
import { TEST_ID_TOKEN_RESPONSE } from '../../../test/id_token_response';
23+
import { _authCredentialFromTokenResponse } from './inferred';
24+
25+
describe('src/core/credentials/inferred', () => {
26+
it('returns a phone credential if response conains correct fields', () => {
27+
const cred = _authCredentialFromTokenResponse({
28+
...TEST_ID_TOKEN_RESPONSE,
29+
temporaryProof: 'temporary-proof',
30+
phoneNumber: 'phone-number'
31+
});
32+
33+
expect(cred!.providerId).to.eq(ProviderId.PHONE);
34+
});
35+
36+
it('returns null if nothing matches', () => {
37+
const cred = _authCredentialFromTokenResponse({
38+
...TEST_ID_TOKEN_RESPONSE,
39+
});
40+
41+
expect(cred).to.be.null;
42+
});
43+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 { PhoneOrOauthTokenResponse } from '../../api/authentication/mfa';
19+
import { SignInWithPhoneNumberResponse } from '../../api/authentication/sms';
20+
import { AuthCredential } from './';
21+
import { PhoneAuthCredential } from './phone';
22+
23+
export function _authCredentialFromTokenResponse(
24+
response: PhoneOrOauthTokenResponse
25+
): AuthCredential | null {
26+
const {
27+
temporaryProof,
28+
phoneNumber
29+
} = response as SignInWithPhoneNumberResponse;
30+
if (temporaryProof && phoneNumber) {
31+
return new PhoneAuthCredential({ temporaryProof, phoneNumber });
32+
}
33+
34+
// TODO: Handle Oauth cases
35+
return null;
36+
}

packages-exp/auth-exp/src/core/strategies/credential.test.ts

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,7 @@
1818
import { expect, use } from 'chai';
1919
import * as chaiAsPromised from 'chai-as-promised';
2020

21-
import {
22-
OperationType,
23-
ProviderId,
24-
SignInMethod
25-
} from '@firebase/auth-types-exp';
21+
import { OperationType, ProviderId, SignInMethod } from '@firebase/auth-types-exp';
2622
import { FirebaseError } from '@firebase/util';
2723

2824
import { mockEndpoint } from '../../../test/api/helper';
@@ -36,10 +32,7 @@ import { Auth } from '../../model/auth';
3632
import { IdTokenResponse } from '../../model/id_token';
3733
import { User } from '../../model/user';
3834
import {
39-
_assertLinkedStatus,
40-
linkWithCredential,
41-
reauthenticateWithCredential,
42-
signInWithCredential
35+
linkWithCredential, reauthenticateWithCredential, signInWithCredential
4336
} from './credential';
4437

4538
use(chaiAsPromised);
@@ -167,51 +160,4 @@ describe('core/strategies/credential', () => {
167160
expect(credential).to.be.null;
168161
});
169162
});
170-
171-
describe('_assertLinkedStatus', () => {
172-
it('should error with already linked if expectation is true', async () => {
173-
getAccountInfoEndpoint.response = {
174-
users: [
175-
{
176-
...serverUser,
177-
providerUserInfo: [{ providerId: ProviderId.GOOGLE }]
178-
}
179-
]
180-
};
181-
182-
await expect(
183-
_assertLinkedStatus(false, user, ProviderId.GOOGLE)
184-
).to.be.rejectedWith(
185-
FirebaseError,
186-
'Firebase: User can only be linked to one identity for the given provider. (auth/provider-already-linked).'
187-
);
188-
});
189-
190-
it('should not error if provider is not linked', async () => {
191-
await expect(_assertLinkedStatus(false, user, ProviderId.GOOGLE)).not.to
192-
.be.rejected;
193-
});
194-
195-
it('should error if provider is not linked but it was expected to be', async () => {
196-
await expect(
197-
_assertLinkedStatus(true, user, ProviderId.GOOGLE)
198-
).to.be.rejectedWith(
199-
FirebaseError,
200-
'Firebase: User was not linked to an account with the given provider. (auth/no-such-provider).'
201-
);
202-
});
203-
204-
it('should not error if provider is linked and that is expected', async () => {
205-
getAccountInfoEndpoint.response = {
206-
users: [
207-
{
208-
...serverUser,
209-
providerUserInfo: [{ providerId: ProviderId.GOOGLE }]
210-
}
211-
]
212-
};
213-
await expect(_assertLinkedStatus(true, user, ProviderId.GOOGLE)).not.to.be
214-
.rejected;
215-
});
216-
});
217163
});

packages-exp/auth-exp/src/core/strategies/credential.ts

Lines changed: 7 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,14 @@
1818
import * as externs from '@firebase/auth-types-exp';
1919
import { OperationType, UserCredential } from '@firebase/auth-types-exp';
2020

21-
import { PhoneOrOauthTokenResponse } from '../../api/authentication/mfa';
22-
import { SignInWithPhoneNumberResponse } from '../../api/authentication/sms';
2321
import { Auth } from '../../model/auth';
24-
import { IdTokenResponse } from '../../model/id_token';
2522
import { User } from '../../model/user';
2623
import { AuthCredential } from '../credentials';
27-
import { PhoneAuthCredential } from '../credentials/phone';
28-
import { AuthErrorCode } from '../errors';
2924
import { _parseToken } from '../user/id_token_result';
25+
import { _assertLinkedStatus, _link } from '../user/link_unlink';
26+
import { _reauthenticate } from '../user/reauthenticate';
3027
import { _reloadWithoutSaving } from '../user/reload';
3128
import { UserCredentialImpl } from '../user/user_credential_impl';
32-
import { assert, fail } from '../util/assert';
33-
import { providerDataAsNames } from '../util/providers';
3429

3530
export async function signInWithCredential(
3631
authExtern: externs.Auth,
@@ -56,14 +51,13 @@ export async function linkWithCredential(
5651
): Promise<UserCredential> {
5752
const user = userExtern as User;
5853
const credential = credentialExtern as AuthCredential;
54+
5955
await _assertLinkedStatus(false, user, credential.providerId);
6056

61-
const response = await credential._linkToIdToken(
57+
return _link(user, credential._linkToIdToken(
6258
user.auth,
63-
await user.getIdToken()
64-
);
65-
66-
return userCredForOperation(user, OperationType.LINK, response);
59+
await user.getIdToken(),
60+
));
6761
}
6862

6963
export async function reauthenticateWithCredential(
@@ -73,76 +67,6 @@ export async function reauthenticateWithCredential(
7367
const credential = credentialExtern as AuthCredential;
7468
const user = userExtern as User;
7569

76-
const { auth, uid } = user;
77-
const response = await verifyTokenResponseUid(
78-
credential._getReauthenticationResolver(auth),
79-
uid,
80-
auth.name
81-
);
82-
83-
return userCredForOperation(user, OperationType.REAUTHENTICATE, response);
84-
}
85-
86-
export function _authCredentialFromTokenResponse(
87-
response: PhoneOrOauthTokenResponse
88-
): AuthCredential | null {
89-
const {
90-
temporaryProof,
91-
phoneNumber
92-
} = response as SignInWithPhoneNumberResponse;
93-
if (temporaryProof && phoneNumber) {
94-
return new PhoneAuthCredential({ temporaryProof, phoneNumber });
95-
}
96-
97-
// TODO: Handle Oauth cases
98-
return null;
70+
return _reauthenticate(user, credential._getReauthenticationResolver(user.auth));
9971
}
10072

101-
export async function _assertLinkedStatus(
102-
expected: boolean,
103-
user: User,
104-
provider: externs.ProviderId
105-
): Promise<void> {
106-
await _reloadWithoutSaving(user);
107-
const providerIds = providerDataAsNames(user.providerData);
108-
109-
const code =
110-
expected === false
111-
? AuthErrorCode.PROVIDER_ALREADY_LINKED
112-
: AuthErrorCode.NO_SUCH_PROVIDER;
113-
assert(providerIds.has(provider) === expected, user.auth.name, code);
114-
}
115-
116-
async function verifyTokenResponseUid(
117-
idTokenResolver: Promise<IdTokenResponse>,
118-
uid: string,
119-
appName: string
120-
): Promise<IdTokenResponse> {
121-
try {
122-
const response = await idTokenResolver;
123-
assert(response.idToken, appName, AuthErrorCode.INTERNAL_ERROR);
124-
const parsed = _parseToken(response.idToken);
125-
assert(parsed, appName, AuthErrorCode.INTERNAL_ERROR);
126-
127-
const { sub: localId } = parsed;
128-
assert(uid === localId, appName, AuthErrorCode.USER_MISMATCH);
129-
130-
return response;
131-
} catch (e) {
132-
// Convert user deleted error into user mismatch
133-
if (e?.code === `auth/${AuthErrorCode.USER_DELETED}`) {
134-
fail(appName, AuthErrorCode.USER_MISMATCH);
135-
}
136-
throw e;
137-
}
138-
}
139-
140-
async function userCredForOperation(
141-
user: User,
142-
opType: OperationType,
143-
response: IdTokenResponse
144-
): Promise<UserCredentialImpl> {
145-
const newCred = _authCredentialFromTokenResponse(response);
146-
await user._updateTokensIfNecessary(response, /* reload */ true);
147-
return new UserCredentialImpl(user, newCred, opType);
148-
}

0 commit comments

Comments
 (0)