Skip to content

refactor: Remove Javascript SDK as a dependency #9721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 20 commits into
base: alpha
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions spec/support/CurrentSpecReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const flakyTests = [
"ParseLiveQuery handle invalid websocket payload length",
// Unhandled promise rejection: TypeError: message.split is not a function
"rest query query internal field",
// Timeout
"ParseLiveQuery can handle afterEvent sendEvent to false",
];

/** The minimum execution time in seconds for a test to be considered slow. */
Expand Down
13 changes: 7 additions & 6 deletions src/AccountLockout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// This class handles the Account Lockout Policy settings.
import Parse from 'parse/node';
import ParseError from './ParseError';
import { encodeDate } from './Utils';

export class AccountLockout {
constructor(user, config) {
Expand Down Expand Up @@ -81,7 +82,7 @@ export class AccountLockout {
const now = new Date();

const updateFields = {
_account_lockout_expires_at: Parse._encode(
_account_lockout_expires_at: encodeDate(
new Date(now.getTime() + this._config.accountLockout.duration * 60 * 1000)
),
};
Expand All @@ -91,7 +92,7 @@ export class AccountLockout {
err &&
err.code &&
err.message &&
err.code === Parse.Error.OBJECT_NOT_FOUND &&
err.code === ParseError.OBJECT_NOT_FOUND &&
err.message === 'Object not found.'
) {
return; // nothing to update so we are good
Expand All @@ -110,14 +111,14 @@ export class AccountLockout {
_notLocked() {
const query = {
username: this._user.username,
_account_lockout_expires_at: { $gt: Parse._encode(new Date()) },
_account_lockout_expires_at: { $gt: encodeDate(new Date()) },
_failed_login_count: { $gte: this._config.accountLockout.threshold },
};

return this._config.database.find('_User', query).then(users => {
if (Array.isArray(users) && users.length > 0) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
'Your account is locked due to multiple failed login attempts. Please try again after ' +
this._config.accountLockout.duration +
' minute(s)'
Expand Down
12 changes: 7 additions & 5 deletions src/Adapters/Auth/BaseCodeAuthAdapter.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import ParseError from '../../ParseError';

// abstract class for auth code adapters
import AuthAdapter from './AuthAdapter';
export default class BaseAuthCodeAdapter extends AuthAdapter {
Expand Down Expand Up @@ -31,27 +33,27 @@ export default class BaseAuthCodeAdapter extends AuthAdapter {
async beforeFind(authData) {
if (this.enableInsecureAuth && !authData?.code) {
if (!authData?.access_token) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
}

const user = await this.getUserFromAccessToken(authData.access_token, authData);

if (user.id !== authData.id) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
}

return;
}

if (!authData?.code) {
throw new Parse.Error(Parse.Error.VALIDATION_ERROR, `${this.adapterName} code is required.`);
throw new ParseError(ParseError.VALIDATION_ERROR, `${this.adapterName} code is required.`);
}

const access_token = await this.getAccessTokenFromCode(authData);
const user = await this.getUserFromAccessToken(access_token, authData);

if (authData.id && user.id !== authData.id) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
}

authData.access_token = access_token;
Expand Down Expand Up @@ -104,7 +106,7 @@ export default class BaseAuthCodeAdapter extends AuthAdapter {
const startPos = data.indexOf('(');
const endPos = data.indexOf(')');
if (startPos === -1 || endPos === -1) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `${this.adapterName} auth is invalid for this user.`);
}
const jsonData = data.substring(startPos + 1, endPos);
return JSON.parse(jsonData);
Expand Down
5 changes: 3 additions & 2 deletions src/Adapters/Auth/OAuth1Client.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
var https = require('https'),
crypto = require('crypto');
var Parse = require('parse/node').Parse;

import ParseError from '../../ParseError';

var OAuth = function (options) {
if (!options) {
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'No options passed to OAuth');
throw new ParseError(ParseError.INTERNAL_SERVER_ERROR, 'No options passed to OAuth');
}
this.consumer_key = options.consumer_key;
this.consumer_secret = options.consumer_secret;
Expand Down
16 changes: 8 additions & 8 deletions src/Adapters/Auth/apple.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
// Apple SignIn Auth
// https://developer.apple.com/documentation/signinwithapplerestapi

const Parse = require('parse/node').Parse;
import ParseError from '../../ParseError';
const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');
const authUtils = require('./utils');
Expand All @@ -64,8 +64,8 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
try {
key = await authUtils.getSigningKey(client, keyId);
} catch (error) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
`Unable to find matching key for Key ID: ${keyId}`
);
}
Expand All @@ -74,7 +74,7 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {

const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
if (!token) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
}

const { kid: keyId, alg: algorithm } = authUtils.getHeaderFromToken(token);
Expand All @@ -96,18 +96,18 @@ const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMa
} catch (exception) {
const message = exception.message;

throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `${message}`);
}

if (jwtClaims.iss !== TOKEN_ISSUER) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
`id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`
);
}

if (jwtClaims.sub !== id) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
}
return jwtClaims;
};
Expand Down
24 changes: 12 additions & 12 deletions src/Adapters/Auth/facebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
*/

