Skip to content
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

feat(backend): start of handling grant token rotation more gracefully #2887

Merged
merged 9 commits into from
Aug 23, 2024
19 changes: 19 additions & 0 deletions packages/backend/migrations/20240820101148_drop_unique_grants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.alterTable('grants', function (table) {
table.dropUnique(['authServerId', 'accessType', 'accessActions'])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropping the unique constraint on authServerId accessType and accessActions such that we can properly soft delete grants (see migration below)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems similar to this case? #2863 (comment)

So could we use a partial unique index (where deletedAt is not null)?

Copy link
Contributor Author

@mkurapov mkurapov Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this unique constraint is very useful to begin with, particularly because order matters in accessActions array, meaning [Create, Read] is different from [Read, Create], even though they are the same in terms of how we deal with them in code

})
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.alterTable('grants', function (table) {
table.unique(['authServerId', 'accessType', 'accessActions'])
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why not combine this migration with drop_unique_grants?

Copy link
Contributor Author

@mkurapov mkurapov Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, makes sense for this one, let me do that

* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.alterTable('grants', (table) => {
table.timestamp('deletedAt').nullable()
})
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.alterTable('grants', (table) => {
table.dropColumn('deletedAt')
})
}
4 changes: 4 additions & 0 deletions packages/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ import {
} from './open_payments/wallet_address/middleware'

import { LoggingPlugin } from './graphql/plugin'
import { GrantService } from './open_payments/grant/service'
import { AuthServerService } from './open_payments/authServer/service'
export interface AppContextData {
logger: Logger
container: AppContainer
Expand Down Expand Up @@ -232,6 +234,8 @@ export interface AppServices {
incomingPaymentService: Promise<IncomingPaymentService>
remoteIncomingPaymentService: Promise<RemoteIncomingPaymentService>
receiverService: Promise<ReceiverService>
grantService: Promise<GrantService>
authServerService: Promise<AuthServerService>
Comment on lines +237 to +238
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weren't included before, adding here so we get proper types

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

easy to miss this

streamServer: Promise<StreamServer>
webhookService: Promise<WebhookService>
quoteService: Promise<QuoteService>
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ export function initIocContainer(
container.singleton('grantService', async (deps) => {
return await createGrantService({
authServerService: await deps.use('authServerService'),
openPaymentsClient: await deps.use('openPaymentsClient'),
logger: await deps.use('logger'),
knex: await deps.use('knex')
})
Expand Down
8 changes: 8 additions & 0 deletions packages/backend/src/open_payments/grant/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum GrantError {
GrantRequiresInteraction = 'GrantRequiresInteraction',
InvalidGrantRequest = 'InvalidGrantRequest'
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
export const isGrantError = (o: any): o is GrantError =>
Object.values(GrantError).includes(o)
1 change: 1 addition & 0 deletions packages/backend/src/open_payments/grant/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class Grant extends BaseModel {
public accessType!: AccessType
public accessActions!: AccessAction[]
public expiresAt?: Date | null
public deletedAt?: Date

public get expired(): boolean {
return !!this.expiresAt && this.expiresAt <= new Date()
Expand Down
Loading
Loading