From 946a21ccfd0f135fb1e7559e83ac11485c03d8fb Mon Sep 17 00:00:00 2001 From: Sid Vishnoi <8426945+sidvishnoi@users.noreply.github.com> Date: Mon, 15 Jul 2024 19:23:38 +0530 Subject: [PATCH 1/2] fix(background/paymentSession): handle Invalid Token error --- src/background/services/paymentSession.ts | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/background/services/paymentSession.ts b/src/background/services/paymentSession.ts index c8a3916e..87469efd 100644 --- a/src/background/services/paymentSession.ts +++ b/src/background/services/paymentSession.ts @@ -157,13 +157,10 @@ export class PaymentSession { } catch (e) { if (this.isKeyRevokedError(e)) { this.events.emit('open_payments.key_revoked') + } else if (this.isTokenExpiredError(e)) { + await this.openPaymentsService.rotateToken() + continue } else if (e instanceof OpenPaymentsClientError) { - // Status code 403 -> expired access token - if (e.status === 403) { - await this.openPaymentsService.rotateToken() - continue - } - // We need better error handling. if (e.status === 400) { await this.setIncomingPaymentUrl() @@ -301,11 +298,8 @@ export class PaymentSession { } catch (e) { if (this.isKeyRevokedError(e)) { this.events.emit('open_payments.key_revoked') - } else if (e instanceof OpenPaymentsClientError) { - // Status code 403 -> expired access token - if (e.status === 403) { - await this.openPaymentsService.rotateToken() - } + } else if (this.isTokenExpiredError(e)) { + await this.openPaymentsService.rotateToken() } } finally { if (outgoingPayment) { @@ -355,4 +349,15 @@ export class PaymentSession { } return false } + + private isTokenExpiredError(error: any) { + if (!(error instanceof OpenPaymentsClientError)) return false + return this.isTokenInvalidError(error) || this.isTokenInactiveError(error) + } + private isTokenInvalidError(error: OpenPaymentsClientError) { + return error.status === 401 && error.description === 'Invalid Token' + } + private isTokenInactiveError(error: OpenPaymentsClientError) { + return error.status === 403 && error.description === 'Inactive Token' + } } From b327ed0fef814f489553b82a754c5efb6bbd2e15 Mon Sep 17 00:00:00 2001 From: Sid Vishnoi <8426945+sidvishnoi@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:21:31 +0530 Subject: [PATCH 2/2] move error detection from paymentSession to openPayments (for reuse) --- src/background/services/openPayments.ts | 59 +++++++++++++++++------ src/background/services/paymentSession.ts | 40 +++------------ 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/src/background/services/openPayments.ts b/src/background/services/openPayments.ts index 0ccb088d..4de66979 100644 --- a/src/background/services/openPayments.ts +++ b/src/background/services/openPayments.ts @@ -311,11 +311,9 @@ export class OpenPaymentsService { walletAddress, amount: transformedAmount }).catch((err) => { - if (err instanceof OpenPaymentsClientError) { - if (err.status === 400 && err.code === 'invalid_client') { - const msg = this.t('error_connectWallet_invalidClient') - throw new Error(msg, { cause: err }) - } + if (isInvalidClientError(err)) { + const msg = this.t('error_connectWallet_invalidClient') + throw new Error(msg, { cause: err }) } throw err }) @@ -515,11 +513,9 @@ export class OpenPaymentsService { try { await this.client!.grant.cancel(grantContinuation) } catch (error) { - if (error instanceof OpenPaymentsClientError) { - if (error.status === 400 && error.code === 'invalid_client') { - // key already removed from wallet - return - } + if (isInvalidClientError(error)) { + // key already removed from wallet + return } throw error } @@ -577,11 +573,9 @@ export class OpenPaymentsService { try { await this.rotateToken() } catch (error) { - if (error instanceof OpenPaymentsClientError) { - if (error.status === 400 && error.code === 'invalid_client') { - const msg = this.t('error_connectWallet_invalidClient') - throw new Error(msg, { cause: error }) - } + if (isInvalidClientError(error)) { + const msg = this.t('error_connectWallet_invalidClient') + throw new Error(msg, { cause: error }) } throw error } @@ -610,3 +604,38 @@ export class OpenPaymentsService { this.token = accessToken } } + +const isOpenPaymentsClientError = (error: any) => + error instanceof OpenPaymentsClientError + +export const isKeyRevokedError = (error: any) => { + if (!isOpenPaymentsClientError(error)) return false + return isInvalidClientError(error) || isSignatureValidationError(error) +} + +// AUTH SERVER error +export const isInvalidClientError = (error: any) => { + if (!isOpenPaymentsClientError(error)) return false + return error.status === 400 && error.code === 'invalid_client' +} + +// RESOURCE SERVER error. Create outgoing payment and create quote can fail +// with: `Signature validation error: could not find key in list of client keys` +export const isSignatureValidationError = (error: any) => { + if (!isOpenPaymentsClientError(error)) return false + return ( + error.status === 401 && + error.description?.includes('Signature validation error') + ) +} + +export const isTokenExpiredError = (error: any) => { + if (!isOpenPaymentsClientError(error)) return false + return isTokenInvalidError(error) || isTokenInactiveError(error) +} +export const isTokenInvalidError = (error: OpenPaymentsClientError) => { + return error.status === 401 && error.description === 'Invalid Token' +} +export const isTokenInactiveError = (error: OpenPaymentsClientError) => { + return error.status === 403 && error.description === 'Inactive Token' +} diff --git a/src/background/services/paymentSession.ts b/src/background/services/paymentSession.ts index 87469efd..380199ec 100644 --- a/src/background/services/paymentSession.ts +++ b/src/background/services/paymentSession.ts @@ -8,6 +8,7 @@ import { OpenPaymentsClientError } from '@interledger/open-payments/dist/client' import { sendMonetizationEvent } from '../lib/messages' import { convert, sleep } from '@/shared/helpers' import { transformBalance } from '@/popup/lib/utils' +import { isKeyRevokedError, isTokenExpiredError } from './openPayments' import type { EventsService, OpenPaymentsService, TabState } from '.' import type { Tabs } from 'webextension-polyfill' import type { MonetizationEventDetails } from '@/shared/messages' @@ -155,9 +156,9 @@ export class PaymentSession { amount: this.amount }) } catch (e) { - if (this.isKeyRevokedError(e)) { + if (isKeyRevokedError(e)) { this.events.emit('open_payments.key_revoked') - } else if (this.isTokenExpiredError(e)) { + } else if (isTokenExpiredError(e)) { await this.openPaymentsService.rotateToken() continue } else if (e instanceof OpenPaymentsClientError) { @@ -220,7 +221,7 @@ export class PaymentSession { const incomingPayment = await this.createIncomingPayment() this.incomingPaymentUrl = incomingPayment.id } catch (error) { - if (this.isKeyRevokedError(error)) { + if (isKeyRevokedError(error)) { this.events.emit('open_payments.key_revoked') return } @@ -278,7 +279,7 @@ export class PaymentSession { async pay(amount: number) { const incomingPayment = await this.createIncomingPayment().catch( (error) => { - if (this.isKeyRevokedError(error)) { + if (isKeyRevokedError(error)) { this.events.emit('open_payments.key_revoked') return } @@ -296,9 +297,9 @@ export class PaymentSession { amount: (amount * 10 ** this.sender.assetScale).toFixed(0) }) } catch (e) { - if (this.isKeyRevokedError(e)) { + if (isKeyRevokedError(e)) { this.events.emit('open_payments.key_revoked') - } else if (this.isTokenExpiredError(e)) { + } else if (isTokenExpiredError(e)) { await this.openPaymentsService.rotateToken() } } finally { @@ -333,31 +334,4 @@ export class PaymentSession { this.amount = amount.toString() this.intervalInMs = Number((amount * BigInt(HOUR_MS)) / BigInt(this.rate)) } - - private isKeyRevokedError(error: any) { - if (error instanceof OpenPaymentsClientError) { - return ( - // - [RESOURCE SERVER] create outgoing payment and create quote fail - // with: HTTP 401 + `Signature validation error: could not find key in - // list of client keys` - // - [AUTH SERVER] create incoming payment grant fails with: HTTP 400 + - // `invalid_client` - (error.status === 400 && error.code === 'invalid_client') || - (error.status === 401 && - error.description?.includes('Signature validation error')) - ) - } - return false - } - - private isTokenExpiredError(error: any) { - if (!(error instanceof OpenPaymentsClientError)) return false - return this.isTokenInvalidError(error) || this.isTokenInactiveError(error) - } - private isTokenInvalidError(error: OpenPaymentsClientError) { - return error.status === 401 && error.description === 'Invalid Token' - } - private isTokenInactiveError(error: OpenPaymentsClientError) { - return error.status === 403 && error.description === 'Inactive Token' - } }