Skip to content

Commit b67e044

Browse files
committed
Add sendEmailVerification to auth-next
1 parent 9190a4c commit b67e044

File tree

6 files changed

+157
-10
lines changed

6 files changed

+157
-10
lines changed

packages-exp/auth-exp/src/api/authentication/email_and_password.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export enum GetOobCodeRequestType {
5252
VERIFY_AND_CHANGE_EMAIL = 'VERIFY_AND_CHANGE_EMAIL'
5353
}
5454

55-
interface GetOobCodeRequest {
55+
export interface GetOobCodeRequest {
5656
email?: string; // Everything except VERIFY_AND_CHANGE_EMAIL
5757
continueUrl?: string;
5858
iosBundleId?: string;

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

+66-3
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818
import { FirebaseError } from '@firebase/util';
1919
import { expect, use } from 'chai';
2020
import * as chaiAsPromised from 'chai-as-promised';
21-
import { SinonStub, stub } from 'sinon';
21+
import * as sinonChai from 'sinon-chai';
22+
import { SinonStub, stub, restore } from 'sinon';
2223
import { mockEndpoint } from '../../../test/api/helper';
23-
import { mockAuth } from '../../../test/mock_auth';
24+
import { mockAuth, testUser } from '../../../test/mock_auth';
2425
import * as mockFetch from '../../../test/mock_fetch';
2526
import { Endpoint } from '../../api';
27+
import { GetOobCodeRequestType } from '../../api/authentication/email_and_password';
2628
import { ServerError } from '../../api/errors';
2729
import { ProviderId } from '../providers';
2830
import * as location from '../util/location';
29-
import { fetchSignInMethodsForEmail } from './email';
31+
import { fetchSignInMethodsForEmail, sendEmailVerification } from './email';
3032

3133
use(chaiAsPromised);
34+
use(sinonChai);
3235

3336
describe('fetchSignInMethodsForEmail', () => {
3437
const email = '[email protected]';
@@ -94,3 +97,63 @@ describe('fetchSignInMethodsForEmail', () => {
9497
expect(mock.calls.length).to.eq(1);
9598
});
9699
});
100+
101+
describe('sendEmailVerification', () => {
102+
const email = '[email protected]';
103+
const user = testUser('my-user-uid', email);
104+
const idToken = 'id-token';
105+
let idTokenStub: SinonStub;
106+
let reloadStub: SinonStub;
107+
108+
beforeEach(() => {
109+
mockFetch.setUp();
110+
idTokenStub = stub(user, 'getIdToken');
111+
idTokenStub.callsFake(async () => idToken);
112+
reloadStub = stub(user, 'reload');
113+
});
114+
115+
afterEach(() => {
116+
mockFetch.tearDown();
117+
restore();
118+
});
119+
120+
it('should send the email verification', async () => {
121+
const mock = mockEndpoint(Endpoint.SEND_OOB_CODE, {
122+
email
123+
});
124+
125+
await sendEmailVerification(mockAuth, user);
126+
127+
expect(reloadStub).to.not.have.been.called;
128+
expect(mock.calls[0].request).to.eql({
129+
requestType: GetOobCodeRequestType.VERIFY_EMAIL,
130+
idToken
131+
});
132+
});
133+
134+
it('should reload the user if the API returns a different email', async () => {
135+
const mock = mockEndpoint(Endpoint.SEND_OOB_CODE, {
136+
137+
});
138+
139+
await sendEmailVerification(mockAuth, user);
140+
141+
expect(reloadStub).to.have.been.calledOnce;
142+
expect(mock.calls[0].request).to.eql({
143+
requestType: GetOobCodeRequestType.VERIFY_EMAIL,
144+
idToken
145+
});
146+
});
147+
148+
context('on iOS', () => {
149+
it('should pass action code parameters', () => {
150+
151+
});
152+
});
153+
154+
context('on Android', () => {
155+
it('should pass action code parameters', () => {
156+
157+
});
158+
});
159+
});

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

+25-4
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
* limitations under the License.
1616
*/
1717

18-
import {
19-
createAuthUri,
20-
CreateAuthUriRequest
21-
} from '../../api/authentication/create_auth_uri';
18+
import { createAuthUri, CreateAuthUriRequest } from '../../api/authentication/create_auth_uri';
19+
import * as api from '../../api/authentication/email_and_password';
20+
import { ActionCodeSettings, setActionCodeSettingsOnRequest } from '../../model/action_code_settings';
2221
import { Auth } from '../../model/auth';
22+
import { User } from '../../model/user';
2323
import { getCurrentUrl, isHttpOrHttps } from '../util/location';
2424

2525
export async function fetchSignInMethodsForEmail(
@@ -39,3 +39,24 @@ export async function fetchSignInMethodsForEmail(
3939

4040
return signinMethods || [];
4141
}
42+
43+
export async function sendEmailVerification(
44+
auth: Auth,
45+
user: User,
46+
actionCodeSettings?: ActionCodeSettings
47+
): Promise<void> {
48+
const idToken = await user.getIdToken();
49+
const request: api.VerifyEmailRequest = {
50+
requestType: api.GetOobCodeRequestType.VERIFY_EMAIL,
51+
idToken
52+
};
53+
if (actionCodeSettings) {
54+
setActionCodeSettingsOnRequest(request, actionCodeSettings);
55+
}
56+
57+
const { email } = await api.sendEmailVerification(auth, request);
58+
59+
if (email !== user.email) {
60+
await user.reload();
61+
}
62+
}

packages-exp/auth-exp/src/model/action_code_info.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,11 @@ export enum Operation {
2121
EMAIL_SIGNIN = 'EMAIL_SIGNIN',
2222
VERIFY_EMAIL = 'VERIFY_EMAIL'
2323
}
24+
25+
export interface ActionCodeInfo {
26+
data: {
27+
email: string | null;
28+
fromEmail: string | null;
29+
};
30+
operation: string;
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 { GetOobCodeRequest } from '../api/authentication/email_and_password';
19+
20+
export interface ActionCodeSettings {
21+
android?: {
22+
installApp?: boolean;
23+
minimumVersion?: string;
24+
packageName: string;
25+
};
26+
handleCodeInApp?: boolean;
27+
iOS?: {
28+
bundleId: string;
29+
appStoreId: string;
30+
};
31+
url: string;
32+
dynamicLinkDomain?: string;
33+
}
34+
35+
export function setActionCodeSettingsOnRequest(
36+
request: GetOobCodeRequest,
37+
actionCodeSettings: ActionCodeSettings
38+
): void {
39+
request.continueUrl = actionCodeSettings.url;
40+
request.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;
41+
request.canHandleCodeInApp = actionCodeSettings.handleCodeInApp;
42+
43+
if (actionCodeSettings.iOS) {
44+
request.iosBundleId = actionCodeSettings.iOS.bundleId;
45+
request.iosAppStoreId = actionCodeSettings.iOS.appStoreId;
46+
}
47+
48+
if (actionCodeSettings.android) {
49+
request.androidInstallApp = actionCodeSettings.android.installApp;
50+
request.androidMinimumVersionCode =
51+
actionCodeSettings.android.minimumVersion;
52+
request.androidPackageName = actionCodeSettings.android.packageName;
53+
}
54+
}

packages-exp/auth-exp/test/mock_auth.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ export const mockAuth: Auth = {
3434
}
3535
};
3636

37-
export function testUser(uid: string): User {
37+
export function testUser(uid: string, email?: string): User {
3838
return new UserImpl({
3939
uid,
4040
auth: mockAuth,
41-
stsTokenManager: new StsTokenManager()
41+
stsTokenManager: new StsTokenManager(),
42+
email
4243
});
4344
}

0 commit comments

Comments
 (0)