1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /*! firebase-admin v4.2.1 https://firebase.google.com/terms/ */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var validator = require("../utils/validator"); var deep_copy_1 = require("../utils/deep-copy"); var error_1 = require("../utils/error"); var error_2 = require("../utils/error"); var api_request_1 = require("../utils/api-request"); /** Firebase Auth backend host. */ var FIREBASE_AUTH_HOST = 'www.googleapis.com'; /** Firebase Auth backend port number. */ var FIREBASE_AUTH_PORT = 443; /** Firebase Auth backend path. */ var FIREBASE_AUTH_PATH = '/identitytoolkit/v3/relyingparty/'; /** Firebase Auth request header. */ var FIREBASE_AUTH_HEADER = { 'Content-Type': 'application/json', 'X-Client-Version': 'Node/Admin/4.2.1', }; /** Firebase Auth request timeout duration in milliseconds. */ var FIREBASE_AUTH_TIMEOUT = 10000; /** * Validates a create/edit request object. All unsupported parameters * are removed from the original request. If an invalid field is passed * an error is thrown. * * @param {any} request The create/edit request object. */ function validateCreateEditRequest(request) { // Hash set of whitelisted parameters. var validKeys = { displayName: true, localId: true, email: true, password: true, rawPassword: true, emailVerified: true, photoUrl: true, disabled: true, disableUser: true, deleteAttribute: true, sanityCheck: true, }; // Remove invalid keys from original request. for (var key in request) { if (!(key in validKeys)) { delete request[key]; } } // For any invalid parameter, use the external key name in the error description. // displayName should be a string. if (typeof request.displayName !== 'undefined' && typeof request.displayName !== 'string') { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_DISPLAY_NAME); } if (typeof request.localId !== 'undefined' && !validator.isUid(request.localId)) { // This is called localId on the backend but the developer specifies this as // uid externally. So the error message should use the client facing name. throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_UID); } // email should be a string and a valid email. if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_EMAIL); } // password should be a string and a minimum of 6 chars. if (typeof request.password !== 'undefined' && !validator.isPassword(request.password)) { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_PASSWORD); } // rawPassword should be a string and a minimum of 6 chars. if (typeof request.rawPassword !== 'undefined' && !validator.isPassword(request.rawPassword)) { // This is called rawPassword on the backend but the developer specifies this as // password externally. So the error message should use the client facing name. throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_PASSWORD); } // emailVerified should be a boolean. if (typeof request.emailVerified !== 'undefined' && typeof request.emailVerified !== 'boolean') { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_EMAIL_VERIFIED); } // photoUrl should be a URL. if (typeof request.photoUrl !== 'undefined' && !validator.isURL(request.photoUrl)) { // This is called photoUrl on the backend but the developer specifies this as // photoURL externally. So the error message should use the client facing name. throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_PHOTO_URL); } // disabled should be a boolean. if (typeof request.disabled !== 'undefined' && typeof request.disabled !== 'boolean') { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_DISABLED_FIELD); } // disableUser should be a boolean. if (typeof request.disableUser !== 'undefined' && typeof request.disableUser !== 'boolean') { // This is called disableUser on the backend but the developer specifies this as // disabled externally. So the error message should use the client facing name. throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_DISABLED_FIELD); } } ; /** Instantiates the getAccountInfo endpoint settings. */ exports.FIREBASE_AUTH_GET_ACCOUNT_INFO = new api_request_1.ApiSettings('getAccountInfo', 'POST') .setRequestValidator(function (request) { if (!request.localId && !request.email) { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } }) .setResponseValidator(function (response) { if (!response.users) { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.USER_NOT_FOUND); } }); /** Instantiates the deleteAccount endpoint settings. */ exports.FIREBASE_AUTH_DELETE_ACCOUNT = new api_request_1.ApiSettings('deleteAccount', 'POST') .setRequestValidator(function (request) { if (!request.localId) { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } }); /** Instantiates the setAccountInfo endpoint settings for updating existing accounts. */ exports.FIREBASE_AUTH_SET_ACCOUNT_INFO = new api_request_1.ApiSettings('setAccountInfo', 'POST') .setRequestValidator(function (request) { // localId is a required parameter. if (typeof request.localId === 'undefined') { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } validateCreateEditRequest(request); }) .setResponseValidator(function (response) { // If the localId is not returned, then the request failed. if (!response.localId) { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.USER_NOT_FOUND); } }); /** * Instantiates the signupNewUser endpoint settings for creating a new user with or without * uid being specified. The backend will create a new one if not provided and return it. */ exports.FIREBASE_AUTH_SIGN_UP_NEW_USER = new api_request_1.ApiSettings('signupNewUser', 'POST') .setRequestValidator(function (request) { validateCreateEditRequest(request); }) .setResponseValidator(function (response) { // If the localId is not returned, then the request failed. if (!response.localId) { throw new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new user'); } }); /** * Class that provides mechanism to send requests to the Firebase Auth backend endpoints. */ var FirebaseAuthRequestHandler = (function () { /** * @param {FirebaseApp} app The app used to fetch access tokens to sign API requests. * @constructor */ function FirebaseAuthRequestHandler(app) { this.host = FIREBASE_AUTH_HOST; this.port = FIREBASE_AUTH_PORT; this.path = FIREBASE_AUTH_PATH; this.headers = FIREBASE_AUTH_HEADER; this.timeout = FIREBASE_AUTH_TIMEOUT; this.signedApiRequestHandler = new api_request_1.SignedApiRequestHandler(app); } /** * @param {Object} response The response to check for errors. * @return {string|null} The error code if present; null otherwise. */ FirebaseAuthRequestHandler.getErrorCode = function (response) { return (validator.isNonNullObject(response) && response.error && response.error.message) || null; }; /** * Looks a user by uid. * * @param {string} uid The uid of the user to lookup. * @return {Promise<Object>} A promise that resolves with the user information. */ FirebaseAuthRequestHandler.prototype.getAccountInfoByUid = function (uid) { if (!validator.isUid(uid)) { return Promise.reject(new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_UID)); } var request = { localId: [uid], }; return this.invokeRequestHandler(exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request); }; /** * Looks a user by email. * * @param {string} email The email of the user to lookup. * @return {Promise<Object>} A promise that resolves with the user information. */ FirebaseAuthRequestHandler.prototype.getAccountInfoByEmail = function (email) { if (!validator.isEmail(email)) { return Promise.reject(new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_EMAIL)); } var request = { email: [email], }; return this.invokeRequestHandler(exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request); }; /** * Deletes an account identified by a uid. * * @param {string} uid The uid of the user to delete. * @return {Promise<Object>} A promise that resolves when the user is deleted. */ FirebaseAuthRequestHandler.prototype.deleteAccount = function (uid) { if (!validator.isUid(uid)) { return Promise.reject(new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_UID)); } var request = { localId: uid, }; return this.invokeRequestHandler(exports.FIREBASE_AUTH_DELETE_ACCOUNT, request); }; /** * Edits an existing user. * * @param {string} uid The user to edit. * @param {Object} properties The properties to set on the user. * @return {Promise<string>} A promise that resolves when the operation completes * with the user id that was edited. */ FirebaseAuthRequestHandler.prototype.updateExistingAccount = function (uid, properties) { if (!validator.isUid(uid)) { return Promise.reject(new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_UID)); } else if (!validator.isNonNullObject(properties)) { return Promise.reject(new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.')); } // Build the setAccountInfo request. var request = deep_copy_1.deepCopy(properties); request.localId = uid; // For deleting displayName or photoURL, these values must be passed as null. // They will be removed from the backend request and an additional parameter // deleteAttribute: ['PHOTO_URL', 'DISPLAY_NAME'] // with an array of the parameter names to delete will be passed. // Parameters that are deletable and their deleteAttribute names. // Use client facing names, photoURL instead of photoUrl. var deletableParams = { displayName: 'DISPLAY_NAME', photoURL: 'PHOTO_URL', }; // Properties to delete if available. request.deleteAttribute = []; for (var key in deletableParams) { if (request[key] === null) { // Add property identifier to list of attributes to delete. request.deleteAttribute.push(deletableParams[key]); // Remove property from request. delete request[key]; } } if (request.deleteAttribute.length === 0) { delete request.deleteAttribute; } // Rewrite photoURL to photoUrl. if (typeof request.photoURL !== 'undefined') { request.photoUrl = request.photoURL; delete request.photoURL; } // Rewrite disabled to disableUser. if (typeof request.disabled !== 'undefined') { request.disableUser = request.disabled; delete request.disabled; } return this.invokeRequestHandler(exports.FIREBASE_AUTH_SET_ACCOUNT_INFO, request) .then(function (response) { return response.localId; }); }; /** * Create a new user with the properties supplied. * * @param {Object} properties The properties to set on the user. * @return {Promise<string>} A promise that resolves when the operation completes * with the user id that was created. */ FirebaseAuthRequestHandler.prototype.createNewAccount = function (properties) { if (!validator.isNonNullObject(properties)) { return Promise.reject(new error_2.FirebaseAuthError(error_2.AuthClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.')); } // Build the signupNewUser request. var request = deep_copy_1.deepCopy(properties); // Rewrite photoURL to photoUrl. if (typeof request.photoURL !== 'undefined') { request.photoUrl = request.photoURL; delete request.photoURL; } // Rewrite uid to localId if it exists. if (typeof request.uid !== 'undefined') { request.localId = request.uid; delete request.uid; } return this.invokeRequestHandler(exports.FIREBASE_AUTH_SIGN_UP_NEW_USER, request) .then(function (response) { // Return the user id. return response.localId; }); }; /** * Invokes the request handler based on the API settings object passed. * * @param {ApiSettings} apiSettings The API endpoint settings to apply to request and response. * @param {Object} requestData The request data. * @return {Promise<Object>} A promise that resolves with the response. */ FirebaseAuthRequestHandler.prototype.invokeRequestHandler = function (apiSettings, requestData) { var _this = this; var path = this.path + apiSettings.getEndpoint(); var httpMethod = apiSettings.getHttpMethod(); return Promise.resolve() .then(function () { // Validate request. var requestValidator = apiSettings.getRequestValidator(); requestValidator(requestData); // Process request. return _this.signedApiRequestHandler.sendRequest(_this.host, _this.port, path, httpMethod, requestData, _this.headers, _this.timeout); }) .then(function (response) { // Check for backend errors in the response. var errorCode = FirebaseAuthRequestHandler.getErrorCode(response); if (errorCode) { throw error_2.FirebaseAuthError.fromServerError(errorCode, /* message */ undefined, response); } // Validate response. var responseValidator = apiSettings.getResponseValidator(); responseValidator(response); // Return entire response. return response; }) .catch(function (response) { var error; if (typeof response === 'object' && 'statusCode' in response) { // response came directly from a non-200 response. error = response.error; } else { // response came from a thrown error on a 200 response. error = response; } if (error instanceof error_1.FirebaseError) { throw error; } var errorCode = FirebaseAuthRequestHandler.getErrorCode(error); throw error_2.FirebaseAuthError.fromServerError(errorCode, /* message */ undefined, error); }); }; return FirebaseAuthRequestHandler; }()); exports.FirebaseAuthRequestHandler = FirebaseAuthRequestHandler; |