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
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
@@ -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. */
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) {
@@ -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)
),
};
@@ -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
@@ -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)'
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 {
@@ -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;
@@ -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);
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;
16 changes: 8 additions & 8 deletions src/Adapters/Auth/apple.js
Original file line number Diff line number Diff line change
@@ -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');
@@ -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}`
);
}
@@ -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);
@@ -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;
};
24 changes: 12 additions & 12 deletions src/Adapters/Auth/facebook.js
Original file line number Diff line number Diff line change
@@ -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');
@@ -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.');
});
}

@@ -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.');
}
}

@@ -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}`
);
}
@@ -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);
@@ -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;
};
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.
*
@@ -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);
@@ -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]) {
@@ -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}`);
}
}

@@ -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.');
}
}

@@ -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.`);
}
}

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
@@ -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;
@@ -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;
17 changes: 8 additions & 9 deletions src/Adapters/Auth/google.js
Original file line number Diff line number Diff line change
@@ -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');
@@ -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);
@@ -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.`
);
}
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.