Skip to content

Commit 6c7c326

Browse files
Implements the Admin SDK API to generate email action links. (#364)
Implements the Admin SDK API to generate email action links. This includes the APIs: ``` generatePasswordResetLink( email: string, actionCodeSettings?: admin.auth.ActionCodeSettings, ): Promise<string>; generateEmailVerificationLink( email: string, actionCodeSettings?: admin.auth.ActionCodeSettings, ): Promise<string>; generateSignInWithEmailLink( email: string, actionCodeSettings?: admin.auth.ActionCodeSettings, ): Promise<string>; ``` In addition, refactors Auth API endpoints to use new OP compliant endpoints. Defines BaseAuth class as this will be extended in the future to support different types of Auth instances.
1 parent 977222a commit 6c7c326

15 files changed

+1462
-134
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
- [feature] Added the email action link generation APIs for creating links for
4+
password reset, email verification and email link sign-in via
5+
`auth.generatePasswordResetLink()`, `auth.generateEmailVerificationLink()`
6+
and `auth.generateSignInWithEmailLink()`.
37
- [changed] Upgraded Cloud Firestore client to v0.19.0.
48
- [added] Exposed the `Transaction` type from the `admin.firestore` namespace.
59
- [fixed] Fixing error handling in FCM. The SDK now checks the key
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*!
2+
* Copyright 2018 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as validator from '../utils/validator';
18+
import {AuthClientErrorCode, FirebaseAuthError} from '../utils/error';
19+
20+
/** Defines the ActionCodeSettings interface. */
21+
export interface ActionCodeSettings {
22+
url: string;
23+
handleCodeInApp?: boolean;
24+
iOS?: {
25+
bundleId: string;
26+
};
27+
android?: {
28+
packageName: string;
29+
installApp?: boolean;
30+
minimumVersion?: string;
31+
};
32+
dynamicLinkDomain?: string;
33+
}
34+
35+
/** Defines the email action code server request. */
36+
interface EmailActionCodeRequest {
37+
continueUrl?: string;
38+
canHandleCodeInApp?: boolean;
39+
dynamicLinkDomain?: string;
40+
androidPackageName?: string;
41+
androidMinimumVersion: string;
42+
androidInstallApp?: boolean;
43+
iOSBundleId?: string;
44+
}
45+
46+
/**
47+
* Defines the ActionCodeSettings builder class used to convert the
48+
* ActionCodeSettings object to its corresponding server request.
49+
*/
50+
export class ActionCodeSettingsBuilder {
51+
private continueUrl?: string;
52+
private apn?: string;
53+
private amv?: string;
54+
private installApp?: boolean;
55+
private ibi?: string;
56+
private canHandleCodeInApp?: boolean;
57+
private dynamicLinkDomain?: string;
58+
59+
/**
60+
* ActionCodeSettingsBuilder constructor.
61+
*
62+
* @param {ActionCodeSettings} actionCodeSettings The ActionCodeSettings
63+
* object used to initiliaze this server request builder.
64+
* @constructor
65+
*/
66+
constructor(actionCodeSettings: ActionCodeSettings) {
67+
if (!validator.isNonNullObject(actionCodeSettings)) {
68+
throw new FirebaseAuthError(
69+
AuthClientErrorCode.INVALID_ARGUMENT,
70+
'"ActionCodeSettings" must be a non-null object.',
71+
);
72+
}
73+
if (typeof actionCodeSettings.url === 'undefined') {
74+
throw new FirebaseAuthError(
75+
AuthClientErrorCode.MISSING_CONTINUE_URI,
76+
);
77+
} else if (!validator.isURL(actionCodeSettings.url)) {
78+
throw new FirebaseAuthError(
79+
AuthClientErrorCode.INVALID_CONTINUE_URI,
80+
);
81+
}
82+
this.continueUrl = actionCodeSettings.url;
83+
84+
if (typeof actionCodeSettings.handleCodeInApp !== 'undefined' &&
85+
!validator.isBoolean(actionCodeSettings.handleCodeInApp)) {
86+
throw new FirebaseAuthError(
87+
AuthClientErrorCode.INVALID_ARGUMENT,
88+
'"ActionCodeSettings.handleCodeInApp" must be a boolean.',
89+
);
90+
}
91+
this.canHandleCodeInApp = actionCodeSettings.handleCodeInApp || false;
92+
93+
if (typeof actionCodeSettings.dynamicLinkDomain !== 'undefined' &&
94+
!validator.isNonEmptyString(actionCodeSettings.dynamicLinkDomain)) {
95+
throw new FirebaseAuthError(
96+
AuthClientErrorCode.INVALID_DYNAMIC_LINK_DOMAIN,
97+
);
98+
}
99+
this.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;
100+
101+
if (typeof actionCodeSettings.iOS !== 'undefined') {
102+
if (!validator.isNonNullObject(actionCodeSettings.iOS)) {
103+
throw new FirebaseAuthError(
104+
AuthClientErrorCode.INVALID_ARGUMENT,
105+
'"ActionCodeSettings.iOS" must be a valid non-null object.',
106+
);
107+
} else if (typeof actionCodeSettings.iOS.bundleId === 'undefined') {
108+
throw new FirebaseAuthError(
109+
AuthClientErrorCode.MISSING_IOS_BUNDLE_ID,
110+
);
111+
} else if (!validator.isNonEmptyString(actionCodeSettings.iOS.bundleId)) {
112+
throw new FirebaseAuthError(
113+
AuthClientErrorCode.INVALID_ARGUMENT,
114+
'"ActionCodeSettings.iOS.bundleId" must be a valid non-empty string.',
115+
);
116+
}
117+
this.ibi = actionCodeSettings.iOS.bundleId;
118+
}
119+
120+
if (typeof actionCodeSettings.android !== 'undefined') {
121+
if (!validator.isNonNullObject(actionCodeSettings.android)) {
122+
throw new FirebaseAuthError(
123+
AuthClientErrorCode.INVALID_ARGUMENT,
124+
'"ActionCodeSettings.android" must be a valid non-null object.',
125+
);
126+
} else if (typeof actionCodeSettings.android.packageName === 'undefined') {
127+
throw new FirebaseAuthError(
128+
AuthClientErrorCode.MISSING_ANDROID_PACKAGE_NAME,
129+
);
130+
} else if (!validator.isNonEmptyString(actionCodeSettings.android.packageName)) {
131+
throw new FirebaseAuthError(
132+
AuthClientErrorCode.INVALID_ARGUMENT,
133+
'"ActionCodeSettings.android.packageName" must be a valid non-empty string.',
134+
);
135+
} else if (typeof actionCodeSettings.android.minimumVersion !== 'undefined' &&
136+
!validator.isNonEmptyString(actionCodeSettings.android.minimumVersion)) {
137+
throw new FirebaseAuthError(
138+
AuthClientErrorCode.INVALID_ARGUMENT,
139+
'"ActionCodeSettings.android.minimumVersion" must be a valid non-empty string.',
140+
);
141+
} else if (typeof actionCodeSettings.android.installApp !== 'undefined' &&
142+
!validator.isBoolean(actionCodeSettings.android.installApp)) {
143+
throw new FirebaseAuthError(
144+
AuthClientErrorCode.INVALID_ARGUMENT,
145+
'"ActionCodeSettings.android.installApp" must be a valid boolean.',
146+
);
147+
}
148+
this.apn = actionCodeSettings.android.packageName;
149+
this.amv = actionCodeSettings.android.minimumVersion;
150+
this.installApp = actionCodeSettings.android.installApp || false;
151+
}
152+
}
153+
154+
/**
155+
* Returns the corresponding constructed server request corresponding to the
156+
* current ActionCodeSettings.
157+
*
158+
* @return {EmailActionCodeRequest} The constructed EmailActionCodeRequest request.
159+
*/
160+
public buildRequest(): EmailActionCodeRequest {
161+
const request: {[key: string]: any} = {
162+
continueUrl: this.continueUrl,
163+
canHandleCodeInApp: this.canHandleCodeInApp,
164+
dynamicLinkDomain: this.dynamicLinkDomain,
165+
androidPackageName: this.apn,
166+
androidMinimumVersion: this.amv,
167+
androidInstallApp: this.installApp,
168+
iOSBundleId: this.ibi,
169+
};
170+
// Remove all null and undefined fields from request.
171+
for (const key in request) {
172+
if (request.hasOwnProperty(key)) {
173+
if (typeof request[key] === 'undefined' || request[key] === null) {
174+
delete request[key];
175+
}
176+
}
177+
}
178+
return request as EmailActionCodeRequest;
179+
}
180+
}

0 commit comments

Comments
 (0)