chore(): delete old AppError/OauthError code

This commit is contained in:
Amri Toufali
2025-12-01 20:30:02 -08:00
parent 5a8a388892
commit 775f5b5e25
148 changed files with 958 additions and 2966 deletions

View File

@@ -8,10 +8,6 @@ import { execSync } from 'child_process';
// Maintain List of frozen files here!
const frozen: Array<{ pattern: string; reason: string }> = [
{
pattern: 'packages/fxa-auth-server/(lib|lib/oauth)/error.js',
reason: 'Files moved to libs/accounts/errors',
},
{
pattern: 'packages/fxa-auth-server/lib/senders/email.js',
reason: 'Files moved to libs/accounts/email-sender',

View File

@@ -10,9 +10,10 @@ import {
DEFAULT_ERRROR,
IGNORED_ERROR_NUMBERS,
DEBUGGABLE_PAYLOAD_KEYS,
OAUTH_ERRNO,
} from './constants';
import { OauthError } from './oauth-error';
import { Request as HapiRequest } from 'hapi';
import type { Request as HapiRequest } from 'hapi';
/**
* Augmented Hapi request. Extends request.app interface auth-server specific feilds that this lib uses.
@@ -296,7 +297,7 @@ export class AppError extends Error {
);
}
static accountExists(email: string) {
static accountExists(email?: string) {
return new AppError(
{
code: 400,
@@ -310,7 +311,7 @@ export class AppError extends Error {
);
}
static unknownAccount(email: string) {
static unknownAccount(email?: string) {
return new AppError(
{
code: 400,
@@ -387,7 +388,7 @@ export class AppError extends Error {
});
}
static invalidVerificationCode(details: Record<string, any>) {
static invalidVerificationCode(details?: Record<string, any>) {
return new AppError(
{
code: 400,
@@ -408,7 +409,7 @@ export class AppError extends Error {
});
}
static invalidRequestParameter(validation: string) {
static invalidRequestParameter(validation?: unknown) {
return new AppError(
{
code: 400,
@@ -422,7 +423,7 @@ export class AppError extends Error {
);
}
static missingRequestParameter(param: string) {
static missingRequestParameter(param?: string) {
return new AppError(
{
code: 400,
@@ -486,7 +487,7 @@ export class AppError extends Error {
});
}
static unauthorized(reason: string) {
static unauthorized(reason?: string) {
return new AppError(
{
code: 401,
@@ -575,7 +576,7 @@ export class AppError extends Error {
);
}
static serviceUnavailable(retryAfter: number) {
static serviceUnavailable(retryAfter?: number) {
if (!retryAfter) {
retryAfter = 30;
}
@@ -595,7 +596,7 @@ export class AppError extends Error {
);
}
static featureNotEnabled(retryAfter: number) {
static featureNotEnabled(retryAfter?: number) {
if (!retryAfter) {
retryAfter = 30;
}
@@ -656,7 +657,7 @@ export class AppError extends Error {
});
}
static deviceSessionConflict(deviceId: string) {
static deviceSessionConflict(deviceId?: string) {
return new AppError(
{
code: 400,
@@ -770,7 +771,10 @@ export class AppError extends Error {
);
}
static currencyCountryMismatch(currency: string, country: string) {
static currencyCountryMismatch(
currency?: string | null,
country?: string | null
) {
return new AppError(
{
code: 400,
@@ -785,7 +789,10 @@ export class AppError extends Error {
);
}
static currencyCurrencyMismatch(currencyA: string, currencyB: string) {
static currencyCurrencyMismatch(
currencyA?: string | null,
currencyB?: string | null
) {
return new AppError(
{
code: 400,
@@ -1249,6 +1256,15 @@ export class AppError extends Error {
});
}
static oauthNotPublicClient() {
return new AppError({
code: 400,
error: 'Bad Request',
errno: OAUTH_ERRNO.NOT_PUBLIC_CLIENT,
message: 'Not a public client',
});
}
static redisConflict() {
return new AppError({
code: 409,
@@ -1376,7 +1392,8 @@ export class AppError extends Error {
);
}
static unknownCustomer(uid?: string) {
static unknownCustomer(uid?: string | { id?: string | null } | null) {
const resolvedUid = typeof uid === 'string' ? uid : (uid?.id ?? undefined);
return new AppError(
{
code: 404,
@@ -1385,7 +1402,7 @@ export class AppError extends Error {
message: 'Unknown customer',
},
{
uid,
uid: resolvedUid,
}
);
}
@@ -1433,8 +1450,8 @@ export class AppError extends Error {
}
static rejectedSubscriptionPaymentToken(
message: string,
paymentError: Error
message?: string,
paymentError?: Error
) {
return new AppError(
{
@@ -1443,11 +1460,13 @@ export class AppError extends Error {
errno: ERRNO.REJECTED_SUBSCRIPTION_PAYMENT_TOKEN,
message,
},
undefined,
undefined,
paymentError
);
}
static rejectedCustomerUpdate(message: string, paymentError: Error) {
static rejectedCustomerUpdate(message?: string, paymentError?: Error) {
return new AppError(
{
code: 400,
@@ -1455,6 +1474,8 @@ export class AppError extends Error {
errno: ERRNO.REJECTED_CUSTOMER_UPDATE,
message,
},
undefined,
undefined,
paymentError
);
}
@@ -1541,9 +1562,9 @@ export class AppError extends Error {
static invalidInvoicePreviewRequest(
error: Error,
message: string,
priceId: string,
customer: string
message?: string,
priceId?: string,
customer?: string
) {
const extra = error ? [{}, undefined, error] : [];
return new AppError(
@@ -1638,7 +1659,12 @@ export class AppError extends Error {
);
}
static internalValidationError(op: string, data: unknown, error: Error) {
static internalValidationError(
op: string,
data?: unknown,
error?: Error | string
) {
const errInstance = typeof error === 'string' ? new Error(error) : error;
return new AppError(
{
code: 500,
@@ -1651,7 +1677,7 @@ export class AppError extends Error {
data,
},
{},
error
errInstance
);
}

View File

@@ -133,6 +133,33 @@ export const ERRNO = {
UNEXPECTED_ERROR: 999,
};
export const OAUTH_ERRNO = {
UNKNOWN_CLIENT: 101,
INCORRECT_SECRET: 102,
INCORRECT_REDIRECT: 103,
INVALID_ASSERTION: 104,
UNKNOWN_CODE: 105,
INCORRECT_CODE: 106,
EXPIRED_CODE: 107,
INVALID_TOKEN: 108,
INVALID_PARAMETER: 109,
INVALID_RESPONSE_TYPE: 110,
UNAUTHORIZED: 111,
FORBIDDEN: 112,
INVALID_CONTENT_TYPE: 113,
INVALID_SCOPES: 114,
EXPIRED_TOKEN: 115,
NOT_PUBLIC_CLIENT: 116,
INCORRECT_CODE_CHALLENGE: 117,
MISSING_PKCE_PARAMETERS: 118,
STALE_AUTH_AT: 119,
MISMATCH_ACR_VALUES: 120,
INVALID_GRANT_TYPE: 121,
UNKNOWN_TOKEN: 122,
SERVER_UNAVAILABLE: 201,
DISABLED_CLIENT_ID: 202,
};
/**
* Takes an object and swaps keys with values. Useful when a value -> key look up is needed.
* @param obj - Object to swap keys and values on
@@ -149,6 +176,7 @@ function swapObjectKeysAndValues(obj: { [key: string]: string | number }) {
* A reversed map of errnos.
*/
export const ERRNO_REVERSE_MAP = swapObjectKeysAndValues(ERRNO);
export const OAUTH_ERRNO_REVERSE_MAP = swapObjectKeysAndValues(OAUTH_ERRNO);
/**
* The Default Unexpected Error State

View File

@@ -2,5 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export * from './constants';
export * from './app-error';
export * from './oauth-error';
export * from './util';

View File

@@ -59,7 +59,7 @@ if (config.subscriptions && config.subscriptions.stripeApiKey) {
}
}
const error = require('../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const Token = require('../lib/tokens')(log, config);
const SQSReceiver = require('../lib/sqs')(log, statsd);
const bounces = require('../lib/email/bounces')(log, error, config);

View File

@@ -24,7 +24,7 @@ const {
} = require('@fxa/shared/cms');
const TracingProvider = require('fxa-shared/tracing/node-tracing');
const error = require('../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const { JWTool } = require('@fxa/vendored/jwtool');
const { StatsD } = require('hot-shots');
const { Container } = require('typedi');

View File

@@ -13,7 +13,7 @@ import { StatsD } from 'hot-shots';
import * as Sentry from '@sentry/node';
import { ConfigType } from '../config';
import { ERRNO } from './error';
import { ERRNO } from '@fxa/accounts/errors';
import OAuthDb from './oauth/db';
import { AppleIAP } from './payments/iap/apple-app-store/apple-iap';
import { PlayBilling } from './payments/iap/google-play/play-billing';

View File

@@ -4,7 +4,7 @@
'use strict';
const error = require('./error');
const { AppError: error } = require('@fxa/accounts/errors');
// Maps our variety of verification methods down to a few short standard
// "authentication method reference" strings that we're happy to expose to

View File

@@ -4,7 +4,7 @@
'use strict';
const error = require('./error');
const { AppError: error } = require('@fxa/accounts/errors');
module.exports = (config, db) => {
const configBounces = (config.smtp && config.smtp.bounces) || {};

View File

@@ -30,7 +30,7 @@ import { normalizeEmail } from 'fxa-shared/email/helpers';
import { StatsD } from 'hot-shots';
import { Container } from 'typedi';
import random, { base32 } from './crypto/random';
import error from './error';
import { AppError as error } from '@fxa/accounts/errors';
import {
verificationMethodToString,
VerificationMethod,
@@ -1064,15 +1064,15 @@ export const createDB = (
);
}
async verifiedLoginSecurityEventsByUid(params: { uid: string; skipTimeframeMs: number}) {
async verifiedLoginSecurityEventsByUid(params: {
uid: string;
skipTimeframeMs: number;
}) {
log.trace('DB.verifiedLoginSecurityEventsByUid', {
params: params,
});
const { uid, skipTimeframeMs } = params;
return SecurityEvent.findByUidAndVerifiedLogin(
uid,
skipTimeframeMs
);
return SecurityEvent.findByUidAndVerifiedLogin(uid, skipTimeframeMs);
}
async securityEventsByUid(params: { uid: string }) {

View File

@@ -8,7 +8,7 @@ const isA = require('joi');
const Sentry = require('@sentry/node');
const DESCRIPTION = require('../docs/swagger/shared/descriptions').default;
const validators = require('./routes/validators');
const error = require('./error');
const { AppError: error } = require('@fxa/accounts/errors');
const oauthDB = require('./oauth/db');
const { DISPLAY_SAFE_UNICODE_WITH_NON_BMP, HEX_STRING, URL_SAFE_BASE_64 } =
validators;

View File

@@ -14,7 +14,7 @@ import {
import { ConfigType } from '../config';
import { AuthRequest } from './types';
import { IncomingHttpHeaders } from 'http';
import AppError from './error';
import { AppError } from '@fxa/accounts/errors';
import {
InactiveAccountsManager,
emailTypeToHandlerVals,

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
'use strict';
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
const ACTIVITY_EVENTS = new Set([
'account.changedPassword',

View File

@@ -10,7 +10,7 @@ import {
import { version } from '../../../package.json';
import { createHash } from 'crypto';
import { AuthRequest } from '../../types';
import * as AppError from '../../error';
import { AppError } from '@fxa/accounts/errors';
import { clientId as clientIdValidator } from '../../oauth/validators';
import { MetricsContext } from '@fxa/shared/metrics/glean';
@@ -423,7 +423,7 @@ export function gleanMetrics(config: ConfigType) {
knownIp: createEventFn('login_confirm_skip_for_known_ip'),
newAccount: createEventFn('login_confirm_skip_for_new_account'),
knownDevice: createEventFn('login_confirm_skip_for_known_device'),
}
},
};
}

View File

@@ -10,7 +10,7 @@ const logger = require('./log')(
'configure-sentry'
);
const { version } = require('../package.json');
const { ignoreErrors } = require('./error');
const { ignoreErrors } = require('@fxa/accounts/errors');
/**
* Initialize sentry & otel

View File

@@ -17,7 +17,7 @@
const Joi = require('joi');
const validators = require('./validators');
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const { config } = require('../../config');
const { verifyJWT } = require('../../lib/serverJWT');

View File

@@ -4,7 +4,7 @@
const ScopeSet = require('fxa-shared').oauth.scopes;
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const token = require('./token');
const validators = require('./validators');

View File

@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const oauthDB = require('./db');
const ScopeSet = require('fxa-shared').oauth.scopes;

View File

@@ -6,7 +6,7 @@ const crypto = require('crypto');
const buf = (v) => (Buffer.isBuffer(v) ? v : Buffer.from(v, 'hex'));
const Joi = require('joi');
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const validators = require('./validators');
const db = require('./db');
const encrypt = require('fxa-shared/auth/encrypt');
@@ -59,7 +59,7 @@ module.exports.getClientCredentials = function getClientCredentials(
// the Authorization header or request body, but not both.
if (headers.authorization) {
const authzMatch = validators.BASIC_AUTH_HEADER.exec(headers.authorization);
const err = new OauthError.invalidRequestParameter({
const err = OauthError.invalidRequestParameter({
keys: ['authorization'],
});
if (!authzMatch || creds.client_id || creds.client_secret) {
@@ -106,7 +106,7 @@ module.exports.authenticateClient = async function authenticateClient(
// and should never submit a client_secret.
if (client.publicClient) {
if (creds.client_secret) {
throw new OauthError.invalidRequestParameter({ keys: ['client_secret'] });
throw OauthError.invalidRequestParameter({ keys: ['client_secret'] });
}
return client;
}
@@ -114,7 +114,7 @@ module.exports.authenticateClient = async function authenticateClient(
// Check client_secret against both current and previous stored secrets,
// to allow for seamless rotation of the secret.
if (!creds.client_secret) {
throw new OauthError.invalidRequestParameter({ keys: ['client_secret'] });
throw OauthError.invalidRequestParameter({ keys: ['client_secret'] });
}
const submitted = encrypt.hash(buf(creds.client_secret));
const stored = client.hashedSecret;

View File

@@ -1,387 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const util = require('util');
const hex = (v) => (Buffer.isBuffer(v) ? v.toString('hex') : v);
const DEFAULTS = {
code: 500,
error: 'Internal Server Error',
errno: 999,
info: 'https://mozilla.github.io/ecosystem-platform/api#section/Errors',
message: 'Unspecified error',
};
function merge(target, other) {
var keys = Object.keys(other || {});
for (var i = 0; i < keys.length; i++) {
target[keys[i]] = other[keys[i]];
}
}
// Deprecated. New error types should be defined in lib/error.js
function OauthError(options, extra, headers) {
this.message = options.message || DEFAULTS.message;
this.isBoom = true;
if (options.stack) {
this.stack = options.stack;
} else {
Error.captureStackTrace(this, OauthError);
}
this.errno = options.errno || DEFAULTS.errno;
this.output = {
statusCode: options.code || DEFAULTS.code,
payload: {
code: options.code || DEFAULTS.code,
errno: this.errno,
error: options.error || DEFAULTS.error,
message: this.message,
info: options.info || DEFAULTS.info,
},
headers: headers || {},
};
merge(this.output.payload, extra);
}
util.inherits(OauthError, Error);
OauthError.prototype.toString = function () {
return 'Error: ' + this.message;
};
OauthError.prototype.header = function (name, value) {
this.output.headers[name] = value;
};
OauthError.isOauthRoute = function isOauthRoute(path) {
const routes = require('../routes/oauth')();
// ironically, routes that include "oauth" are considered auth-server routes
return (
// For now we use a fragile heuristic that all oauth routes set cors config
// TODO: when we merge oauth errors into auth, rethink this.
routes.findIndex((r) => `/v1${r.path}` === path && r.config.cors) > -1
);
};
OauthError.translate = function translate(response) {
if (response instanceof OauthError) {
return response;
}
var error;
var payload = response.output.payload;
if (payload.validation) {
error = OauthError.invalidRequestParameter(payload.validation);
} else if (payload.statusCode === 415) {
error = OauthError.invalidContentType();
} else {
error = new OauthError({
message: payload.message,
code: payload.statusCode,
error: payload.error,
errno: payload.errno,
stack: response.stack,
});
}
return error;
};
OauthError.prototype.backtrace = function (traced) {
this.output.payload.log = traced;
};
OauthError.unexpectedError = function unexpectedError() {
return new OauthError({});
};
OauthError.unknownClient = function unknownClient(clientId) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 101,
message: 'Unknown client',
},
{
clientId: hex(clientId),
}
);
};
OauthError.incorrectSecret = function incorrectSecret(clientId) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 102,
message: 'Incorrect secret',
},
{
clientId: hex(clientId),
}
);
};
OauthError.incorrectRedirect = function incorrectRedirect(uri) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 103,
message: 'Incorrect redirect_uri',
},
{
redirectUri: uri,
}
);
};
OauthError.invalidAssertion = function invalidAssertion() {
return new OauthError({
code: 401,
error: 'Bad Request',
errno: 104,
message: 'Invalid assertion',
});
};
OauthError.unknownCode = function unknownCode(code) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 105,
message: 'Unknown code',
},
{
requestCode: code,
}
);
};
OauthError.mismatchCode = function mismatchCode(code, clientId) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 106,
message: 'Incorrect code',
},
{
requestCode: hex(code),
client: hex(clientId),
}
);
};
OauthError.expiredCode = function expiredCode(code, expiredAt) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 107,
message: 'Expired code',
},
{
requestCode: hex(code),
expiredAt: expiredAt,
}
);
};
OauthError.invalidToken = function invalidToken() {
return new OauthError({
code: 400,
error: 'Bad Request',
errno: 108,
message: 'Invalid token',
});
};
OauthError.invalidRequestParameter = function invalidRequestParameter(val) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 109,
message: 'Invalid request parameter',
},
{
validation: val,
}
);
};
OauthError.invalidResponseType = function invalidResponseType() {
return new OauthError({
code: 400,
error: 'Bad Request',
errno: 110,
message: 'Invalid response_type',
});
};
OauthError.unauthorized = function unauthorized(reason) {
return new OauthError(
{
code: 401,
error: 'Unauthorized',
errno: 111,
message: 'Unauthorized for route',
},
{
detail: reason,
}
);
};
OauthError.forbidden = function forbidden() {
return new OauthError({
code: 403,
error: 'Forbidden',
errno: 112,
message: 'Forbidden',
});
};
OauthError.invalidContentType = function invalidContentType() {
return new OauthError({
code: 415,
error: 'Unsupported Media Type',
errno: 113,
message:
'Content-Type must be either application/json or ' +
'application/x-www-form-urlencoded',
});
};
OauthError.invalidScopes = function invalidScopes(scopes) {
return new OauthError(
{
code: 400,
error: 'Invalid scopes',
errno: 114,
message: 'Requested scopes are not allowed',
},
{
invalidScopes: scopes,
}
);
};
OauthError.expiredToken = function expiredToken(expiredAt) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 115,
message: 'Expired token',
},
{
expiredAt: expiredAt,
}
);
};
OauthError.notPublicClient = function notPublicClient(clientId) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 116,
message: 'Not a public client',
},
{
clientId: hex(clientId),
}
);
};
OauthError.mismatchCodeChallenge = function mismatchCodeChallenge(
pkceHashValue
) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 117,
message: 'Incorrect code_challenge',
},
{
requestCodeChallenge: pkceHashValue,
}
);
};
OauthError.missingPkceParameters = function missingPkceParameters() {
return new OauthError({
code: 400,
error: 'PKCE parameters missing',
errno: 118,
message: 'Public clients require PKCE OAuth parameters',
});
};
OauthError.staleAuthAt = function staleAuthAt(authAt) {
return new OauthError(
{
code: 401,
error: 'Bad Request',
errno: 119,
message: 'Stale authentication timestamp',
},
{
authAt: authAt,
}
);
};
OauthError.mismatchAcr = function mismatchAcr(foundValue) {
return new OauthError(
{
code: 400,
error: 'Bad Request',
errno: 120,
message: 'Mismatch acr value',
},
{ foundValue }
);
};
OauthError.invalidGrantType = function invalidGrantType() {
return new OauthError({
code: 400,
error: 'Bad Request',
errno: 121,
message: 'Invalid grant_type',
});
};
OauthError.unknownToken = function unknownToken() {
return new OauthError({
code: 400,
error: 'Bad Request',
errno: 122,
message: 'Unknown token',
});
};
// N.B. `errno: 201` is traditionally our generic "service unavailable" error,
// so let's reserve it for that purpose here as well.
OauthError.disabledClient = function disabledClient(clientId) {
return new OauthError(
{
code: 503,
error: 'Client Disabled',
errno: 202, // TODO reconcile this with the auth-server version
message: 'This client has been temporarily disabled',
},
{ clientId }
);
};
// Deprecated. New error types should be defined in lib/error.js
module.exports = OauthError;

View File

@@ -6,7 +6,7 @@ const { Container } = require('typedi');
const { CapabilityService } = require('../payments/capability');
const { config } = require('../../config');
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const db = require('./db');
const util = require('./util');
const ScopeSet = require('fxa-shared').oauth.scopes;
@@ -46,7 +46,6 @@ const TRUSTED_CLIENT_ALLOWED_SCOPES = ScopeSet.fromArray([
'profile:subscriptions',
]);
/** @type {CapabilityService} */
let capabilityService = undefined;
@@ -116,9 +115,13 @@ module.exports.validateRequestedGrant = async function validateRequestedGrant(
// For trusted clients, validate scopes against clients trusted scopes
if (client.trusted) {
const clientScopeSet = ScopeSet.fromString(client.allowedScopes || '');
const trustedClientAllowedScopes = clientScopeSet.union(TRUSTED_CLIENT_ALLOWED_SCOPES);
const trustedClientAllowedScopes = clientScopeSet.union(
TRUSTED_CLIENT_ALLOWED_SCOPES
);
const invalidScopes = requestedGrant.scope.difference(trustedClientAllowedScopes);
const invalidScopes = requestedGrant.scope.difference(
trustedClientAllowedScopes
);
if (!invalidScopes.isEmpty()) {
if (config.get('oauthServer.strictScopeValidation')) {
// Strict mode: remove invalid scopes

View File

@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const jwt = require('./jwt');
const sub = require('./jwt_sub');
const { OAUTH_SCOPE_OLD_SYNC } = require('fxa-shared/oauth/constants');
@@ -23,8 +23,8 @@ exports.create = async function generateJWTAccessToken(accessToken, grant) {
const audience = grant.resource
? [clientId, grant.resource]
: grant.scope.contains(OAUTH_SCOPE_OLD_SYNC)
? TOKEN_SERVER_URL
: clientId;
? TOKEN_SERVER_URL
: clientId;
// Claims list from:
// https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt#section-2.2

View File

@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const jwt = require('./jwt');
/**

View File

@@ -4,7 +4,7 @@
const ScopeSet = require('fxa-shared').oauth.scopes;
const OauthError = require('./error');
const { OauthError } = require('@fxa/accounts/errors');
const { config } = require('../../config');
const db = require('./db');
const encrypt = require('fxa-shared/auth/encrypt');

View File

@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const AuthError = require('../error');
const { AppError: AuthError } = require('@fxa/accounts/errors');
const { signJWT } = require('../serverJWT');
const crypto = require('crypto');

View File

@@ -32,7 +32,7 @@ import {
import * as Sentry from '@sentry/node';
import { SeverityLevel } from '@sentry/core';
import error from '../error';
import { AppError as error } from '@fxa/accounts/errors';
import { authEvents } from '../events';
import { AppConfig, AuthLogger, AuthRequest } from '../types';
import { ConfigType } from '../../config';

View File

@@ -5,7 +5,7 @@ import { randomUUID } from 'crypto';
import { Logger } from 'mozlog';
import { Container } from 'typedi';
import errors from '../../error';
import { AppError as errors } from '@fxa/accounts/errors';
import { AppConfig, AuthFirestore, AuthLogger } from '../../types';
import {

View File

@@ -5,7 +5,7 @@ import { AppendedAppStoreSubscriptionPurchase } from 'fxa-shared/payments/iap/ap
import { MozillaSubscriptionTypes } from 'fxa-shared/subscriptions/types';
import Container from 'typedi';
import { internalValidationError } from '../../../../lib/error';
import { AppError } from '@fxa/accounts/errors';
import { AppConfig } from '../../../types';
import { StripeHelper } from '../../stripe';
import { SubscriptionsService } from '../types';
@@ -20,7 +20,7 @@ export class AppStoreSubscriptions
constructor() {
const config = Container.get(AppConfig);
if (!config.subscriptions.enabled) {
throw internalValidationError(
throw AppError.internalValidationError(
'AppStoreSubscriptions',
{},
new Error(
@@ -30,7 +30,7 @@ export class AppStoreSubscriptions
}
if (!Container.has(StripeHelper)) {
throw internalValidationError(
throw AppError.internalValidationError(
'AppStoreSubscriptions',
{},
new Error(

View File

@@ -4,7 +4,7 @@
import { MozillaSubscriptionTypes } from 'fxa-shared/subscriptions/types';
import Container from 'typedi';
import { internalValidationError } from '../../../../lib/error';
import { AppError } from '@fxa/accounts/errors';
import { AppConfig } from '../../../types';
import { StripeHelper } from '../../stripe';
import { SubscriptionsService } from '../types';
@@ -20,7 +20,7 @@ export class PlaySubscriptions
constructor() {
const config = Container.get(AppConfig);
if (!config.subscriptions.enabled) {
throw internalValidationError(
throw AppError.internalValidationError(
'PlaySubscriptions',
{},
new Error(
@@ -30,7 +30,7 @@ export class PlaySubscriptions
}
if (!Container.has(StripeHelper)) {
throw internalValidationError(
throw AppError.internalValidationError(
'PlaySubscriptions',
{},
new Error(

View File

@@ -5,7 +5,7 @@ import { Firestore } from '@google-cloud/firestore';
import { Container } from 'typedi';
import { TypedCollectionReference } from '@fxa/vendored/typesafe-node-firestore';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { AppConfig, AuthFirestore, AuthLogger } from '../../types';
import { IapConfig } from './types';

View File

@@ -21,7 +21,7 @@ import {
TransactionSearchOptions,
TransactionStatus,
} from '@fxa/payments/paypal';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { CurrencyHelper } from '../currencies';
import { StripeHelper } from '../stripe';
import { RefusedError } from './error';
@@ -525,7 +525,7 @@ export class PayPalHelper {
try {
[transactionResponse] = (await Promise.all(promises)) as [
ChargeResponse,
any
any,
];
} catch (err) {
if (PayPalClientError.hasPayPalNVPError(err) && !batchProcessing) {
@@ -672,14 +672,19 @@ export class PayPalHelper {
/**
* Refunds the invoice passed, throwing an error on any issue encountered.
*/
public async refundInvoice(invoice: Stripe.Invoice, behavior: {
refundType: RefundType.Full,
} | {
refundType: RefundType.Partial,
amount: number
} = {
refundType: RefundType.Full,
}) {
public async refundInvoice(
invoice: Stripe.Invoice,
behavior:
| {
refundType: RefundType.Full;
}
| {
refundType: RefundType.Partial;
amount: number;
} = {
refundType: RefundType.Full,
}
) {
this.log.debug('PayPalHelper.refundInvoice', {
invoiceId: invoice.id,
});
@@ -707,12 +712,25 @@ export class PayPalHelper {
throw new RefundError('Invoice already refunded with PayPal');
}
if (behavior.refundType === RefundType.Partial && behavior.amount >= invoice.amount_paid) {
throw new RefundError('Partial refunds must be less than the amount paid on the invoice');
if (
behavior.refundType === RefundType.Partial &&
behavior.amount >= invoice.amount_paid
) {
throw new RefundError(
'Partial refunds must be less than the amount paid on the invoice'
);
}
const amount = behavior.refundType === RefundType.Partial ? behavior.amount : undefined;
const amount =
behavior.refundType === RefundType.Partial
? behavior.amount
: undefined;
await this.issueRefund(invoice, transactionId, behavior.refundType, amount);
await this.issueRefund(
invoice,
transactionId,
behavior.refundType,
amount
);
this.log.info('refundInvoice', {
invoiceId: invoice.id,

View File

@@ -8,7 +8,7 @@ import { Container } from 'typedi';
import { PayPalClientError } from '@fxa/payments/paypal';
import { ConfigType } from '../../../config';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { StripeWebhookHandler } from '../../routes/subscriptions/stripe-webhook';
import { reportSentryError } from '../../sentry';
import { AuthLogger } from '../../types';

View File

@@ -61,7 +61,7 @@ import { Stripe } from 'stripe';
import { Container } from 'typedi';
import { ConfigType } from '../../config';
import error from '../error';
import { AppError as error } from '@fxa/accounts/errors';
import { GoogleMapsService } from '../google-maps-services';
import Redis from '../redis';
import { subscriptionProductMetadataValidator } from '../routes/validators';
@@ -2181,7 +2181,7 @@ export class StripeHelper extends StripeHelperBase {
subscriptionId
);
if (!subscription) {
throw error.unknownSubscription();
throw error.unknownSubscription(subscriptionId);
}
await this.updateSubscriptionAndBackfill(subscription, {
@@ -2213,7 +2213,7 @@ export class StripeHelper extends StripeHelperBase {
subscriptionId
);
if (!subscription) {
throw error.unknownSubscription();
throw error.unknownSubscription(subscriptionId);
}
if (!ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status)) {
@@ -3545,8 +3545,10 @@ export class StripeHelper extends StripeHelperBase {
* Process a invoice event that needs to be saved to Firestore.
*/
async processInvoiceEventToFirestore(event: Stripe.Event) {
if (event.data.object.object !== "invoice") {
throw new Error("processInvoiceEventToFirestore must receive only invoice types");
if (event.data.object.object !== 'invoice') {
throw new Error(
'processInvoiceEventToFirestore must receive only invoice types'
);
}
const eventData = event.data.object;
@@ -3554,15 +3556,21 @@ export class StripeHelper extends StripeHelperBase {
if (!invoiceId) throw new Error('Invoice ID must be specified');
const customerId = eventData.customer;
if (typeof customerId !== 'string') {
throw new Error("customerId must be a string");
throw new Error('customerId must be a string');
}
try {
await this.stripeFirestore.fetchAndInsertInvoice(invoiceId, event.created);
await this.stripeFirestore.fetchAndInsertInvoice(
invoiceId,
event.created
);
} catch (err) {
if (err.name === FirestoreStripeError.FIRESTORE_CUSTOMER_NOT_FOUND) {
await this.stripeFirestore.fetchAndInsertCustomer(customerId);
await this.stripeFirestore.fetchAndInsertInvoice(invoiceId, event.created);
await this.stripeFirestore.fetchAndInsertInvoice(
invoiceId,
event.created
);
return;
}
throw err;
@@ -3586,7 +3594,10 @@ export class StripeHelper extends StripeHelperBase {
const paymentMethodId = (event.data.object as Stripe.PaymentMethod).id;
try {
await this.stripeFirestore.fetchAndInsertPaymentMethod(paymentMethodId, event.created);
await this.stripeFirestore.fetchAndInsertPaymentMethod(
paymentMethodId,
event.created
);
} catch (err) {
if (
!(

View File

@@ -20,7 +20,7 @@ import { StatsD } from 'hot-shots';
import { performance } from 'perf_hooks';
import { ConfigType } from '../../config';
import error from '../error';
import { AppError as error } from '@fxa/accounts/errors';
import { PushboxDB } from './db';
// Pushbox stores strings, so these are a little pair

View File

@@ -22,7 +22,7 @@ import MISC_DOCS from '../../docs/swagger/misc-api';
import DESCRIPTION from '../../docs/swagger/shared/descriptions';
import authMethods from '../authMethods';
import random from '../crypto/random';
import error from '../error';
import { AppError as error } from '@fxa/accounts/errors';
import { getClientById } from '../oauth/client';
import { generateAccessToken } from '../oauth/grant';
import jwt from '../oauth/jwt';

View File

@@ -7,7 +7,7 @@
const isA = require('joi');
const validators = require('./validators');
const authorizedClients = require('../oauth/authorized_clients');
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
const HEX_STRING = validators.HEX_STRING;
const DEVICES_SCHEMA = require('../devices').schema;

View File

@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const AppError = require('../../error');
const { AppError } = require('@fxa/accounts/errors');
const token = require('../../oauth/token');
const authName = 'fxa-oauth';

View File

@@ -4,7 +4,7 @@
import { OAuth2Client } from 'google-auth-library';
import { Request, ResponseToolkit } from '@hapi/hapi';
import AppError from '../../error';
import { AppError } from '@fxa/accounts/errors';
export const strategy = ({
aud,

View File

@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const AppError = require('../../error');
const { AppError } = require('@fxa/accounts/errors');
const Boom = require('@hapi/boom');
// The following regexes and Hawk header parsing are taken from the Hawk library.

View File

@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { Request, ResponseToolkit } from '@hapi/hapi';
import * as AppError from '../../error';
import { AppError } from '@fxa/accounts/errors';
import { ConfigType } from '../../../config/index';
import * as jwt from 'jsonwebtoken';
import { Account } from 'fxa-shared/db/models/auth';

View File

@@ -4,7 +4,7 @@
'use strict';
const AppError = require('../../error');
const { AppError } = require('@fxa/accounts/errors');
const joi = require('joi');
const validators = require('../validators');
const { BEARER_AUTH_REGEX } = validators;
@@ -26,15 +26,14 @@ module.exports = function schemeRefreshTokenScheme(config, db) {
return {
async authenticate(request, h) {
if (config.oauth.deviceAccessEnabled === false) {
throw new AppError.featureNotEnabled();
throw AppError.featureNotEnabled();
}
const bearerMatch = BEARER_AUTH_REGEX.exec(
request.headers.authorization
);
const bearerMatchErr = new AppError.invalidRequestParameter(
'authorization'
);
const bearerMatchErr =
AppError.invalidRequestParameter('authorization');
const bearerToken = bearerMatch && bearerMatch[1];
if (bearerToken) {
joi.attempt(bearerMatch[1], validators.refreshToken, bearerMatchErr);
@@ -45,7 +44,7 @@ module.exports = function schemeRefreshTokenScheme(config, db) {
const tokenId = encrypt.hash(bearerToken);
const refreshToken = await oauthDB.getRefreshToken(tokenId);
if (!refreshToken) {
return h.unauthenticated(new AppError.invalidToken());
return h.unauthenticated(AppError.invalidToken());
}
if (
!refreshToken.scope.intersects(ALLOWED_REFRESH_TOKEN_SCHEME_SCOPES)
@@ -71,7 +70,7 @@ module.exports = function schemeRefreshTokenScheme(config, db) {
credentials.client = await client.getClientById(refreshToken.clientId);
if (!credentials.client || !credentials.client.publicClient) {
return h.unauthenticated(new AppError.notPublicClient());
return h.unauthenticated(AppError.notPublicClient());
}
const devices = await db.devices(credentials.uid);

View File

@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Boom = require('@hapi/boom');
const AppError = require('../../error');
const { AppError } = require('@fxa/accounts/errors');
const crypto = require('crypto');
const constantTimeCompare = (subject, object) => {

View File

@@ -4,7 +4,7 @@
'use strict';
const AppError = require('../../error');
const { AppError } = require('@fxa/accounts/errors');
const authMethods = require('../../authMethods');
const { parseAuthorizationHeader } = require('./hawk-fxa-token');

View File

@@ -6,7 +6,7 @@ import crypto from 'crypto';
import { ConfigType } from '../../config';
import { AuthLogger, AuthRequest } from '../types';
import { Container } from 'typedi';
import AppError from '../error';
import { AppError } from '@fxa/accounts/errors';
import isA from 'joi';
import validators from './validators';
import { StatsD } from 'hot-shots';
@@ -28,7 +28,12 @@ export class CMSHandler {
this.config = config;
this.statsd = statsD;
this.log = log;
this.localization = new CMSLocalization(log, config, this.cmsManager, this.statsd);
this.localization = new CMSLocalization(
log,
config,
this.cmsManager,
this.statsd
);
}
ensureCmsManager() {
@@ -109,7 +114,7 @@ export class CMSHandler {
this.log.info('cms.getLocalizedConfig.noBaseConfig', {
clientId,
entrypoint,
locale
locale,
});
return {};
}
@@ -120,33 +125,39 @@ export class CMSHandler {
clientId,
entrypoint,
locale,
reason: locale === 'en' ? 'default-locale' : 'localization-disabled'
reason: locale === 'en' ? 'default-locale' : 'localization-disabled',
});
return baseConfig;
}
// 4. Try to fetch localized FTL content with fallback logic
const ftlContent = await this.localization.fetchLocalizedFtlWithFallback(locale);
const ftlContent =
await this.localization.fetchLocalizedFtlWithFallback(locale);
// 5. If no localized content, return base config
if (!ftlContent) {
this.log.info('cms.getLocalizedConfig.fallbackToBase', {
clientId,
entrypoint,
locale
locale,
});
this.statsd.increment('cms.getLocalizedConfig.fallback');
return baseConfig;
}
// 6. Merge base config with localized data
const localizedConfig = await this.localization.mergeConfigs(baseConfig, ftlContent, clientId, entrypoint);
const localizedConfig = await this.localization.mergeConfigs(
baseConfig,
ftlContent,
clientId,
entrypoint
);
this.log.info('cms.getLocalizedConfig.success', {
clientId,
entrypoint,
locale,
ftlContentLength: ftlContent.length
ftlContentLength: ftlContent.length,
});
this.statsd.increment('cms.getLocalizedConfig.success');
@@ -157,7 +168,7 @@ export class CMSHandler {
clientId,
entrypoint,
locale,
error: error.message
error: error.message,
});
this.statsd.increment('cms.getLocalizedConfig.error');
@@ -169,7 +180,7 @@ export class CMSHandler {
clientId,
entrypoint,
locale,
error: fallbackError.message
error: fallbackError.message,
});
return {};
}
@@ -223,7 +234,11 @@ export class CMSHandler {
// Only create PRs when entries are actually published
const allowedEvents = ['entry.publish'];
if (!eventType || !allowedEvents.includes(eventType) || webhookPayload.model !== 'relying-party') {
if (
!eventType ||
!allowedEvents.includes(eventType) ||
webhookPayload.model !== 'relying-party'
) {
this.log.info('cms.strapiWebhook.skipped', {
eventType,
reason: 'Event not in allowed list or not relying-party model',
@@ -251,7 +266,8 @@ export class CMSHandler {
await this.localization.validateGitHubConfig();
// Generate FTL content for all entries
const ftlContent = this.localization.generateFtlContentFromEntries(allEntries);
const ftlContent =
this.localization.generateFtlContentFromEntries(allEntries);
// Check for existing PR
const existingPr = await this.localization.findExistingPR(
@@ -408,7 +424,7 @@ export const cmsRoutes = (
},
handler: async (request: AuthRequest) =>
cmsHandler.handleCacheInvalidationWebhook(request),
}
},
];
};

View File

@@ -4,7 +4,7 @@
'use strict';
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
const getVersion = require('../version').getVersion;

View File

@@ -8,7 +8,7 @@ const { URL } = require('url');
const Ajv = require('ajv');
const ajv = new Ajv();
const hex = (v) => (Buffer.isBuffer(v) ? v.toString('hex') : v);
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
const fs = require('fs');
const isA = require('joi');
const path = require('path');
@@ -249,7 +249,7 @@ module.exports = (
config.oauth.deviceCommandsEnabled === false &&
credentials.refreshTokenId
) {
throw new error.featureNotEnabled();
throw error.featureNotEnabled();
}
if (!deviceId) {
@@ -262,7 +262,7 @@ module.exports = (
uaOSVersion: credentials.uaOSVersion,
});
throw new error.unknownDevice();
throw error.unknownDevice();
}
const response = await pushbox.retrieve(uid, deviceId, limit, index);
@@ -329,7 +329,7 @@ module.exports = (
config.oauth.deviceCommandsEnabled === false &&
credentials.refreshTokenId
) {
throw new error.featureNotEnabled();
throw error.featureNotEnabled();
}
await customs.checkAuthenticated(
@@ -606,7 +606,7 @@ module.exports = (
config.oauth.deviceCommandsEnabled === false &&
credentials.refreshTokenId
) {
throw new error.featureNotEnabled();
throw error.featureNotEnabled();
}
// If this request is using a session token we bump the last access time

View File

@@ -6,7 +6,7 @@
const butil = require('../crypto/butil');
const emailUtils = require('./utils/email');
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
const isA = require('joi');
const random = require('../crypto/random');
const Sentry = require('@sentry/node');

View File

@@ -89,6 +89,10 @@ module.exports = function (
statsd,
glean
);
const oauthRoutes = oauth.map((route) => ({
path: route.path,
config: route.config || {},
}));
const devicesSessions = require('./devices-and-sessions')(
log,
db,
@@ -305,6 +309,7 @@ module.exports = function (
r.path = basePath + r.path;
});
const allRoutes = defaults.concat(idp, v1Routes);
allRoutes.oauthRoutes = oauthRoutes;
allRoutes.forEach((r) => {
// Default auth.payload to 'optional' for all authenticated routes.

View File

@@ -17,7 +17,7 @@ import {
import THIRD_PARTY_AUTH_DOCS from '../../docs/swagger/third-party-auth-api';
import isA from 'joi';
import DESCRIPTION from '../../docs/swagger/shared/descriptions';
import error from '../error';
import { AppError as error } from '@fxa/accounts/errors';
import { schema as METRICS_CONTEXT_SCHEMA } from '../metrics/context';
import {
getGooglePublicKey,

View File

@@ -13,7 +13,7 @@ import { AuthRequest, SessionTokenAuthCredential } from '../types';
import { recordSecurityEvent } from './utils/security-event';
import { ConfigType } from '../../config';
import { OtpUtils } from './utils/otp';
import * as AppError from '../error';
import { AppError } from '@fxa/accounts/errors';
/** Customs interface for mfa specific operations. */
interface Customs {

View File

@@ -7,7 +7,7 @@
const MISC_DOCS = require('../../docs/swagger/misc-api').default;
const validators = require('./validators');
const ScopeSet = require('fxa-shared/oauth/scopes').scopeSetHelpers;
const AppError = require('../../lib/error');
const { AppError } = require('@fxa/accounts/errors');
const Joi = require('joi');
const { OAUTH_SCOPE_NEWSLETTERS } = require('fxa-shared/oauth/constants');

View File

@@ -5,8 +5,8 @@
const hex = (v) => (Buffer.isBuffer(v) ? v.toString('hex') : v);
const Joi = require('joi');
const OauthError = require('../../oauth/error');
const AuthError = require('../../error');
const { OauthError } = require('@fxa/accounts/errors');
const { AppError: AuthError } = require('@fxa/accounts/errors');
const validators = require('../../oauth/validators');
const { validateRequestedGrant, generateTokens } = require('../../oauth/grant');
const { makeAssertionJWT } = require('../../oauth/util');

View File

@@ -4,7 +4,7 @@
const Joi = require('joi');
const AppError = require('../../../oauth/error');
const { AppError } = require('@fxa/accounts/errors');
const validators = require('../../../oauth/validators');
const DESCRIPTION =
require('../../../../docs/swagger/shared/descriptions').default;

View File

@@ -5,8 +5,8 @@
const crypto = require('crypto');
const Joi = require('joi');
const OauthError = require('../../oauth/error');
const AuthError = require('../../error');
const { OauthError } = require('@fxa/accounts/errors');
const { AppError: AuthError } = require('@fxa/accounts/errors');
const encrypt = require('fxa-shared/auth/encrypt');
const validators = require('../../oauth/validators');
const { getTokenId } = require('../../oauth/token');

View File

@@ -5,7 +5,7 @@
/*jshint camelcase: false*/
const Joi = require('joi');
const validators = require('../../oauth/validators');
const AppError = require('../../oauth/error');
const { AppError } = require('@fxa/accounts/errors');
const { getTokenId } = require('../../oauth/token');
const OAUTH_SERVER_DOCS =
require('../../../docs/swagger/oauth-server-api').default;
@@ -79,7 +79,7 @@ module.exports = ({ oauthDB }) => ({
// in the future other clients should be able to use it
// by providing client_secret in the Authentication header
if (!client || !client.publicClient) {
throw new AppError.notPublicClient();
throw AppError.oauthNotPublicClient();
}
}
}

View File

@@ -3,8 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Joi = require('joi');
const OauthError = require('../../oauth/error');
const AuthError = require('../../error');
const { OauthError } = require('@fxa/accounts/errors');
const { AppError: AuthError } = require('@fxa/accounts/errors');
const config = require('../../../config').default.getProperties();
const validators = require('../../oauth/validators');
const verifyAssertion = require('../../oauth/assertion');

View File

@@ -26,8 +26,8 @@
/*jshint camelcase: false*/
const crypto = require('crypto');
const OauthError = require('../../oauth/error');
const AuthError = require('../../error');
const { OauthError } = require('@fxa/accounts/errors');
const { AppError: AuthError } = require('@fxa/accounts/errors');
const buf = (v) => (Buffer.isBuffer(v) ? v : Buffer.from(v, 'hex'));
const hex = (v) => (Buffer.isBuffer(v) ? v.toString('hex') : v);
const Joi = require('joi');

View File

@@ -14,7 +14,7 @@ import PASSWORD_DOCS from '../../docs/swagger/password-api';
import DESCRIPTION from '../../docs/swagger/shared/descriptions';
import * as butil from '../crypto/butil';
import * as random from '../crypto/random';
import * as error from '../error';
import { AppError as error } from '@fxa/accounts/errors';
import { schema as METRICS_CONTEXT_SCHEMA } from '../metrics/context';
import { gleanMetrics } from '../metrics/glean';
import * as requestHelper from '../routes/utils/request_helper';

View File

@@ -4,7 +4,7 @@
'use strict';
const errors = require('../error');
const { AppError: errors } = require('@fxa/accounts/errors');
const isA = require('joi');
const validators = require('./validators');
const { Container } = require('typedi');

View File

@@ -8,8 +8,7 @@ const RECOVERY_KEY_DOCS =
require('../../docs/swagger/recovery-key-api').default;
const DESCRIPTION = require('../../docs/swagger/shared/descriptions').default;
const AppError = require('../error');
const errors = require('../error');
const { AppError: errors } = require('@fxa/accounts/errors');
const { recordSecurityEvent } = require('./utils/security-event');
const validators = require('./validators');
const isA = require('joi');
@@ -149,7 +148,7 @@ module.exports = (
// if we are not explicitly requesting a key change
// but an enabled key exists, throw an error
} else if (enabledKey.exists && replaceKey !== true) {
throw AppError.recoveryKeyExists();
throw errors.recoveryKeyExists();
} else {
// if no key is enabled, attempt to create a new key
await db.createRecoveryKey(

View File

@@ -27,7 +27,7 @@ import {
SessionTokenAuthCredential,
} from '../types';
import { E164_NUMBER } from './validators';
import AppError from '../error';
import { AppError } from '@fxa/accounts/errors';
import Localizer from '../l10n';
import NodeRendererBindings from '../senders/renderer/bindings-node';
import { AccountEventsManager } from '../account-events';

View File

@@ -4,7 +4,7 @@
'use strict';
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
const isA = require('joi');
const requestHelper = require('../routes/utils/request_helper');
const METRICS_CONTEXT_SCHEMA = require('../metrics/context').schema;

View File

@@ -8,7 +8,7 @@ import { OAUTH_SCOPE_SUBSCRIPTIONS_IAP } from 'fxa-shared/oauth/constants';
import { Container } from 'typedi';
import SUBSCRIPTIONS_DOCS from '../../../docs/swagger/subscriptions-api';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { CapabilityService } from '../../payments/capability';
import { AppleIAP } from '../../payments/iap/apple-app-store/apple-iap';
import { PurchaseUpdateError } from '../../payments/iap/apple-app-store/types/errors';

View File

@@ -6,7 +6,7 @@ import isA from 'joi';
import { OAUTH_SCOPE_SUBSCRIPTIONS_IAP } from 'fxa-shared/oauth/constants';
import { Container } from 'typedi';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { CapabilityService } from '../../payments/capability';
import { PlayBilling } from '../../payments/iap/google-play/play-billing';
import { PurchaseUpdateError } from '../../payments/iap/google-play/types/errors';

View File

@@ -9,7 +9,7 @@ import { Container } from 'typedi';
import SUBSCRIPTIONS_DOCS from '../../../docs/swagger/subscriptions-api';
import { AppStoreSubscriptions } from '../../../lib/payments/iap/apple-app-store/subscriptions';
import { PlaySubscriptions } from '../../../lib/payments/iap/google-play/subscriptions';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import {
appStoreSubscriptionPurchaseToAppStoreSubscriptionDTO,
playStoreSubscriptionPurchaseToPlayStoreSubscriptionDTO,

View File

@@ -11,7 +11,7 @@ import {
import Stripe from 'stripe';
import { ConfigType } from '../../../config';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import {
IpnMerchPmtType,
isIpnMerchPmt,

View File

@@ -14,7 +14,7 @@ import { Stripe } from 'stripe';
import Container from 'typedi';
import { ConfigType } from '../../../config';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { PayPalHelper } from '../../payments/paypal/helper';
import { StripeHelper } from '../../payments/stripe';
import { reportSentryError } from '../../sentry';

View File

@@ -6,7 +6,7 @@ import isA from 'joi';
import { Container } from 'typedi';
import SUBSCRIPTIONS_DOCS from '../../../docs/swagger/subscriptions-api';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { CapabilityService } from '../../payments/capability';
import { PlayBilling } from '../../payments/iap/google-play/play-billing';
import { DeveloperNotification } from '../../payments/iap/google-play/types';

View File

@@ -21,7 +21,7 @@ import {
reportSentryMessage,
reportValidationError,
} from '../../../lib/sentry';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { PayPalHelper, RefusedError } from '../../payments/paypal';
import { RefundType } from '@fxa/payments/paypal';
import {
@@ -949,7 +949,7 @@ export class StripeWebhookHandler extends StripeHandler {
{
acceptLanguage: account.locale,
...invoiceDetails,
email: account.primaryEmail
email: account.primaryEmail,
}
);
await this.stripeHelper.updateEmailSent(invoice, 'paymentFailed');
@@ -971,7 +971,7 @@ export class StripeWebhookHandler extends StripeHandler {
{
acceptLanguage: account.locale,
...invoiceDetails,
email: account.primaryEmail
email: account.primaryEmail,
},
];
switch (invoice.billing_reason) {
@@ -1085,7 +1085,7 @@ export class StripeWebhookHandler extends StripeHandler {
{
acceptLanguage: account.locale,
...invoiceDetails,
email: account.primaryEmail
email: account.primaryEmail,
}
);
} else if (!subscription.cancel_at_period_end) {
@@ -1100,7 +1100,7 @@ export class StripeWebhookHandler extends StripeHandler {
...invoiceDetails,
showOutstandingBalance,
cancelAtEnd: subscription.cancel_at_period_end,
email: account.primaryEmail
email: account.primaryEmail,
}
);
}

View File

@@ -7,10 +7,7 @@ import assertNotNull from 'assert';
import isA from 'joi';
import * as Sentry from '@sentry/node';
import { SeverityLevel } from '@sentry/core';
import {
CustomerError,
PromotionCodeManager,
} from '@fxa/payments/customer';
import { CustomerError, PromotionCodeManager } from '@fxa/payments/customer';
import { getAccountCustomerByUid } from 'fxa-shared/db/models/auth';
import {
AbbrevPlan,
@@ -34,7 +31,7 @@ import { Logger } from 'mozlog';
import { Stripe } from 'stripe';
import { ConfigType } from '../../../config';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import {
COUNTRIES_LONG_NAME_TO_SHORT_NAME_MAP,
StripeHelper,
@@ -235,7 +232,7 @@ export class StripeHandler {
subscriptionId
);
if (!subscription) {
throw error.unknownSubscription();
throw error.unknownSubscription(subscriptionId);
}
const currentPlan = singlePlan(subscription);
@@ -268,8 +265,9 @@ export class StripeHandler {
const { amount: planAmount, currency: planCurrency } =
await this.stripeHelper.findAbbrevPlanById(planId);
assertNotNull(planAmount, 'planAmount');
if (customer && customer.currency !== planCurrency) {
throw error.currencyCurrencyMismatch(customer.currency, planCurrency);
const customerCurrency = customer?.currency || undefined;
if (customerCurrency && customerCurrency !== planCurrency) {
throw error.currencyCurrencyMismatch(customerCurrency, planCurrency);
}
// Update the plan
@@ -359,7 +357,7 @@ export class StripeHandler {
const plans = await this.stripeHelper.allAbbrevPlans();
const planForProduct = plans.find((plan) => plan.product_id === productId);
if (!planForProduct) {
throw error.unknownSubscriptionPlan();
throw error.unknownSubscriptionPlan(productId);
}
this.log.info('subscriptions.getProductName', {
productId,

View File

@@ -7,11 +7,10 @@ import zendesk from 'node-zendesk';
import pRetry from 'p-retry';
import { ConfigType } from '../../../config';
import error from '../../error';
import { AppError } from '@fxa/accounts/errors';
import { AuthLogger, AuthRequest } from '../../types';
import { handleAuth } from './utils';
import { email } from '../validators';
import AppError from '../../error';
const MISC_DOCS = require('../../../docs/swagger/misc-api').default;
@@ -198,7 +197,7 @@ export const supportRoutes = (
}
return { success: true, ticket: createRequest.id };
} catch (err) {
throw error.backendServiceFailure(
throw AppError.backendServiceFailure(
'zendesk',
operation,
{ uid, email },

View File

@@ -5,7 +5,7 @@ import { OAUTH_SCOPE_SUBSCRIPTIONS } from 'fxa-shared/oauth/constants';
import ScopeSet from 'fxa-shared/oauth/scopes';
import { Logger } from 'mozlog';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { AuthRequest, TaxAddress } from '../../types';
/**

View File

@@ -4,7 +4,7 @@
'use strict';
const errors = require('../error');
const { AppError: errors } = require('@fxa/accounts/errors');
const validators = require('./validators');
const isA = require('joi');
const otplib = require('otplib');

View File

@@ -1,6 +1,6 @@
import { StripeHelper } from '../../payments/stripe';
import { AuthLogger, AuthRequest } from '../../types';
import error from '../../error';
import { AppError as error } from '@fxa/accounts/errors';
import { reportSentryError } from '../../../lib/sentry';
import { RelyingPartiesQuery } from '../../../../../libs/shared/cms/src/__generated__/graphql';
import { RelyingPartyConfigurationManager } from '@fxa/shared/cms';

View File

@@ -4,7 +4,7 @@
'use strict';
const error = require('../../error');
const { AppError: error } = require('@fxa/accounts/errors');
const BOUNCE_ERRORS = new Set([
error.ERRNO.BOUNCE_COMPLAINT,

View File

@@ -4,7 +4,7 @@
import { authenticator } from 'otplib';
import { StatsD } from 'hot-shots';
import errors from '../../error';
import { AppError as errors } from '@fxa/accounts/errors';
export interface OtpDb {
totpToken(uid: string): Promise<{ verified: boolean; enabled: boolean }>;

View File

@@ -8,7 +8,7 @@ const emailUtils = require('./email');
const isA = require('joi');
const validators = require('../validators');
const butil = require('../../crypto/butil');
const error = require('../../error');
const { AppError: error } = require('@fxa/accounts/errors');
const { Container } = require('typedi');
const { AccountEventsManager } = require('../../account-events');
const { emailsMatch } = require('fxa-shared').email.helpers;

View File

@@ -7,7 +7,7 @@
const Hoek = require('@hapi/hoek');
const Sentry = require('@sentry/node');
const verror = require('verror');
const { ignoreErrors } = require('./error');
const { ignoreErrors } = require('@fxa/accounts/errors');
const {
formatMetadataValidationErrorMessage,

View File

@@ -173,6 +173,7 @@ async function create(log, error, config, routes, db, statsd, glean, customs) {
}
const server = new Hapi.Server(serverOptions);
const oauthRoutes = routes.oauthRoutes || [];
server.validator(require('joi'));
server.ext('onRequest', (request, h) => {
@@ -337,7 +338,7 @@ async function create(log, error, config, routes, db, statsd, glean, customs) {
translateStripeErrors(response);
}
response = error.translate(request, response);
response = error.translate(request, response, oauthRoutes);
if (config.env !== 'prod') {
response.backtrace(request.app.traced);
}

View File

@@ -26,7 +26,7 @@
const butil = require('../crypto/butil');
const crypto = require('crypto');
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
const hkdf = require('../crypto/hkdf');
const HASH_ALGORITHM = 'sha256';

View File

@@ -4,7 +4,7 @@
'use strict';
const error = require('../error');
const { AppError: error } = require('@fxa/accounts/errors');
module.exports = (log, config) => {
config = config || {};

View File

@@ -6,7 +6,7 @@ import createClient from 'openapi-fetch';
import { components, paths } from './identity-schema';
import { DB } from '../../lib/db';
import { ERRNO } from '../../lib/error';
import { ERRNO } from '@fxa/accounts/errors';
import * as pbkdf2 from '../../lib/crypto/pbkdf2';
import PasswordFn from '../../lib/crypto/password';
import hkdf from '../../lib/crypto/hkdf';

View File

@@ -20,7 +20,7 @@ const LIB_DIR = `${ROOT_DIR}/lib`;
const config = require(`${ROOT_DIR}/config`).default.getProperties();
const error = require(`${LIB_DIR}/error`);
const { AppError: error } = require('@fxa/accounts/errors');
const log = require(`${LIB_DIR}/log`)(config.log);
const jwt = require(`${LIB_DIR}/oauth/jwt`);
const verificationReminders = require(`${LIB_DIR}/verification-reminders`)(
@@ -30,8 +30,9 @@ const verificationReminders = require(`${LIB_DIR}/verification-reminders`)(
const Sentry = require('@sentry/node');
const cadReminders = require(`${LIB_DIR}/cad-reminders`)(config, log);
const subscriptionAccountReminders =
require(`${LIB_DIR}/subscription-account-reminders`)(log, config);
const subscriptionAccountReminders = require(
`${LIB_DIR}/subscription-account-reminders`
)(log, config);
Sentry.init({});
const checkInId = Sentry.captureCheckIn({

View File

@@ -9,7 +9,7 @@ const { default: Container } = require('typedi');
const { AppConfig, AuthLogger } = require('../../lib/types');
const mocks = require('../mocks');
const uuid = require('uuid');
const error = require('../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const {
AppleIAP,
} = require('../../lib/payments/iap/apple-app-store/apple-iap');

View File

@@ -8,7 +8,7 @@ const sinon = require('sinon');
const assert = { ...sinon.assert, ...require('chai').assert };
const mocks = require('../mocks');
const error = require('../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const authMethods = require('../../lib/authMethods');

View File

@@ -9,7 +9,7 @@ const ROOT_DIR = '../..';
const assert = require('assert');
const config = require(`${ROOT_DIR}/config`).default.getProperties();
const createBounces = require(`${ROOT_DIR}/lib/bounces`);
const error = require(`${ROOT_DIR}/lib/error`);
const { AppError: error } = require('@fxa/accounts/errors');
const sinon = require('sinon');
const EMAIL = Math.random() + '@example.test';

View File

@@ -7,7 +7,7 @@
const sinon = require('sinon');
const assert = { ...sinon.assert, ...require('chai').assert };
const mocks = require('../mocks');
const error = require(`../../lib/error.js`);
const { AppError: error } = require('@fxa/accounts/errors');
const nock = require('nock');
const CUSTOMS_URL_REAL = 'http://localhost:7000';

View File

@@ -9,7 +9,7 @@ const sinon = require('sinon');
const proxyquire = require('proxyquire');
const crypto = require('crypto');
const mocks = require('../mocks');
const error = require('../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const uuid = require('uuid');
describe('lib/devices:', () => {

View File

@@ -8,7 +8,7 @@ const ROOT_DIR = '../../..';
const { assert } = require('chai');
const bounces = require(`${ROOT_DIR}/lib/email/bounces`);
const error = require(`${ROOT_DIR}/lib/error`);
const { AppError: error } = require('@fxa/accounts/errors');
const { EventEmitter } = require('events');
const { mockLog } = require('../../mocks');
const sinon = require('sinon');
@@ -428,7 +428,7 @@ describe('bounce messages', () => {
it('should log errors when deleting the email record', () => {
mockDB.deleteAccount = sinon.spy(() =>
Promise.reject(new error.unknownAccount('test@example.com'))
Promise.reject(error.unknownAccount('test@example.com'))
);
const mockMsg = mockMessage({
bounce: {
@@ -461,7 +461,7 @@ describe('bounce messages', () => {
mockDB.accountRecord = sinon.spy((email) => {
// Lookup only succeeds when using original, unquoted email addr.
if (email !== 'test.@example.com') {
return Promise.reject(new error.unknownAccount(email));
return Promise.reject(error.unknownAccount(email));
}
return Promise.resolve({
createdAt: Date.now(),
@@ -503,7 +503,7 @@ describe('bounce messages', () => {
mockDB.accountRecord = sinon.spy((email) => {
// Lookup only succeeds when using original, unquoted email addr.
if (email !== 'test..me@example.com') {
return Promise.reject(new error.unknownAccount(email));
return Promise.reject(error.unknownAccount(email));
}
return Promise.resolve({
createdAt: Date.now(),
@@ -544,7 +544,7 @@ describe('bounce messages', () => {
it('should log a warning if it receives an unparseable email address', () => {
mockDB.accountRecord = sinon.spy(() =>
Promise.reject(new error.unknownAccount())
Promise.reject(error.unknownAccount())
);
return mockedBounces(log, mockDB)
.handleBounce(

View File

@@ -7,7 +7,7 @@
const ROOT_DIR = '../../..';
const { assert } = require('chai');
const error = require(`${ROOT_DIR}/lib/error`);
const { AppError: error } = require('@fxa/accounts/errors');
const { mockLog } = require('../../mocks');
const notifications = require(`${ROOT_DIR}/lib/email/notifications`);
const sinon = require('sinon');

View File

@@ -6,15 +6,21 @@
const { assert } = require('chai');
const verror = require('verror');
const AppError = require('../../lib/error');
const OauthError = require('../../lib/oauth/error');
const { AppError, OauthError } = require('@fxa/accounts/errors');
const mockOauthRoutes = [
{
path: '/token',
config: { cors: true },
},
];
describe('AppErrors', () => {
it('exported functions exist', () => {
assert.equal(typeof AppError, 'function');
assert.equal(AppError.length, 4);
assert.equal(typeof AppError.translate, 'function');
assert.lengthOf(AppError.translate, 2);
assert.lengthOf(AppError.translate, 3);
assert.equal(typeof AppError.invalidRequestParameter, 'function');
assert.equal(AppError.invalidRequestParameter.length, 1);
assert.equal(typeof AppError.missingRequestParameter, 'function');
@@ -27,7 +33,8 @@ describe('AppErrors', () => {
assert.equal(oauthError.errno, 104);
const result = AppError.translate(
{ route: { path: '/v1/oauth/token' } },
oauthError
oauthError,
mockOauthRoutes
);
assert.ok(result instanceof AppError, 'instanceof AppError');
assert.equal(result.errno, 110);
@@ -38,7 +45,8 @@ describe('AppErrors', () => {
assert.equal(oauthError.errno, 104);
const result = AppError.translate(
{ route: { path: '/v1/token' } },
oauthError
oauthError,
mockOauthRoutes
);
assert.ok(result instanceof OauthError, 'instanceof OauthError');
assert.equal(result.errno, 104);

View File

@@ -5,7 +5,7 @@
import proxyquire from 'proxyquire';
import sinon, { SinonStub } from 'sinon';
import { assert } from 'chai';
import AppError from '../../../lib/error';
import { AppError } from '@fxa/accounts/errors';
import mocks from '../../mocks';
import { GleanMetricsType } from '../../../lib/metrics/glean';
import { AuthRequest } from '../../../lib/types';
@@ -192,12 +192,10 @@ const gleanProxy = proxyquire('../../../lib/metrics/glean', {
recordTwoStepAuthPhoneReplaceSuccess,
recordTwoStepAuthPhoneReplaceFailure:
recordTwoStepAuthPhoneReplaceFailure,
recordLoginConfirmSkipForKnownIp:
recordLoginConfirmSkipForKnownIp,
recordLoginConfirmSkipForNewAccount:
recordLoginConfirmSkipForNewAccount,
recordLoginConfirmSkipForKnownIp: recordLoginConfirmSkipForKnownIp,
recordLoginConfirmSkipForNewAccount: recordLoginConfirmSkipForNewAccount,
recordLoginConfirmSkipForKnownDevice:
recordLoginConfirmSkipForKnownDevice,
recordLoginConfirmSkipForKnownDevice,
}),
},
});

View File

@@ -26,7 +26,7 @@ const {
} = require('../../../../lib/payments/configuration/manager');
const { setupFirestore } = require('../../../../lib/firestore-db');
const { randomUUID } = require('crypto');
const errors = require('../../../../lib/error');
const { AppError: errors } = require('@fxa/accounts/errors');
const {
ProductConfig,
} = require('fxa-shared/subscriptions/configuration/product');

View File

@@ -12,7 +12,7 @@ const { PayPalHelper } = require('../../../lib/payments/paypal/helper');
const { mockLog } = require('../../mocks');
const { PaypalProcessor } = require('../../../lib/payments/paypal/processor');
const { StripeHelper } = require('../../../lib/payments/stripe');
const error = require('../../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const paidInvoice = require('./fixtures/stripe/invoice_paid.json');
const unpaidInvoice = require('./fixtures/stripe/invoice_open.json');
const customer1 = require('./fixtures/stripe/customer1.json');

View File

@@ -19,7 +19,7 @@ const {
} = require('@fxa/payments/paypal');
const { PayPalHelper, RefusedError } = require('../../../lib/payments/paypal');
const { mockLog } = require('../../mocks');
const error = require('../../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const successfulSetExpressCheckoutResponse = require('./fixtures/paypal/set_express_checkout_success.json');
const successfulDoReferenceTransactionResponse = require('./fixtures/paypal/do_reference_transaction_success.json');
const successfulRefundTransactionResponse = require('./fixtures/paypal/refund_transaction_success.json');
@@ -736,7 +736,8 @@ describe('PayPalHelper', () => {
...validInvoice,
amount_paid: 1000,
};
const expectedErrorMessage = 'Partial refunds must be less than the amount paid on the invoice';
const expectedErrorMessage =
'Partial refunds must be less than the amount paid on the invoice';
mockStripeHelper.getInvoicePaypalTransactionId =
sinon.fake.returns('123');
mockStripeHelper.getInvoicePaypalRefundTransactionId =
@@ -909,9 +910,8 @@ describe('PayPalHelper', () => {
it('returns false with no billing agreement found', async () => {
mockStripeHelper.getCustomerPaypalAgreement =
sinon.fake.returns(undefined);
const result = await paypalHelper.conditionallyRemoveBillingAgreement(
mockCustomer
);
const result =
await paypalHelper.conditionallyRemoveBillingAgreement(mockCustomer);
assert.isFalse(result);
});
@@ -921,9 +921,8 @@ describe('PayPalHelper', () => {
mockCustomer.subscriptions = {
data: [{ status: 'active', collection_method: 'send_invoice' }],
};
const result = await paypalHelper.conditionallyRemoveBillingAgreement(
mockCustomer
);
const result =
await paypalHelper.conditionallyRemoveBillingAgreement(mockCustomer);
assert.isFalse(result);
});
@@ -933,9 +932,8 @@ describe('PayPalHelper', () => {
mockCustomer.subscriptions = { data: [] };
paypalHelper.cancelBillingAgreement = sinon.fake.resolves({});
mockStripeHelper.removeCustomerPaypalAgreement = sinon.fake.resolves({});
const result = await paypalHelper.conditionallyRemoveBillingAgreement(
mockCustomer
);
const result =
await paypalHelper.conditionallyRemoveBillingAgreement(mockCustomer);
assert.isTrue(result);
sinon.assert.calledOnceWithExactly(
mockStripeHelper.getCustomerPaypalAgreement,

View File

@@ -12,7 +12,7 @@ const Chance = require('chance');
const { setupAuthDatabase } = require('fxa-shared/db');
const Knex = require('knex');
const { mockLog, asyncIterable } = require('../../mocks');
const error = require('../../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const stripeError = require('stripe').Stripe.errors;
const uuidv4 = require('uuid').v4;
const moment = require('moment');
@@ -125,7 +125,6 @@ const {
newFirestoreStripeError,
StripeFirestoreMultiError,
} = require('../../../lib/payments/stripe-firestore');
const AppError = require('../../../lib/error');
const mockConfig = {
authFirestore: {
@@ -2034,7 +2033,7 @@ describe('#integration - StripeHelper', () => {
...expectedTemplate,
valid: false,
};
const err = new AppError('previewInvoiceFailed');
const err = new error('previewInvoiceFailed');
sandbox.stub(stripeHelper, 'previewInvoice').rejects(err);
sandbox.stub(stripeHelper, 'retrievePromotionCodeForPlan').resolves({
@@ -4276,10 +4275,10 @@ describe('#integration - StripeHelper', () => {
} catch (err) {
thrown = err;
}
assert.isObject(thrown);
assert.instanceOf(thrown, Error);
assert.equal(thrown.message, 'System unavailable, try again soon');
assert.equal(
thrown.cause().message,
thrown.jse_cause?.message,
'Stripe Customer: cus_new has mismatched uid in metadata.'
);
});
@@ -4589,7 +4588,7 @@ describe('#integration - StripeHelper', () => {
thrown = err;
}
assert(stripeHelper.stripe.plans.list.calledOnce);
assert.isObject(thrown);
assert.instanceOf(thrown, Error);
assert.equal(thrown.errno, error.ERRNO.UNKNOWN_SUBSCRIPTION_PLAN);
});
});

View File

@@ -10,7 +10,7 @@ const sandbox = sinon.createSandbox();
const { pushboxApi } = require('../../lib/pushbox');
const pushboxDbModule = require('../../lib/pushbox/db');
const error = require('../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const { mockLog } = require('../mocks');
let mockStatsD;

View File

@@ -17,7 +17,7 @@ const {
const uuid = require('uuid');
const crypto = require('crypto');
const error = require('../../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const log = require('../../../lib/log');
const otplib = require('otplib');
const { Container } = require('typedi');
@@ -946,7 +946,7 @@ describe('/account/create', () => {
wrapWrapKb: 'wibble',
},
{
emailRecord: new error.unknownAccount(),
emailRecord: error.unknownAccount(),
}
);
const mockMailer = mocks.mockMailer();
@@ -1571,7 +1571,7 @@ describe('/account/stub', () => {
wrapWrapKb: 'wibble',
},
{
emailRecord: new error.unknownAccount(),
emailRecord: error.unknownAccount(),
}
);
const mockMailer = mocks.mockMailer();
@@ -1712,7 +1712,7 @@ describe('/account/status', () => {
},
{
...(shouldError && {
emailRecord: new error.unknownAccount(),
emailRecord: error.unknownAccount(),
}),
}
);
@@ -1891,7 +1891,7 @@ describe('/account/finish_setup', () => {
verifierSetAt: options.verifierSetAt,
},
{
emailRecord: new error.unknownAccount(),
emailRecord: error.unknownAccount(),
}
);
const mockMailer = mocks.mockMailer();
@@ -2043,7 +2043,7 @@ describe('/account/set_password', () => {
verifierSetAt: options.verifierSetAt,
},
{
emailRecord: new error.unknownAccount(),
emailRecord: error.unknownAccount(),
}
);
const mockMailer = mocks.mockMailer();
@@ -4171,10 +4171,8 @@ describe('/account/login', () => {
});
it('unknown account', () => {
mockDB.accountRecord = () =>
Promise.reject(new error.unknownAccount());
mockDB.emailRecord = () =>
Promise.reject(new error.unknownAccount());
mockDB.accountRecord = () => Promise.reject(error.unknownAccount());
mockDB.emailRecord = () => Promise.reject(error.unknownAccount());
return runTest(route, mockRequestWithUnblockCode).then(
() => assert(false),
(err) => {

View File

@@ -9,7 +9,7 @@ const assert = { ...sinon.assert, ...require('chai').assert };
const crypto = require('crypto');
const getRoute = require('../../routes_helpers').getRoute;
const mocks = require('../../mocks');
const error = require('../../../lib/error');
const { AppError: error } = require('@fxa/accounts/errors');
const proxyquire = require('proxyquire');
const uuid = require('uuid');

Some files were not shown because too many files have changed in this diff Show More