// Helper functions for accessing the Facebook Graph API.
const Parse = require('parse/node').Parse;
import ParseError from '../../ParseError';
const crypto = require('crypto');
const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');
Expand Down Expand Up @@ -88,7 +88,7 @@ function validateGraphToken(authData, options) {
if ((data && data.id == authData.id) || (process.env.TESTING && authData.id === 'test')) {
return;
}
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
});
}

Expand All @@ -98,16 +98,16 @@ async function validateGraphAppId(appIds, authData, options) {
return;
}
if (!Array.isArray(appIds)) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'appIds must be an array.');
}
if (!appIds.length) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'Facebook auth is not configured.');
}
const data = await graphRequest(
`app?access_token=${access_token}${getAppSecretPath(authData, options)}`
);
if (!data || !appIds.includes(data.id)) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'Facebook auth is invalid for this user.');
}
}

Expand All @@ -123,8 +123,8 @@ const getFacebookKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
try {
key = await authUtils.getSigningKey(client, keyId);
} catch (error) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
`Unable to find matching key for Key ID: ${keyId}`
);
}
Expand All @@ -133,7 +133,7 @@ const getFacebookKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {

const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
if (!token) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'id token is invalid for this user.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'id token is invalid for this user.');
}

const { kid: keyId, alg: algorithm } = authUtils.getHeaderFromToken(token);
Expand All @@ -155,18 +155,18 @@ const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMa
} catch (exception) {
const message = exception.message;

throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `${message}`);
}

if (jwtClaims.iss !== TOKEN_ISSUER) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
`id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`
);
}

