diff --git a/CHANGELOG.md b/CHANGELOG.md index 7becaac65f..9a320e4628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,23 @@ X.Y.Z Release notes * Realm Object Server: 3.0.0 or later ### Breaking changes -* None. +* The authentication API has been completely revamped. + * The following methods have been deprecated and will be removed at a next major version: + * `Realm.Sync.User.login` + * `Realm.Sync.User.register` + * `Realm.Sync.User.authenticate` + * `Realm.Sync.User.registerWithProvider` + * `Realm.Sync.User.adminUser` + * A new `Realm.Sync.User.login` method has been added that accepts the server url and a credentials object. + * A new class - `Realm.Sync.Credentials` has been added that contains factory methods to create credentials + with all supported providers. + * Here are some examples on how to transform your old code to use the new API: + + | Old | New | + | - | - | + | `const user = await Realm.Sync.User.login(serverUrl, 'username', 'password');` | `const credentials = Realm.Sync.Credentials.usernamePassword('username', 'password');`
`const user = await Realm.Sync.User.login(serverUrl, credentials);` | + | `const jwtToken = 'acc3ssT0ken...';`
`const user = await Realm.Sync.User.registerWithProvider(serverUrl, 'jwt', jwtToken);` | `const jwtToken = 'acc3ssT0ken...';`
`const credentials = Realm.Sync.Credentials.jwt(jwtToken);`
`const user = await Realm.Sync.User.login(serverUrl, credentials);` | + | `const customToken = 'acc3ssT0ken...';`
`const userInfo = { someValue: true };`
`const user = await Realm.Sync.User.registerWithProvider(serverUrl, 'custom/fooauth', customToken, userInfo);` | `const customToken = 'acc3ssT0ken...';`
`const userInfo = { someValue: true };`
`const credentials = Realm.Sync.Credentials.custom('custom/fooauth', customToken, userInfo);`
`const user = await Realm.Sync.User.login(serverUrl, credentials);` | ### Enhancements * Exposed `User.serialize` to create a persistable representation of a user instance, as well as diff --git a/docs/sync.js b/docs/sync.js index 1123f1518d..1e4fb69a8c 100644 --- a/docs/sync.js +++ b/docs/sync.js @@ -99,7 +99,7 @@ class Sync { * Add a sync listener to listen to changes across multiple Realms. * * @param {string} serverUrl - The sync server to listen to. - * @param {SyncUser} adminUser - An admin user obtained by calling `new Realm.Sync.User.adminUser`. + * @param {SyncUser} adminUser - an admin user obtained by calling {@linkcode Realm.Sync.User.login|User.login} with admin credentials. * @param {string} filterRegex - A regular expression used to determine which changed Realms should trigger events. Use `.*` to match all Realms. * @param {string} name - The name of the event. * @param {function(changeEvent)} changeCallback - The callback to invoke with the events. @@ -129,7 +129,7 @@ class Sync { * Add a sync listener to listen to changes across multiple Realms. * * @param {string} serverUrl - The sync server to listen to. - * @param {SyncUser} adminUser - An admin user obtained by calling `new Realm.Sync.User.adminUser`. + * @param {SyncUser} adminUser - an admin user obtained by calling {@linkcode Realm.Sync.User.login|User.login} with admin credentials. * @param {string} filterRegex - A regular expression used to determine which changed Realms should trigger events. Use `.*` to match all Realms. * @param {Realm.Worker} worker - Worker to deliver events to. * @@ -288,58 +288,120 @@ class IncompatibleSyncedRealmError { } /** - * Class for logging in and managing Sync users. + * Class for creating user credentials * @memberof Realm.Sync */ -class User { +class Credentials { /** - * Login a sync user with username and password. - * @param {string} server - authentication server - * @param {string} username - * @param {string} password - * @param {function(error, user)} [callback] - called with the following arguments: - * - `error` - an Error object is provided on failure - * - `user` - a valid User object on success - * @returns {void|Promise} Returns a promise with a user if the callback was not specified + * Creates credentials based on a login with a username and a password. + * @param {string} username The username of the user. + * @param {string} password The user's password. + * @param {boolean} [createUser] optional - `true` if the user should be created, `false` otherwise. If + * `true` is provided and the user exists, or `false` is provided and the user doesn't exist, + * an error will be thrown. If not specified, if the user doesn't exist, they will be created, + * otherwise, they'll be logged in if the password matches. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. */ - static login(server, username, password, callback) {} + static usernamePassword(username, password, createUser) {}; /** - * Authenticate a sync user with provider. - * @param {string} server - authentication server - * @param {string} provider - the provider (curently: 'password', and 'jwt') - * @param {object} options - options used by provider: - * - jwt - `token`; a JWT token - * - password - `username` and `password` - * @return {Promise} Returns a promise with a user + * Creates credentials based on a Facebook login. + * @param {string} token A Facebook authentication token, obtained by logging into Facebook.. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. */ - static authenticate(server, provider, options) {} + static facebook(token) {}; /** - * Register/login a sync user using an external login provider. - * @param {string} server - authentication server - * @param {object} options - options, containing the following: - * @param {string} options.provider - The provider type - * @param {string} options.providerToken - The access token for the given provider - * @param {object} [options.userInfo] - A map containing additional data required by the provider - * @param {function(error, User)} [callback] - an optional callback called with the following arguments: - * - `error` - an Error object is provided on failure - * - `user` - a valid User object on success - * @return {void|Promise} Returns a promise with a user if the callback was not specified + * Creates credentials based on a Google login. + * @param {string} token A Google authentication token, obtained by logging into Google.. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. */ - static registerWithProvider(server, options, callback) {} + static google(token) {}; /** - * Register a sync user with username and password. - * @param {string} server - authentication server - * @param {string} username - * @param {string} password - * @param {function(error, user)} [callback] - called with the following arguments: - * - `error` - an Error object is provided on failure - * - `user` - a valid User object on success - * @return {void|Promise} Returns a promise with a user if the callback was not specified + * Creates credentials for an anonymous user. These can only be used once - using them a second + * time will result in a different user being logged in. If you need to get a user that has already logged + * in with the Anonymous credentials, use {@linkcode Realm.Sync.User.current|User.current} or {@linkcode Realm.Sync.User.all|User.all} + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. + */ + static anonymous() {}; + + /** + * Creates credentials based on a login with a nickname. If multiple users try to login + * with the same nickname, they'll get the same underlying sync user. + * @param {string} value The nickname of the user. + * @param {boolean} [isAdmin] An optional parameter controlling whether the user is admin. Default is `false`. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. + */ + static nickname(value, isAdmin) {}; + + /** + * Creates credentials based on an Active Directory login. + * @param {string} token An access token, obtained by logging into Azure Active Directory. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. + */ + static azureAD(token) {}; + + /** + * Creates credentials based on a JWT login. + * @param {string} token A JSON Web Token, that will be validated against the server's configured rules. + * @param {string} [providerName] The name of the provider as configured in the Realm Object. If not specified, the default + * name - `jwt` - will be used. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. + */ + static jwt(token, providerName) {}; + + /** + * Creates credentials based on an admin token. Using this credential will not contact the Realm Object Server. + * @param {string} token The admin token. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. + */ + static adminToken(token) {}; + + /** + * Creates credentials with a custom provider and user identifier. + * @param {string} providerName Provider used to verify the credentials. + * @param {string} token A string identifying the user. Usually an identity token or a username. + * @param {userInfo} token Data describing the user further or null if the user does not have any extra data. + * The data will be serialized to JSON, so all values must be mappable to a valid JSON data type. + * @return {Credentials} An instance of `Credentials` that can be used in {@linkcode Realm.Sync.User.login|User.login}. + */ + static custom(providerName, token, userInfo) {}; + + + /** + * Gets the identity provider for the credentials. + * @returns {string} The identity provider, such as Google, Facebook, etc. + */ + get identityProvider() {}; + + /** + * Gets the access token. + * @returns {string} + */ + get token() {}; + + /** + * Gets additional user information associated with the credentials. + * @returns {object} A dictionary, containing the additional information. */ - static register(server, username, password, callback) {} + get userInfo() {}; +} + +/** + * Class for managing Sync users. + * @memberof Realm.Sync + */ +class User { + /** + * Logs the user in to the Realm Object Server. + * @param {string} server The url of the server that the user is authenticated against. + * @param {Credentials} credentials The credentials to use for authentication. Obtain them by calling one of + * the {@linkcode Realm.Sync.Credentials|Credentials} static methods. + * @return {Promise | User} A {@linkcode Realm.Sync.User|User} object if the credentials are + * {@linkcode Realm.Sync.Credentials.adminToken|adminToken}, {@link Realm.Sync.User|`Promise`} otherwise. + */ + static login(server, credentials) {} /** * Request a password reset email to be sent to a user's email. @@ -361,11 +423,11 @@ class User { * open the app, extract the token, and navigate to a view that allows to change the password within the app. * * @param {string} server - authentication server - * @param {string} reset_token - The token that was sent to the user's email address. - * @param {string} new_password - The user's new password. + * @param {string} resetToken - The token that was sent to the user's email address. + * @param {string} newPassword - The user's new password. * @return {Promise} A promise which is resolved when the request has been sent. */ - static completePasswordReset(server, reset_token, new_password) {} + static completePasswordReset(server, resetToken, newPassword) {} /** * Request an email confirmation email to be sent to a user's email. @@ -385,18 +447,10 @@ class User { * open the app, extract the token, and navigate to a view that allows to confirm the email within the app. * * @param {string} server - authentication server - * @param {string} confirmation_token - The token that was sent to the user's email address. + * @param {string} confirmationToken - The token that was sent to the user's email address. * @return {Promise} A promise which is resolved when the request has been sent. */ - static confirmEmail(server, confirmation_token) {} - - /** - * Create an admin user for the given authentication server with an existing token - * @param {string} adminToken - existing admin token - * @param {string} server - authentication server - * @return {User} - admin user populated with the given token and server - */ - static adminUser(adminToken, server) {} + static confirmEmail(server, confirmationToken) {} /** * Creates a new sync user instance from the serialized representation. @@ -483,7 +537,7 @@ class User { * Get account information for a user. (requires administrator privilidges) * @param {string} provider - the provider to query for user account information (ex. 'password') * @param {string} username - the target username which account information should be retrieved - * @returns {Promise} - a promise that will be resolved with the retrieved account information as json object + * @returns {Promise} - a promise that will be resolved with the retrieved account information as JSON object * @example * { * "provider_id": "user@email.co", @@ -549,6 +603,27 @@ class User { * {@link Realm#Sync#User#offerPermissions offerPermissions}. */ invalidatePermissionOffer(permissionOfferOrToken) { } + + // Deprecated + /** + * @deprecated to be removed in future versions. Use User.login(server, Credentials.usernamePassword) instead. + */ + static register(server, username, password) {} + + /** + * @deprecated to be removed in future versions. Use User.login(server, Credentials.adminToken) instead. + */ + static adminUser(adminToken, server) {} + + /** + * @deprecated to be removed in future versions. Use User.login(server, Credentials.SOME-PROVIDER) instead. + */ + static registerWithProvider(server, options) {} + + /** + * @deprecated to be removed in future versions. Use User.login(server, Credentials.SOME-PROVIDER) instead. + */ + static authenticate(server, provider, options) {} } /** @@ -806,7 +881,7 @@ class Adapter { * Create a new Adapter to monitor and process changes made across multiple Realms * @param {string} localPath - the local path where realm files are stored * @param {string} serverUrl - the sync server to listen to - * @param {SyncUser} adminUser - an admin user obtained by calling `new Realm.Sync.User.adminUser` + * @param {SyncUser} adminUser - an admin user obtained by calling {@linkcode Realm.Sync.User.login|User.login} with admin credentials. * @param {string} regex - a regular expression used to determine which changed Realms should be monitored - * use `.*` to match all all Realms * @param {function(realmPath)} changeCallback - called when a new transaction is available @@ -886,7 +961,7 @@ class Adapter { * Open the Realm used by the Adapter for the given path. This is useful for writing two way * adapters as transactions written to this realm will be ignored when calling `current` and `advance` * @param {string} path - the path for the Realm to open - * @param {Realm~ObjectSchema[]} [optional] schema - schema to apply when opening the Realm + * @param {Realm~ObjectSchema[]} [schema] - optional schema to apply when opening the Realm * @returns {Realm} */ realmAtPath(path, schema) {} diff --git a/lib/extensions.js b/lib/extensions.js index 42af07b009..2950b91525 100644 --- a/lib/extensions.js +++ b/lib/extensions.js @@ -178,6 +178,8 @@ module.exports = function(realmConstructor) { Object.defineProperties(realmConstructor.Sync.User, getOwnPropertyDescriptors(userMethods.static)); Object.defineProperties(realmConstructor.Sync.User.prototype, getOwnPropertyDescriptors(userMethods.instance)); Object.defineProperty(realmConstructor.Sync.User, '_realmConstructor', { value: realmConstructor }); + realmConstructor.Sync.Credentials = {}; + Object.defineProperties(realmConstructor.Sync.Credentials, getOwnPropertyDescriptors(userMethods.credentials)); realmConstructor.Sync.AuthError = require('./errors').AuthError; diff --git a/lib/index.d.ts b/lib/index.d.ts index 931eb234f9..034eb975ae 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -290,6 +290,22 @@ declare namespace Realm.Sync { isAdmin: boolean; } + class Credentials { + static usernamePassword(username: string, password: string, createUser?: boolean): Credentials; + static facebook(token: string): Credentials; + static google(token: string): Credentials; + static anonymous(): Credentials; + static nickname(value: string, isAdmin?: boolean): Credentials; + static azureAD(token: string): Credentials; + static jwt(token: string, providerName?: string): Credentials; + static adminToken(token: string): Credentials; + static custom(providerName: string, token: string, userInfo: {[key: string]: any}): Credentials; + + readonly identityProvider: string; + readonly token: string; + readonly userInfo: { [key: string]: any }; + } + /** * User * @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.User.html } @@ -302,34 +318,15 @@ declare namespace Realm.Sync { readonly isAdminToken: boolean; readonly server: string; readonly token: string; - static adminUser(adminToken: string, server?: string): User; - - /** - * @deprecated, to be removed in future versions - */ - static login(server: string, username: string, password: string, callback: (error: any, user: User) => void): void; - static login(server: string, username: string, password: string): Promise; - - /** - * @deprecated, to be removed in future versions - */ - static register(server: string, username: string, password: string, callback: (error: any, user: User) => void): void; - static register(server: string, username: string, password: string): Promise; - - /** - * @deprecated, to be removed in versions - */ - static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }, callback: (error: Error | null, user: User | null) => void): void; - static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }): Promise; - static authenticate(server: string, provider: string, options: any): Promise; + static login(server: string, credentials: Credentials): Promise | User; static requestPasswordReset(server: string, email: string): Promise; - static completePasswordReset(server:string, reset_token:string, new_password:string): Promise; + static completePasswordReset(server:string, resetToken:string, newPassword:string): Promise; static requestEmailConfirmation(server:string, email:string): Promise; - static confirmEmail(server:string, confirmation_token:string): Promise; + static confirmEmail(server:string, confirmationToken:string): Promise; static deserialize(serialized: SerializedUser): Realm.Sync.User; @@ -344,6 +341,19 @@ declare namespace Realm.Sync { offerPermissions(realmUrl: string, accessLevel: AccessLevel, expiresAt?: Date): Promise; acceptPermissionOffer(token: string): Promise invalidatePermissionOffer(permissionOfferOrToken: PermissionOffer | string): Promise; + + // Deprecated + + /** @deprecated, to be removed in future versions */ + static adminUser(adminToken: string, server?: string): User; + /** @deprecated, to be removed in future versions */ + static login(server: string, username: string, password: string): Promise; + /** @deprecated, to be removed in future versions */ + static register(server: string, username: string, password: string): Promise; + /** @deprecated, to be removed in future versions */ + static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }): Promise; + /** @deprecated, to be removed in future versions */ + static authenticate(server: string, provider: string, options: any): Promise; } interface _PermissionConditionUserId { diff --git a/lib/user-methods.js b/lib/user-methods.js index 7ede965b93..90f1f8f3f0 100644 --- a/lib/user-methods.js +++ b/lib/user-methods.js @@ -251,7 +251,7 @@ function refreshAccessToken(user, localRealmPath, realmUrl) { * @param {Function} callback an optional callback with an error and user parameter * @returns {Promise} only returns a promise if the callback parameter was omitted */ -function _authenticate(userConstructor, server, json, callback) { +function _authenticate(userConstructor, server, json) { json.app_id = ''; const url = append_url(server, 'auth'); const options = { @@ -261,7 +261,7 @@ function _authenticate(userConstructor, server, json, callback) { open_timeout: 5000 }; - const promise = performFetch(url, options) + return performFetch(url, options) .then((response) => { const contentType = response.headers.get('Content-Type'); if (contentType.indexOf('application/json') === -1) { @@ -283,17 +283,6 @@ function _authenticate(userConstructor, server, json, callback) { }); } }); - - if (callback) { - promise.then(user => { - callback(null, user); - }) - .catch(err => { - callback(err); - }); - } else { - return promise; - } } function _updateAccount(userConstructor, server, json) { @@ -322,170 +311,183 @@ function _updateAccount(userConstructor, server, json) { }); } +const credentialsMethods = { + usernamePassword(username, password, createUser) { + checkTypes(arguments, ['string', 'string', 'boolean']); + return new Credentials('password', username, { register: createUser, password }); + }, + + facebook(token) { + checkTypes(arguments, ['string']); + return new Credentials('facebook', token); + }, + + google(token) { + checkTypes(arguments, ['string']); + return new Credentials('google', token); + }, + + anonymous() { + return new Credentials('anonymous'); + }, + + nickname(value, isAdmin) { + checkTypes(arguments, ['string', 'boolean']); + return new Credentials('nickname', value, { is_admin: isAdmin || false }); + }, + + azureAD(token) { + checkTypes(arguments, ['string']); + return new Credentials('azuread', token) + }, + + jwt(token, providerName) { + checkTypes(arguments, ['string', 'string']); + return new Credentials(providerName || 'jwt', token); + }, + + adminToken(token) { + checkTypes(arguments, ['string']); + return new Credentials('adminToken', token); + }, + + custom(providerName, token, userInfo) { + checkTypes(arguments, ['string', 'string', 'object']); + return new Credentials(providerName, token, userInfo); + } +} + const staticMethods = { - get current() { - const allUsers = this.all; - const keys = Object.keys(allUsers); - if (keys.length === 0) { - return undefined; - } else if (keys.length > 1) { - throw new Error("Multiple users are logged in"); - } + get current() { + const allUsers = this.all; + const keys = Object.keys(allUsers); + if (keys.length === 0) { + return undefined; + } else if (keys.length > 1) { + throw new Error("Multiple users are logged in"); + } - return allUsers[keys[0]]; - }, - - adminUser(token, server) { - checkTypes(arguments, ['string', 'string']); - return this._adminUser(server, token); - }, - - register(server, username, password, callback) { - checkTypes(arguments, ['string', 'string', 'string', 'function']); - const json = { - provider: 'password', - user_info: { password: password, register: true }, - data: username - }; - - if (callback) { - const message = "register(..., callback) is now deprecated in favor of register(): Promise. This function argument will be removed in future versions."; - (console.warn || console.log).call(console, message); - } + return allUsers[keys[0]]; + }, - return _authenticate(this, server, json, callback); - }, + login(server, credentials) { + if (arguments.length === 3) { + // Deprecated legacy signature. + checkTypes(arguments, ['string', 'string', 'string']); + console.warn("User.login is deprecated. Please use User.login(server, Credentials.usernamePassword(...)) instead."); + const newCredentials = credentialsMethods.usernamePassword(arguments[1], arguments[2], /* createUser */ false); + return this.login(server, newCredentials); + } - login(server, username, password, callback) { - checkTypes(arguments, ['string', 'string', 'string', 'function']); - const json = { - provider: 'password', - user_info: { password: password, register: false }, - data: username - }; + checkTypes(arguments, ['string', 'object']); + if (credentials.identityProvider === 'adminToken') { + return this._adminUser(server, credenitals.token); + } - if (callback) { - const message = "login(..., callback) is now deprecated in favor of login(): Promise. This function argument will be removed in future versions."; - (console.warn || console.log).call(console, message); - } + return _authenticate(this, server, credentials); + }, - return _authenticate(this, server, json, callback); - }, + deserialize(serialized) { + checkObjectTypes(serialized, { + server: 'string', + identity: 'string', + refreshToken: 'string', + isAdmin: 'boolean', + }); - registerWithProvider(server, options, callback) { + return this.createUser(serialized.server, serialized.identity, serialized.refreshToken, false, serialized.isAdmin || false); + }, - // Compatibility with previous signature: - // registerWithProvider(server, provider, providerToken, callback) - if (arguments.length === 4) { - checkTypes(arguments, ['string', 'string', 'string', 'function']); - options = { - provider: arguments[1], - providerToken: arguments[2] - }; - callback = arguments[3]; - } else { - checkTypes(arguments, ['string', 'object', 'function']); - } + requestPasswordReset(server, email) { + checkTypes(arguments, ['string', 'string']); + const json = { + provider_id: email, + data: { action: 'reset_password' } + }; - let json = { - provider: options.provider, - data: options.providerToken, - }; + return _updateAccount(this, server, json); + }, - if (options.userInfo) { - json.user_info = options.userInfo; + completePasswordReset(server, resetToken, newPassword) { + checkTypes(arguments, ['string', 'string']); + const json = { + data: { + action: 'complete_reset', + token: resetToken, + new_password: newPassword } + }; + + return _updateAccount(this, server, json); + }, + + requestEmailConfirmation(server, email) { + checkTypes(arguments, ['string', 'string']); + const json = { + provider_id: email, + data: { action: 'request_email_confirmation' } + }; + + return _updateAccount(this, server, json); + }, - if (callback) { - const message = "registerWithProvider(..., callback) is now deprecated in favor of registerWithProvider(): Promise. This function argument will be removed in future versions."; - (console.warn || console.log).call(console, message); + confirmEmail(server, confirmationToken) { + checkTypes(arguments, ['string', 'string']); + const json = { + data: { + action: 'confirm_email', + token: confirmationToken } + }; + + return _updateAccount(this, server, json); + }, + + _refreshAccessToken: refreshAccessToken, - return _authenticate(this, server, json, callback); - }, + // Deprecated... + adminUser(token, server) { + checkTypes(arguments, ['string', 'string']); + console.warn("User.adminUser is deprecated. Please use User.login(server, Credentials.adminToken(token)) instead."); + const credentials = credentialsMethods.adminToken(token); + return this.login(server, credentials); + }, - authenticate(server, provider, options) { - checkTypes(arguments, ['string', 'string', 'object']) + register(server, username, password) { + checkTypes(arguments, ['string', 'string', 'string']); + console.warn("User.register is deprecated. Please use User.login(server, Credentials.usernamePassword(...)) instead."); + const credentials = credentialsMethods.usernamePassword(username, password, /* createUser */ true); + return this.login(server, credentials); + }, + + registerWithProvider(server, options) { + checkTypes(arguments, ['string', 'object']); + console.warn("User.registerWithProvider is deprecated. Please use User.login(server, Credentials.SOME-PROVIDER(...)) instead."); + const credentials = credentialsMethods.custom(options.provider, options.providerToken, options.userInfo); + return this.login(server, credentials); + }, - var json = {} - switch (provider.toLowerCase()) { + authenticate(server, provider, options) { + checkTypes(arguments, ['string', 'string', 'object']) + console.warn("User.authenticate is deprecated. Please use User.login(server, Credentials.SOME-PROVIDER(...)) instead."); + + let credentials; + switch (provider.toLowerCase()) { case 'jwt': - json.provider = 'jwt' - json.token = options.token; + credentials = credentialsMethods.jwt(options.token, 'jwt'); break case 'password': - json.provider = 'password' - json.user_info = { password: options.password } - json.data = options.username + credentials = credentialsMethods.usernamePassword(options.username, options.password); break default: - Object.assign(json, options) - json.provider = provider - } - - return _authenticate(this, server, json) - }, - - deserialize(serialized) { - checkObjectTypes(serialized, { - server: 'string', - identity: 'string', - refreshToken: 'string', - isAdmin: 'boolean', - }); - - return this.createUser(serialized.server, serialized.identity, serialized.refreshToken, false, serialized.isAdmin || false); - }, - - requestPasswordReset(server, email) { - checkTypes(arguments, ['string', 'string']); - const json = { - provider_id: email, - data: { action: 'reset_password' } - }; - - return _updateAccount(this, server, json); - }, - - completePasswordReset(server, reset_token, new_password) { - checkTypes(arguments, ['string', 'string']); - const json = { - data: { - action: 'complete_reset', - token: reset_token, - new_password: new_password - } - }; - - return _updateAccount(this, server, json); - }, - - requestEmailConfirmation(server, email) { - checkTypes(arguments, ['string', 'string']); - const json = { - provider_id: email, - data: { action: 'request_email_confirmation' } - }; - - return _updateAccount(this, server, json); - }, - - confirmEmail(server, confirmation_token) { - checkTypes(arguments, ['string', 'string']); - const json = { - data: { - action: 'confirm_email', - token: confirmation_token - } - }; - - return _updateAccount(this, server, json); - }, + credentials = credentialsMethods.custom(provider, options.data, options.user_info || options.userInfo); + break; + } - _refreshAccessToken: refreshAccessToken, + return this.login(server, credentials); + }, }; - const instanceMethods = { logout() { this._logout(); @@ -609,10 +611,27 @@ const instanceMethods = { }, }; +class Credentials { + constructor(identityProvider, token, userInfo) { + this.identityProvider = identityProvider; + this.token = token; + this.userInfo = userInfo; + } + + toJSON() { + return { + data: this.token, + provider: this.identityProvider, + user_info: this.userInfo || {}, + }; + } +} + // Append the permission apis Object.assign(instanceMethods, permissionApis); module.exports = { static: staticMethods, - instance: instanceMethods + instance: instanceMethods, + credentials: credentialsMethods, }; diff --git a/tests/js/admin-user-helper.js b/tests/js/admin-user-helper.js index aadce9445f..4c0e2edcbc 100644 --- a/tests/js/admin-user-helper.js +++ b/tests/js/admin-user-helper.js @@ -10,7 +10,8 @@ const adminName = "realm-admin" const password = ''; exports.createAdminUser = function () { - return Realm.Sync.User.login('http://localhost:9080', adminName, password).then((user) => { + const credentials = Realm.Sync.Credentials.usernamePassword(adminName, password); + return Realm.Sync.User.login('http://localhost:9080', credentials).then((user) => { if (!user.isAdmin) { throw new Error(`${adminName} user is not an admin user on this server`); } diff --git a/tests/js/download-api-helper.js b/tests/js/download-api-helper.js index 46076544a8..31e945f0fb 100644 --- a/tests/js/download-api-helper.js +++ b/tests/js/download-api-helper.js @@ -40,15 +40,11 @@ function createObjects(user) { }); } -let registrationError; -Realm.Sync.User.register('http://localhost:9080', username, 'password') - .catch((error) => { - registrationError = JSON.stringify(error); - return Realm.Sync.User.login('http://localhost:9080', username, 'password') - }) +const credentials = Realm.Sync.Credentials.nickname(username); +Realm.Sync.User.login('http://localhost:9080', credentials) .catch((error) => { const loginError = JSON.stringify(error); - console.error(`download-api-helper failed:\n User.register() error:\n${registrationError}\n User.login() error:\n${registrationError}`); + console.error(`download-api-helper failed:\n User login error:\n${loginError}`); process.exit(-2); }) .then((user) => createObjects(user)) diff --git a/tests/js/encryption-tests.js b/tests/js/encryption-tests.js index f8c9f68cba..b4195ab47c 100644 --- a/tests/js/encryption-tests.js +++ b/tests/js/encryption-tests.js @@ -75,7 +75,8 @@ module.exports = { return Promise.resolve(); } - return Realm.Sync.User.login('http://localhost:9080', "realm-admin", '').then(adminUser => { + const credentials = Realm.Sync.Credentials.usernamePassword('realm-admin', ''); + return Realm.Sync.User.login('http://localhost:9080', credentials).then(adminUser => { new Realm({ encryptionKey: new Int8Array(64), sync: { diff --git a/tests/js/nested-list-helper.js b/tests/js/nested-list-helper.js index 7de46e5ee2..b0d2fa6926 100644 --- a/tests/js/nested-list-helper.js +++ b/tests/js/nested-list-helper.js @@ -1,5 +1,5 @@ /* -This script creates new nested objects into a new Realm. +This script creates new nested objects into a new Realm. */ 'use strict'; @@ -54,15 +54,11 @@ function createObjects(user) { }); } -let registrationError; -Realm.Sync.User.register('http://localhost:9080', username, 'password') - .catch((error) => { - registrationError = JSON.stringify(error); - return Realm.Sync.User.login('http://localhost:9080', username, 'password') - }) +const credentials = Realm.Sync.Credentials.nickname(username); +Realm.Sync.User.login('http://localhost:9080', credentials) .catch((error) => { const loginError = JSON.stringify(error); - console.error(`nested-list-helper failed:\n User.register() error:\n${registrationError}\n User.login() error:\n${registrationError}`); + console.error(`nested-list-helper failed:\n User login error:\n${loginError}`); process.exit(-2); }) .then((user) => createObjects(user)) diff --git a/tests/js/object-id-tests.js b/tests/js/object-id-tests.js index faf778a941..8b96f9e6bc 100644 --- a/tests/js/object-id-tests.js +++ b/tests/js/object-id-tests.js @@ -47,8 +47,9 @@ module.exports = { if (!global.enableSyncTests) { return Promise.resolve(); } - - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => { + + const credentials = Realm.Sync.Credentials.anonymous(); + return Realm.Sync.User.login('http://localhost:9080', credentials).then(user => { const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/myrealm' }, schema: [{ name: 'IntegerPrimaryKey', properties: { int: 'int?' }, primaryKey: 'int' }, { name: 'StringPrimaryKey', properties: { string: 'string?' }, primaryKey: 'string' }, diff --git a/tests/js/partial-sync-api-helper.js b/tests/js/partial-sync-api-helper.js index e64a3bbb41..04039fd239 100644 --- a/tests/js/partial-sync-api-helper.js +++ b/tests/js/partial-sync-api-helper.js @@ -57,15 +57,11 @@ function createObjects(user) { }); } -let registrationError; -Realm.Sync.User.register('http://localhost:9080', username, 'password') - .catch((error) => { - registrationError = JSON.stringify(error); - return Realm.Sync.User.login('http://localhost:9080', username, 'password') - }) +const credentials = Realm.Sync.Credentials.nickname(username); +Realm.Sync.User.login('http://localhost:9080', credentials) .catch((error) => { const loginError = JSON.stringify(error); - console.error(`partial-sync-api-helper failed:\n User.register() error:\n${registrationError}\n User.login() error:\n${registrationError}`); + console.error(`partial-sync-api-helper failed:\n User login error:\n${loginError}`); process.exit(-2); }) .then((user) => createObjects(user)) diff --git a/tests/js/permission-tests.js b/tests/js/permission-tests.js index 93d945bd08..8e71c20eef 100644 --- a/tests/js/permission-tests.js +++ b/tests/js/permission-tests.js @@ -31,7 +31,7 @@ function uuid() { function createUsersWithTestRealms(count) { const createUserWithTestRealm = () => { return Realm.Sync.User - .register('http://localhost:9080', uuid(), 'password') + .login('http://localhost:9080', Realm.Sync.Credentials.anonymous()) .then(user => { new Realm({sync: {user, url: 'realm://localhost:9080/~/test', fullSynchronization: true }}).close(); return user; @@ -190,11 +190,11 @@ module.exports = { }; let owner, otherUser return Realm.Sync.User - .register('http://localhost:9080', uuid(), 'password') + .login('http://localhost:9080', Realm.Sync.Credentials.nickname(uuid())) .then(user => { owner = user; new Realm({sync: {user, url: 'realm://localhost:9080/default'}}).close(); - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password') + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.nickname(uuid())) }) .then((user) => { otherUser = user; diff --git a/tests/js/session-tests.js b/tests/js/session-tests.js index 98d874874b..c4bc5af0c6 100644 --- a/tests/js/session-tests.js +++ b/tests/js/session-tests.js @@ -105,14 +105,11 @@ module.exports = { return; } - const username = uuid(); - const realmName = uuid(); - - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then(user => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then(user => { let config = { - sync: { - user, - url: `realm://localhost:9080/~/${realmName}`, + sync: { + user, + url: `realm://localhost:9080/~/${uuid()}`, fullSynchronization: true, custom_http_headers: { 'X-Foo': 'Bar' @@ -131,7 +128,7 @@ module.exports = { }, testProperties() { - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then(user => { return new Promise((resolve, reject) => { const accessTokenRefreshed = this; let successCounter = 0; @@ -180,12 +177,12 @@ module.exports = { const expectedObjectsCount = 3; let user, config; + + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(u => { user = u; - const accessTokenRefreshed = this; - let successCounter = 0; config = { sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true }, @@ -216,13 +213,11 @@ module.exports = { const expectedObjectsCount = 3; let user, config; + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(u => { user = u; - const accessTokenRefreshed = this; - let successCounter = 0; - config = { sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true }, schema: [{ name: 'Dog', properties: { name: 'string' } }], @@ -259,12 +254,10 @@ module.exports = { const realmName = uuid(); const expectedObjectsCount = 3; + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(user => { - const accessTokenRefreshed = this; - let successCounter = 0; - let config = { sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true }, schema: [{ name: 'Dog', properties: { name: 'string' } }], @@ -310,12 +303,10 @@ module.exports = { const realmName = uuid(); const expectedObjectsCount = 3; - return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + const credentials = Realm.Sync.Credentials.nickname(username); + return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(user => { - const accessTokenRefreshed = this; - let successCounter = 0; - let config = { sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true } }; @@ -410,7 +401,7 @@ module.exports = { }, testErrorHandling() { - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then(user => { return new Promise((resolve, _reject) => { const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/myrealm' } }); config.sync.error = (sender, error) => { @@ -440,8 +431,9 @@ module.exports = { const username = uuid(); const realmName = uuid(); + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/nested-list-helper.js', __dirname + '/schemas.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(user => { let config = { schema: [schemas.ParentObject, schemas.NameObject], @@ -481,7 +473,7 @@ module.exports = { Realm.copyBundledRealmFiles(); } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password') + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()) .then(user => { const config = { path: realm, @@ -524,7 +516,7 @@ module.exports = { Realm.copyBundledRealmFiles(); } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then(user => { return new Promise((resolve, _reject) => { const config = { path: realm, @@ -565,7 +557,7 @@ module.exports = { Realm.copyBundledRealmFiles(); } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then(user => { return new Promise((resolve, _reject) => { const config = { path: realm, @@ -636,8 +628,9 @@ module.exports = { const username = uuid(); const realmName = uuid(); + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(user => { let config = { sync: { @@ -703,8 +696,9 @@ module.exports = { const realmName = uuid(); let progressCalled = false; + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(user => { let config = { sync: { @@ -729,8 +723,9 @@ module.exports = { const username = uuid(); const realmName = uuid(); + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH) - .then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password')) + .then(() => Realm.Sync.User.login('http://localhost:9080', credentials)) .then(user => { return new Promise((resolve, reject) => { let config = { @@ -836,9 +831,10 @@ module.exports = { TestCase.assertThrows(() => { let realm = new Realm(config); } ); } + const credentials = Realm.Sync.Credentials.nickname(username); return runOutOfProcess(__dirname + '/partial-sync-api-helper.js', username, REALM_MODULE_PATH) .then(() => { - return Realm.Sync.User.login('http://localhost:9080', username, 'password').then((u) => { + return Realm.Sync.User.login('http://localhost:9080', credentials).then((u) => { user = u; __partialIsAllowed(); @@ -921,7 +917,7 @@ module.exports = { return; } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then(user => { return new Promise((resolve, _reject) => { var realm; const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/myrealm' } }); @@ -954,7 +950,7 @@ module.exports = { return; } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then((u) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((u) => { return new Promise((resolve, reject) => { let config = { sync: { @@ -963,7 +959,7 @@ module.exports = { fullSynchronization: true, } }; - + Realm.open(config).then(realm => { realm.syncSession.addConnectionNotification((newState, oldState) => { if (oldState === Realm.Sync.ConnectionState.Connected && newState === Realm.Sync.ConnectionState.Disconnected) { @@ -981,7 +977,7 @@ module.exports = { return; } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then((u) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((u) => { return new Promise((resolve, reject) => { let config = { sync: { @@ -990,7 +986,7 @@ module.exports = { fullSynchronization: true, } }; - + Realm.open(config).then(realm => { let callback1 = () => { reject("Should not be called"); @@ -1015,7 +1011,7 @@ module.exports = { return; } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then((u) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((u) => { return new Promise((resolve, reject) => { let config = { sync: { @@ -1171,7 +1167,7 @@ module.exports = { return; } - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then((u) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((u) => { return new Promise((resolve, reject) => { let realm = new Realm(u.createConfiguration()); TestCase.assertEqual(5, realm.objects(Realm.Permissions.Class.schema.name).length); diff --git a/tests/js/user-tests.js b/tests/js/user-tests.js index df15f5641f..6e29122aa2 100644 --- a/tests/js/user-tests.js +++ b/tests/js/user-tests.js @@ -68,8 +68,7 @@ function assertIsAuthError(error, code, title) { module.exports = { testLogout() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { assertIsUser(user); assertIsSameUser(user, Realm.Sync.User.current); @@ -84,8 +83,7 @@ module.exports = { }, testRegisterUser() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { // Can we open a realm with the registered user? const realm = new Realm({sync: {user: user, url: 'realm://localhost:9080/~/test'}}); TestCase.assertInstanceOf(realm, Realm); @@ -94,9 +92,10 @@ module.exports = { testRegisterExistingUser() { const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + const credentials = Realm.Sync.Credentials.usernamePassword(username, 'password', true); + return Realm.Sync.User.login('http://localhost:9080', credentials).then((user) => { assertIsUser(user); - return Realm.Sync.User.register('http://localhost:9080', username, 'password') + return Realm.Sync.User.login('http://localhost:9080', credentials) .then((user) => { throw new Error(user); }) .catch((e) => { assertIsAuthError(e, 611, "The provided credentials are invalid or the user does not exist."); @@ -105,43 +104,43 @@ module.exports = { }, testRegisterMissingUsername() { - TestCase.assertThrows(() => Realm.Sync.User.register('http://localhost:9080', undefined, 'password')); + TestCase.assertThrows(() => Realm.Sync.Credentials.usernamePassword(undefined, 'password')); }, testRegisterMissingPassword() { - const username = uuid(); - TestCase.assertThrows(() => Realm.Sync.User.register('http://localhost:9080', username, undefined)); + TestCase.assertThrows(() => Realm.Sync.Credentials.usernamePassword(uuid(), undefined)); }, testRegisterServerOffline() { - const username = uuid(); // Because it waits for answer this takes some time.. - return Realm.Sync.User.register('http://fake_host.local', username, 'password') + return Realm.Sync.User.login('http://fake_host.local', Realm.Sync.Credentials.anonymous()) .catch((e) => {}) .then((user) => { if (user) { throw new Error('should not have been able to register'); }}) }, testLogin() { - const username = uuid(); - // Create user, logout the new user, then login - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { - user.logout(); - return Realm.Sync.User.login('http://localhost:9080', username, 'password'); - }).then((user => { - assertIsUser(user); - // Can we open a realm with the logged-in user? - const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/test' }}); - const realm = new Realm(config); - TestCase.assertInstanceOf(realm, Realm); - realm.close(); - })) + const username = uuid(); + const registerCredentials = Realm.Sync.Credentials.usernamePassword(username, 'password', true); + // Create user, logout the new user, then login + return Realm.Sync.User.login('http://localhost:9080', registerCredentials).then((user) => { + user.logout(); + const loginCredentials = Realm.Sync.Credentials.usernamePassword(username, 'password', false); + return Realm.Sync.User.login('http://localhost:9080', loginCredentials); + }).then((user => { + assertIsUser(user); + // Can we open a realm with the logged-in user? + const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/test' }}); + const realm = new Realm(config); + TestCase.assertInstanceOf(realm, Realm); + realm.close(); + })) }, testAuthenticateWithPassword() { const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.usernamePassword(username, 'password', true)).then((user) => { user.logout(); - return Realm.Sync.User.authenticate('http://localhost:9080', 'password', { username: username, password: 'password' }); + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.usernamePassword(username, 'password')); }).then((user => { assertIsUser(user); const realm = new Realm(user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/test' } })); @@ -160,25 +159,14 @@ module.exports = { }, testLoginNonExistingUser() { - return Realm.Sync.User.login('http://localhost:9080', 'does_not', 'exist') + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.usernamePassword('foo', 'pass', false)) .then((user) => { throw new Error(user); }) .catch((e) => assertIsAuthError(e, 611, "The provided credentials are invalid or the user does not exist.")) }, - testLoginServerOffline() { - const username = uuid(); - - // Because it waits for answer this takes some time.. - return Realm.Sync.User.register('http://fake_host.local', username, 'password') - .then((user) => { throw new Error(user); }) - .catch((e) => assertIsError(e)); - }, - testLoginTowardsMisbehavingServer() { - const username = uuid(); - // Try authenticating towards a server thats clearly not ROS - return Realm.Sync.User.register('https://github.com/realm/realm-js', username, 'user') + return Realm.Sync.User.login('https://github.com/realm/realm-js', Realm.Sync.Credentials.anonymous()) .catch((e) => { assertIsError(e); TestCase.assertEqual( @@ -188,15 +176,9 @@ module.exports = { }); }, - testAuthenticateInvalidProvider() { - return Realm.Sync.User.authenticate('http://localhost:9080', 'FooBar', {}) - .then((user) => { Promise.reject() } ) - .catch((e) => { Promise.resolve() } ) - }, - testAuthenticateJWT() { let token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJhdXN0aW5femhlbmciLCJpc0FkbWluIjp0cnVlLCJpYXQiOjE1MTI2OTI3NDl9.klca-3wYLe5mGdVk7N7dE9YRIlB1el1Dv6BxZNAKMsJ3Ms4vBTweu4-65kVJftiMrYhmSGY6QtTzqQ-xlLH4XzPd3jYIXlPQ45lxO7PW7EkJNs9m83VdcsJmHRHQ3PRP8V_mx0f2Ks4ga3xZ9IycAQB4q5NXLei_HJk8tRRJccZ6qB5nnAoD48Qu8JOEfhO596Mdoi-QCbH51iJZjgXo4gSRZ4KKK8jU0S6twLj_lf9jehENTqHDdtsRHdyCnICcPcz4AjFrNHEvUrsPkGxXSZ2BCGgDcvsSTVgGNV7rWU4IjH4FaDssenumi50R1QcZh8kiO35s9H6MngQsEm-zApRgd0V9_L3A6Ys47_crmKbunYRsATfMNBn2fKm5tS6RXvM2RN2G_Y9AkGgh2boY42CRy7HOcHby2vQ8IoQ-fZfE5xn_YYktNlKeNiCv3_-i86lANFbmB3tcdScrbjsgO6Tfg3u71VmJ_ZW1_vyMi5vCTEysLXfHG-OA85c3o8-25vcfuX5gIpbU-nMLgPagyn5w7Uazd27uhFfwepP9OMc8jz2JTlQICInLCUdESu8aG5d1F_IPUA5NU_ryPmebqUmyaRVDS8cGChxp0gZDNSiIvaggw8N2JCDGvk-s_PSG2pFGq0f4veYyWGBTHD_iX4a0UrhB471QZplRpMwvu7o' - return Realm.Sync.User.authenticate('http://localhost:9080', 'jwt', { token: token }) + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.jwt(token)) .then((user) => { TestCase.assertEqual(user.identity, 'austin_zheng') Promise.resolve() @@ -209,13 +191,13 @@ module.exports = { TestCase.assertArrayLength(Object.keys(all), 0); let user1; - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { const all = Realm.Sync.User.all; TestCase.assertArrayLength(Object.keys(all), 1); assertIsSameUser(all[user.identity], user); user1 = user; - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password'); + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()); }).then((user2) => { let all = Realm.Sync.User.all; TestCase.assertArrayLength(Object.keys(all), 2); @@ -238,11 +220,11 @@ module.exports = { TestCase.assertUndefined(Realm.Sync.User.current); let user1; - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { user1 = user; assertIsSameUser(Realm.Sync.User.current, user1); - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password'); + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()); }).then((user2) => { TestCase.assertThrows(() => Realm.Sync.User.current, 'We expect Realm.Sync.User.current to throw if > 1 user.'); user2.logout(); @@ -255,8 +237,7 @@ module.exports = { }, testGetExistingUser() { - let userid = uuid(); - return Realm.Sync.User.register('http://localhost:9080', userid, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { let identity = user.identity; let user1 = Realm.Sync.User._getExistingUser('http://localhost:9080', identity); assertIsSameUser(user1, user); @@ -267,7 +248,7 @@ module.exports = { }, testManagementRealm() { - return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { let realm = user.openManagementRealm(); TestCase.assertInstanceOf(realm, Realm); @@ -285,7 +266,8 @@ module.exports = { throw new Error("Test requires an admin user"); } - return Realm.Sync.User.login('http://localhost:9080', global.testAdminUserInfo.username, global.testAdminUserInfo.password).then((user) => { + const credentials = Realm.Sync.Credentials.usernamePassword(global.testAdminUserInfo.username, global.testAdminUserInfo.password); + return Realm.Sync.User.login('http://localhost:9080', credentials).then((user) => { TestCase.assertTrue(user.isAdmin, "Test requires an admin user"); return user.retrieveAccount('password', global.testAdminUserInfo.username) @@ -306,7 +288,8 @@ module.exports = { throw new Error("Test requires an admin user"); } - return Realm.Sync.User.login('http://localhost:9080', global.testAdminUserInfo.username, global.testAdminUserInfo.password).then((user) => { + const credentials = Realm.Sync.Credentials.usernamePassword(global.testAdminUserInfo.username, global.testAdminUserInfo.password); + return Realm.Sync.User.login('http://localhost:9080', credentials).then((user) => { TestCase.assertTrue(user.isAdmin, "Test requires an admin user"); let notExistingUsername = uuid(); @@ -320,8 +303,7 @@ module.exports = { }, testCreateConfiguration_defaultConfig() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { let config = user.createConfiguration(); TestCase.assertEqual(config.sync.url, "realm://localhost:9080/default"); TestCase.assertUndefined(config.sync.partial); @@ -330,8 +312,7 @@ module.exports = { }, testCreateConfiguration_useOldConfiguration() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { let config = user.createConfiguration({ sync: { url: 'http://localhost:9080/other_realm', partial: true }}); TestCase.assertEqual(config.sync.url, 'http://localhost:9080/other_realm'); TestCase.assertUndefined(config.sync.fullSynchronization); @@ -340,8 +321,7 @@ module.exports = { }, testCreateConfiguration_settingPartialAndFullSynchronizationThrows() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { TestCase.assertThrowsContaining(() => { let config = { sync: { @@ -356,8 +336,7 @@ module.exports = { }, testOpen_partialAndFullSynchronizationSetThrows() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { TestCase.assertThrowsContaining(() => { new Realm({ sync: { @@ -372,8 +351,7 @@ module.exports = { }, testSerialize() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => { + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()).then((user) => { const serialized = user.serialize(); TestCase.assertFalse(serialized.isAdmin); TestCase.assertEqual(serialized.identity, user.identity); @@ -383,8 +361,7 @@ module.exports = { }, testDeserialize() { - const username = uuid(); - return Realm.Sync.User.register('http://localhost:9080', username, 'password') + return Realm.Sync.User.login('http://localhost:9080', Realm.Sync.Credentials.anonymous()) .then((user) => { const userConfig = user.createConfiguration({ schema: [{ name: 'Dog', properties: { name: 'string' } }], diff --git a/tests/package-lock.json b/tests/package-lock.json new file mode 100644 index 0000000000..eed54dd158 --- /dev/null +++ b/tests/package-lock.json @@ -0,0 +1,359 @@ +{ + "name": "realm-tests-jasmine", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "requires": { + "color-name": "1.1.1" + } + }, + "color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "event-stream": { + "version": "3.3.4", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "jasmine": { + "version": "2.99.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.99.0.tgz", + "integrity": "sha1-jKctEC5jm4Z8ZImFbg4YqceqQrc=", + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.99.0" + } + }, + "jasmine-console-reporter": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/jasmine-console-reporter/-/jasmine-console-reporter-1.2.8.tgz", + "integrity": "sha1-2JPJwMElcn7Xd3Cc9Hh2gNBRU5Y=", + "requires": { + "chalk": "^2.1.0" + } + }, + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=" + }, + "jasmine-reporters": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-2.3.2.tgz", + "integrity": "sha512-u/7AT9SkuZsUfFBLLzbErohTGNsEUCKaQbsVYnLFW1gEuL2DzmBL4n8v90uZsqIqlWvWUgian8J6yOt5Fyk/+A==", + "requires": { + "mkdirp": "^0.5.1", + "xmldom": "^0.1.22" + } + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", + "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==" + }, + "needle": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-1.6.0.tgz", + "integrity": "sha1-9SpYWJchIWGOAC+OY4TK2sItYk8=", + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "requires": { + "through": "~2.3" + } + }, + "ps-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.0.1.tgz", + "integrity": "sha1-xkBjtM6Ncvf4dJdfPsxfNZesjks=", + "requires": { + "event-stream": "~3.3.0" + } + }, + "querystringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", + "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "segfault-handler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/segfault-handler/-/segfault-handler-1.0.1.tgz", + "integrity": "sha512-3koBV3F0IPxTYacnoL7WoFlaMndXsXj62tiVbbW9Kzp3K+F9Y6GWW5XmKSv7j+7nf2M+qjNzc4W1iZoa8vocjw==", + "requires": { + "bindings": "^1.2.1", + "nan": "^2.0.9" + } + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "requires": { + "through": "2" + } + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "requires": { + "duplexer": "~0.1.1" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "terminate": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-1.0.8.tgz", + "integrity": "sha1-FwPVBhS2/7oX0ZBphXH49isZfAw=", + "requires": { + "ps-tree": "1.0.1" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "requires": { + "os-tmpdir": "~1.0.1" + } + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==" + }, + "url-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz", + "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" + } + } +}