Skip to content

Commit 351c0ed

Browse files
committed
Add API call to signUp
1 parent 622b191 commit 351c0ed

File tree

7 files changed

+480
-0
lines changed

7 files changed

+480
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Auth } from '../../model/auth';
2+
import { IdTokenResponse } from '../../model/id_token';
3+
import { performSignInRequest, HttpMethod, Endpoint } from '..';
4+
5+
export interface SignUpRequest {
6+
returnSecureToken?: boolean;
7+
email?: string;
8+
password?: string;
9+
}
10+
11+
export interface SignUpResponse extends IdTokenResponse {
12+
displayName?: string;
13+
email?: string;
14+
}
15+
16+
export async function signUp(
17+
auth: Auth,
18+
request: SignUpRequest
19+
): Promise<SignUpResponse> {
20+
return performSignInRequest<SignUpRequest, SignUpResponse>(
21+
auth,
22+
HttpMethod.POST,
23+
Endpoint.SIGN_UP,
24+
request
25+
);
26+
}
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/**
2+
* @license
3+
* Copyright 2020 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 { AuthErrorCode } from '../core/errors';
19+
20+
/**
21+
* Errors that can be returned by the backend
22+
*/
23+
export enum ServerError {
24+
ADMIN_ONLY_OPERATION = 'ADMIN_ONLY_OPERATION',
25+
CAPTCHA_CHECK_FAILED = 'CAPTCHA_CHECK_FAILED',
26+
CORS_UNSUPPORTED = 'CORS_UNSUPPORTED',
27+
CREDENTIAL_MISMATCH = 'CREDENTIAL_MISMATCH',
28+
CREDENTIAL_TOO_OLD_LOGIN_AGAIN = 'CREDENTIAL_TOO_OLD_LOGIN_AGAIN',
29+
DYNAMIC_LINK_NOT_ACTIVATED = 'DYNAMIC_LINK_NOT_ACTIVATED',
30+
EMAIL_EXISTS = 'EMAIL_EXISTS',
31+
EMAIL_NOT_FOUND = 'EMAIL_NOT_FOUND',
32+
EXPIRED_OOB_CODE = 'EXPIRED_OOB_CODE',
33+
FEDERATED_USER_ID_ALREADY_LINKED = 'FEDERATED_USER_ID_ALREADY_LINKED',
34+
INVALID_APP_CREDENTIAL = 'INVALID_APP_CREDENTIAL',
35+
INVALID_APP_ID = 'INVALID_APP_ID',
36+
INVALID_CERT_HASH = 'INVALID_CERT_HASH',
37+
INVALID_CODE = 'INVALID_CODE',
38+
INVALID_CONTINUE_URI = 'INVALID_CONTINUE_URI',
39+
INVALID_CUSTOM_TOKEN = 'INVALID_CUSTOM_TOKEN',
40+
INVALID_DYNAMIC_LINK_DOMAIN = 'INVALID_DYNAMIC_LINK_DOMAIN',
41+
INVALID_EMAIL = 'INVALID_EMAIL',
42+
INVALID_ID_TOKEN = 'INVALID_ID_TOKEN',
43+
INVALID_IDP_RESPONSE = 'INVALID_IDP_RESPONSE',
44+
INVALID_IDENTIFIER = 'INVALID_IDENTIFIER',
45+
INVALID_MESSAGE_PAYLOAD = 'INVALID_MESSAGE_PAYLOAD',
46+
INVALID_OAUTH_CLIENT_ID = 'INVALID_OAUTH_CLIENT_ID',
47+
INVALID_OOB_CODE = 'INVALID_OOB_CODE',
48+
INVALID_PASSWORD = 'INVALID_PASSWORD',
49+
INVALID_PENDING_TOKEN = 'INVALID_PENDING_TOKEN',
50+
INVALID_PHONE_NUMBER = 'INVALID_PHONE_NUMBER',
51+
INVALID_PROVIDER_ID = 'INVALID_PROVIDER_ID',
52+
INVALID_RECIPIENT_EMAIL = 'INVALID_RECIPIENT_EMAIL',
53+
INVALID_SENDER = 'INVALID_SENDER',
54+
INVALID_SESSION_INFO = 'INVALID_SESSION_INFO',
55+
INVALID_TEMPORARY_PROOF = 'INVALID_TEMPORARY_PROOF',
56+
INVALID_TENANT_ID = 'INVALID_TENANT_ID',
57+
MISSING_ANDROID_PACKAGE_NAME = 'MISSING_ANDROID_PACKAGE_NAME',
58+
MISSING_APP_CREDENTIAL = 'MISSING_APP_CREDENTIAL',
59+
MISSING_CODE = 'MISSING_CODE',
60+
MISSING_CONTINUE_URI = 'MISSING_CONTINUE_URI',
61+
MISSING_CUSTOM_TOKEN = 'MISSING_CUSTOM_TOKEN',
62+
MISSING_IOS_BUNDLE_ID = 'MISSING_IOS_BUNDLE_ID',
63+
MISSING_OOB_CODE = 'MISSING_OOB_CODE',
64+
MISSING_OR_INVALID_NONCE = 'MISSING_OR_INVALID_NONCE',
65+
MISSING_PASSWORD = 'MISSING_PASSWORD',
66+
MISSING_REQ_TYPE = 'MISSING_REQ_TYPE',
67+
MISSING_PHONE_NUMBER = 'MISSING_PHONE_NUMBER',
68+
MISSING_SESSION_INFO = 'MISSING_SESSION_INFO',
69+
OPERATION_NOT_ALLOWED = 'OPERATION_NOT_ALLOWED',
70+
PASSWORD_LOGIN_DISABLED = 'PASSWORD_LOGIN_DISABLED',
71+
QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',
72+
RESET_PASSWORD_EXCEED_LIMIT = 'RESET_PASSWORD_EXCEED_LIMIT',
73+
REJECTED_CREDENTIAL = 'REJECTED_CREDENTIAL',
74+
SESSION_EXPIRED = 'SESSION_EXPIRED',
75+
TENANT_ID_MISMATCH = 'TENANT_ID_MISMATCH',
76+
TOKEN_EXPIRED = 'TOKEN_EXPIRED',
77+
TOO_MANY_ATTEMPTS_TRY_LATER = 'TOO_MANY_ATTEMPTS_TRY_LATER',
78+
UNSUPPORTED_TENANT_OPERATION = 'UNSUPPORTED_TENANT_OPERATION',
79+
UNAUTHORIZED_DOMAIN = 'UNAUTHORIZED_DOMAIN',
80+
USER_CANCELLED = 'USER_CANCELLED',
81+
USER_DISABLED = 'USER_DISABLED',
82+
USER_NOT_FOUND = 'USER_NOT_FOUND',
83+
WEAK_PASSWORD = 'WEAK_PASSWORD'
84+
}
85+
86+
/**
87+
* API Response in the event of an error
88+
*/
89+
export interface JsonError {
90+
error: {
91+
code: number;
92+
message: ServerError;
93+
errors: [
94+
{
95+
message: ServerError;
96+
domain: string;
97+
reason: string;
98+
}
99+
];
100+
};
101+
}
102+
103+
/**
104+
* Type definition for a map from server errors to developer visible errors
105+
*/
106+
export declare type ServerErrorMap<ApiError extends string> = {
107+
readonly [K in ApiError]: AuthErrorCode;
108+
};
109+
110+
/**
111+
* Map from errors returned by the server to errors to developer visible errors
112+
*/
113+
export const SERVER_ERROR_MAP: ServerErrorMap<ServerError> = {
114+
// Custom token errors.
115+
[ServerError.INVALID_CUSTOM_TOKEN]: AuthErrorCode.INVALID_CUSTOM_TOKEN,
116+
[ServerError.CREDENTIAL_MISMATCH]: AuthErrorCode.CREDENTIAL_MISMATCH,
117+
// This can only happen if the SDK sends a bad request.
118+
[ServerError.MISSING_CUSTOM_TOKEN]: AuthErrorCode.INTERNAL_ERROR,
119+
120+
// Create Auth URI errors.
121+
[ServerError.INVALID_IDENTIFIER]: AuthErrorCode.INVALID_EMAIL,
122+
// This can only happen if the SDK sends a bad request.
123+
[ServerError.MISSING_CONTINUE_URI]: AuthErrorCode.INTERNAL_ERROR,
124+
125+
// Sign in with email and password errors (some apply to sign up too).
126+
[ServerError.INVALID_EMAIL]: AuthErrorCode.INVALID_EMAIL,
127+
[ServerError.INVALID_PASSWORD]: AuthErrorCode.INVALID_PASSWORD,
128+
[ServerError.USER_DISABLED]: AuthErrorCode.USER_DISABLED,
129+
// This can only happen if the SDK sends a bad request.
130+
[ServerError.MISSING_PASSWORD]: AuthErrorCode.INTERNAL_ERROR,
131+
132+
// Sign up with email and password errors.
133+
[ServerError.EMAIL_EXISTS]: AuthErrorCode.EMAIL_EXISTS,
134+
[ServerError.PASSWORD_LOGIN_DISABLED]: AuthErrorCode.OPERATION_NOT_ALLOWED,
135+
136+
// Verify assertion for sign in with credential errors:
137+
[ServerError.INVALID_IDP_RESPONSE]: AuthErrorCode.INVALID_IDP_RESPONSE,
138+
[ServerError.INVALID_PENDING_TOKEN]: AuthErrorCode.INVALID_IDP_RESPONSE,
139+
[ServerError.FEDERATED_USER_ID_ALREADY_LINKED]:
140+
AuthErrorCode.CREDENTIAL_ALREADY_IN_USE,
141+
[ServerError.MISSING_OR_INVALID_NONCE]:
142+
AuthErrorCode.MISSING_OR_INVALID_NONCE,
143+
144+
// Email template errors while sending emails:
145+
[ServerError.INVALID_MESSAGE_PAYLOAD]: AuthErrorCode.INVALID_MESSAGE_PAYLOAD,
146+
[ServerError.INVALID_RECIPIENT_EMAIL]: AuthErrorCode.INVALID_RECIPIENT_EMAIL,
147+
[ServerError.INVALID_SENDER]: AuthErrorCode.INVALID_SENDER,
148+
// This can only happen if the SDK sends a bad request.
149+
[ServerError.MISSING_REQ_TYPE]: AuthErrorCode.INTERNAL_ERROR,
150+
151+
// Send Password reset email errors:
152+
[ServerError.EMAIL_NOT_FOUND]: AuthErrorCode.USER_DELETED,
153+
[ServerError.RESET_PASSWORD_EXCEED_LIMIT]:
154+
AuthErrorCode.TOO_MANY_ATTEMPTS_TRY_LATER,
155+
156+
// Reset password errors:
157+
[ServerError.EXPIRED_OOB_CODE]: AuthErrorCode.EXPIRED_OOB_CODE,
158+
[ServerError.INVALID_OOB_CODE]: AuthErrorCode.INVALID_OOB_CODE,
159+
// This can only happen if the SDK sends a bad request.
160+
[ServerError.MISSING_OOB_CODE]: AuthErrorCode.INTERNAL_ERROR,
161+
162+
// Get Auth URI errors:
163+
[ServerError.INVALID_PROVIDER_ID]: AuthErrorCode.INVALID_PROVIDER_ID,
164+
165+
// Operations that require ID token in request:
166+
[ServerError.CREDENTIAL_TOO_OLD_LOGIN_AGAIN]:
167+
AuthErrorCode.CREDENTIAL_TOO_OLD_LOGIN_AGAIN,
168+
[ServerError.INVALID_ID_TOKEN]: AuthErrorCode.INVALID_AUTH,
169+
[ServerError.TOKEN_EXPIRED]: AuthErrorCode.TOKEN_EXPIRED,
170+
[ServerError.USER_NOT_FOUND]: AuthErrorCode.TOKEN_EXPIRED,
171+
172+
// CORS issues.
173+
[ServerError.CORS_UNSUPPORTED]: AuthErrorCode.CORS_UNSUPPORTED,
174+
175+
// Dynamic link not activated.
176+
[ServerError.DYNAMIC_LINK_NOT_ACTIVATED]:
177+
AuthErrorCode.DYNAMIC_LINK_NOT_ACTIVATED,
178+
179+
// iosBundleId or androidPackageName not valid error.
180+
[ServerError.INVALID_APP_ID]: AuthErrorCode.INVALID_APP_ID,
181+
182+
// Other errors.
183+
[ServerError.TOO_MANY_ATTEMPTS_TRY_LATER]:
184+
AuthErrorCode.TOO_MANY_ATTEMPTS_TRY_LATER,
185+
[ServerError.WEAK_PASSWORD]: AuthErrorCode.WEAK_PASSWORD,
186+
[ServerError.OPERATION_NOT_ALLOWED]: AuthErrorCode.OPERATION_NOT_ALLOWED,
187+
[ServerError.USER_CANCELLED]: AuthErrorCode.USER_CANCELLED,
188+
189+
// Phone Auth related errors.
190+
[ServerError.CAPTCHA_CHECK_FAILED]: AuthErrorCode.CAPTCHA_CHECK_FAILED,
191+
[ServerError.INVALID_APP_CREDENTIAL]: AuthErrorCode.INVALID_APP_CREDENTIAL,
192+
[ServerError.INVALID_CODE]: AuthErrorCode.INVALID_CODE,
193+
[ServerError.INVALID_PHONE_NUMBER]: AuthErrorCode.INVALID_PHONE_NUMBER,
194+
[ServerError.INVALID_SESSION_INFO]: AuthErrorCode.INVALID_SESSION_INFO,
195+
[ServerError.INVALID_TEMPORARY_PROOF]: AuthErrorCode.INVALID_IDP_RESPONSE,
196+
[ServerError.MISSING_APP_CREDENTIAL]: AuthErrorCode.MISSING_APP_CREDENTIAL,
197+
[ServerError.MISSING_CODE]: AuthErrorCode.MISSING_CODE,
198+
[ServerError.MISSING_PHONE_NUMBER]: AuthErrorCode.MISSING_PHONE_NUMBER,
199+
[ServerError.MISSING_SESSION_INFO]: AuthErrorCode.MISSING_SESSION_INFO,
200+
[ServerError.QUOTA_EXCEEDED]: AuthErrorCode.QUOTA_EXCEEDED,
201+
[ServerError.SESSION_EXPIRED]: AuthErrorCode.CODE_EXPIRED,
202+
[ServerError.REJECTED_CREDENTIAL]: AuthErrorCode.REJECTED_CREDENTIAL,
203+
204+
// Other action code errors when additional settings passed.
205+
[ServerError.INVALID_CONTINUE_URI]: AuthErrorCode.INVALID_CONTINUE_URI,
206+
// MISSING_CONTINUE_URI is getting mapped to INTERNAL_ERROR above.
207+
// This is OK as this error will be caught by client side validation.
208+
[ServerError.MISSING_ANDROID_PACKAGE_NAME]:
209+
AuthErrorCode.MISSING_ANDROID_PACKAGE_NAME,
210+
[ServerError.MISSING_IOS_BUNDLE_ID]: AuthErrorCode.MISSING_IOS_BUNDLE_ID,
211+
[ServerError.UNAUTHORIZED_DOMAIN]: AuthErrorCode.UNAUTHORIZED_DOMAIN,
212+
[ServerError.INVALID_DYNAMIC_LINK_DOMAIN]:
213+
AuthErrorCode.INVALID_DYNAMIC_LINK_DOMAIN,
214+
215+
// getProjectConfig errors when clientId is passed.
216+
[ServerError.INVALID_OAUTH_CLIENT_ID]: AuthErrorCode.INVALID_OAUTH_CLIENT_ID,
217+
// getProjectConfig errors when sha1Cert is passed.
218+
[ServerError.INVALID_CERT_HASH]: AuthErrorCode.INVALID_CERT_HASH,
219+
220+
// Multi-tenant related errors.
221+
[ServerError.UNSUPPORTED_TENANT_OPERATION]:
222+
AuthErrorCode.UNSUPPORTED_TENANT_OPERATION,
223+
[ServerError.INVALID_TENANT_ID]: AuthErrorCode.INVALID_TENANT_ID,
224+
[ServerError.TENANT_ID_MISMATCH]: AuthErrorCode.TENANT_ID_MISMATCH,
225+
226+
// User actions (sign-up or deletion) disabled errors.
227+
[ServerError.ADMIN_ONLY_OPERATION]: AuthErrorCode.ADMIN_ONLY_OPERATION
228+
};
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* @license
3+
* Copyright 2020 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 { Auth } from '../model/auth';
19+
import {
20+
JsonError,
21+
SERVER_ERROR_MAP,
22+
ServerErrorMap,
23+
ServerError
24+
} from './errors';
25+
import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../core/errors';
26+
import { FirebaseError } from '@firebase/util';
27+
import { IdTokenResponse } from '../model/id_token';
28+
29+
export enum HttpMethod {
30+
POST = 'POST',
31+
GET = 'GET'
32+
}
33+
34+
export enum Endpoint {
35+
CREATE_AUTH_URI = '/v1/accounts:createAuthUri',
36+
DELETE_ACCOUNT = '/v1/accounts:delete',
37+
RESET_PASSWORD = '/v1/accounts:resetPassword',
38+
SIGN_UP = '/v1/accounts:signUp',
39+
SIGN_IN_WITH_CUSTOM_TOKEN = '/v1/accounts:signInWithCustomToken',
40+
SIGN_IN_WITH_EMAIL_LINK = '/v1/accounts:signInWithEmailLink',
41+
SIGN_IN_WITH_IDP = '/v1/accounts:signInWithIdp',
42+
SIGN_IN_WITH_PASSWORD = '/v1/accounts:signInWithPassword',
43+
SIGN_IN_WITH_PHONE_NUMBER = '/v1/accounts:signInWithPhoneNumber',
44+
SEND_VERIFICATION_CODE = '/v1/accounts:sendVerificationCode',
45+
SEND_OOB_CODE = '/v1/accounts:sendOobCode',
46+
SET_ACCOUNT_INFO = '/v1/accounts:update',
47+
GET_ACCOUNT_INFO = '/v1/accounts:lookup',
48+
GET_RECAPTCHA_PARAM = '/v1/recaptchaParams',
49+
START_PHONE_MFA_ENROLLMENT = '/v2/accounts/mfaEnrollment:start',
50+
FINALIZE_PHONE_MFA_ENROLLMENT = '/v2/accounts/mfaEnrollment:finalize',
51+
START_PHONE_MFA_SIGN_IN = '/v2/accounts/mfaSignIn:start',
52+
FINALIZE_PHONE_MFA_SIGN_IN = '/v2/accounts/mfaSignIn:finalize',
53+
WITHDRAW_MFA = '/v2/accounts/mfaEnrollment:withdraw'
54+
}
55+
56+
export async function performApiRequest<T, V>(
57+
auth: Auth,
58+
method: HttpMethod,
59+
path: Endpoint,
60+
request?: T,
61+
customErrorMap: Partial<ServerErrorMap<ServerError>> = {}
62+
): Promise<V> {
63+
const errorMap = { ...SERVER_ERROR_MAP, ...customErrorMap };
64+
try {
65+
const body = request
66+
? {
67+
body: JSON.stringify(request)
68+
}
69+
: {};
70+
const response = await fetch(
71+
`${auth.config.apiScheme}://${auth.config.apiHost}${path}?key=${auth.config.apiKey}`,
72+
{
73+
method,
74+
headers: {
75+
'Content-Type': 'application/json'
76+
},
77+
referrerPolicy: 'no-referrer',
78+
...body
79+
}
80+
);
81+
if (response.ok) {
82+
return response.json();
83+
} else {
84+
const json: JsonError = await response.json();
85+
const authError = errorMap[json.error.message];
86+
if (authError) {
87+
throw AUTH_ERROR_FACTORY.create(authError, { appName: auth.name });
88+
} else {
89+
// TODO probably should handle improperly formatted errors as well
90+
// If you see this, add an entry to SERVER_ERROR_MAP for the corresponding error
91+
throw new Error(`Unexpected API error: ${json.error.message}`);
92+
}
93+
}
94+
} catch (e) {
95+
if (e instanceof FirebaseError) {
96+
throw e;
97+
}
98+
throw AUTH_ERROR_FACTORY.create(AuthErrorCode.NETWORK_REQUEST_FAILED, {
99+
appName: auth.name
100+
});
101+
}
102+
}
103+
104+
export async function performSignInRequest<T, V extends IdTokenResponse>(
105+
auth: Auth,
106+
method: HttpMethod,
107+
path: Endpoint,
108+
request?: T,
109+
customErrorMap: Partial<ServerErrorMap<ServerError>> = {}
110+
): Promise<V> {
111+
const serverResponse = await performApiRequest<T, V>(
112+
auth,
113+
method,
114+
path,
115+
request,
116+
customErrorMap
117+
);
118+
if (serverResponse.mfaPendingCredential) {
119+
throw AUTH_ERROR_FACTORY.create(AuthErrorCode.MFA_REQUIRED, {
120+
appName: auth.name,
121+
serverResponse
122+
});
123+
}
124+
125+
return serverResponse;
126+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export enum Operation {
2+
PASSWORD_RESET = 'PASSWORD_RESET',
3+
RECOVER_EMAIL = 'RECOVER_EMAIL',
4+
EMAIL_SIGNIN = 'EMAIL_SIGNIN',
5+
VERIFY_EMAIL = 'VERIFY_EMAIL'
6+
}

0 commit comments

Comments
 (0)