if (jwtClaims.sub !== id) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'auth data is invalid for this user.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'auth data is invalid for this user.');
}
return jwtClaims;
};
Expand Down
15 changes: 8 additions & 7 deletions src/Adapters/Auth/gcenter.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ParseError from '../../ParseError';
/**
* Parse Server authentication adapter for Apple Game Center.
*
Expand Down Expand Up @@ -120,7 +121,7 @@ class GameCenterAuth extends AuthAdapter {
!headers.get('content-length') ||
parseInt(headers.get('content-length'), 10) > 10000
) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid rootCertificateURL.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'Invalid rootCertificateURL.');
}

this.ca.cert = pki.certificateFromPem(certificate);
Expand Down Expand Up @@ -160,7 +161,7 @@ class GameCenterAuth extends AuthAdapter {

async getAppleCertificate(publicKeyUrl) {
if (!this.verifyPublicKeyUrl(publicKeyUrl)) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Invalid publicKeyUrl: ${publicKeyUrl}`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `Invalid publicKeyUrl: ${publicKeyUrl}`);
}

if (this.cache[publicKeyUrl]) {
Expand All @@ -185,14 +186,14 @@ class GameCenterAuth extends AuthAdapter {
const publicKeyCert = pki.certificateFromPem(cert);

if (!this.ca.cert) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
'Root certificate is invalid or missing.'
);
}

if (!this.ca.cert.verify(publicKeyCert)) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Invalid publicKeyUrl: ${publicKeyUrl}`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `Invalid publicKeyUrl: ${publicKeyUrl}`);
}
}

Expand All @@ -206,7 +207,7 @@ class GameCenterAuth extends AuthAdapter {
verifier.update(Buffer.from(authData.salt, 'base64'));

if (!verifier.verify(publicKey, authData.signature, 'base64')) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid signature.');
throw new ParseError(ParseError.OBJECT_NOT_FOUND, 'Invalid signature.');
}
}

Expand All @@ -219,7 +220,7 @@ class GameCenterAuth extends AuthAdapter {

for (const key of requiredKeys) {
if (!authData[key]) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `AuthData ${key} is missing.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `AuthData ${key} is missing.`);
}
}

Expand Down
9 changes: 5 additions & 4 deletions src/Adapters/Auth/github.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ParseError from '../../ParseError';
/**
* Parse Server authentication adapter for GitHub.
* @class GitHubAdapter
Expand Down Expand Up @@ -88,12 +89,12 @@ class GitHubAdapter extends BaseCodeAuthAdapter {
});

if (!response.ok) {
throw new Parse.Error(Parse.Error.VALIDATION_ERROR, `Failed to exchange code for token: ${response.statusText}`);
throw new ParseError(ParseError.VALIDATION_ERROR, `Failed to exchange code for token: ${response.statusText}`);
}

const data = await response.json();
if (data.error) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, data.error_description || data.error);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, data.error_description || data.error);
}

return data.access_token;
Expand All @@ -110,12 +111,12 @@ class GitHubAdapter extends BaseCodeAuthAdapter {
});

if (!response.ok) {
throw new Parse.Error(Parse.Error.VALIDATION_ERROR, `Failed to fetch GitHub user: ${response.statusText}`);
throw new ParseError(ParseError.VALIDATION_ERROR, `Failed to fetch GitHub user: ${response.statusText}`);
}

const userData = await response.json();
if (!userData.id || !userData.login) {
throw new Parse.Error(Parse.Error.VALIDATION_ERROR, 'Invalid GitHub user data received.');
throw new ParseError(ParseError.VALIDATION_ERROR, 'Invalid GitHub user data received.');
}

return userData;
Expand Down
17 changes: 8 additions & 9 deletions src/Adapters/Auth/google.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@
'use strict';

// Helper functions for accessing the google API.
var Parse = require('parse/node').Parse;

import ParseError from '../../ParseError';
const https = require('https');
const jwt = require('jsonwebtoken');
const authUtils = require('./utils');
Expand Down Expand Up @@ -98,7 +97,7 @@ function getGoogleKeyByKeyId(keyId) {

async function verifyIdToken({ id_token: token, id }, { clientId }) {
if (!token) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `id token is invalid for this user.`);
}

const { kid: keyId, alg: algorithm } = authUtils.getHeaderFromToken(token);
Expand All @@ -112,23 +111,23 @@ async function verifyIdToken({ id_token: token, id }, { clientId }) {
});
} catch (exception) {
const message = exception.message;
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `${message}`);
}

if (jwtClaims.iss !== TOKEN_ISSUER && jwtClaims.iss !== HTTPS_TOKEN_ISSUER) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
`id token not issued by correct provider - expected: ${TOKEN_ISSUER} or ${HTTPS_TOKEN_ISSUER} | from: ${jwtClaims.iss}`
);
}

if (jwtClaims.sub !== id) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
throw new ParseError(ParseError.OBJECT_NOT_FOUND, `auth data is invalid for this user.`);
}

if (clientId && jwtClaims.aud !== clientId) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
throw new ParseError(
ParseError.OBJECT_NOT_FOUND,
`id token not authorized for this clientId.`
);
}
Expand Down
Loading
Loading