Skip to content

Commit

Permalink
fix: always pass in identifier into token introspection (#2907)
Browse files Browse the repository at this point in the history
* feat(auth): check identifier only if present in grant

* chore(backend): always pass in identifier during token introspection
  • Loading branch information
mkurapov authored Aug 27, 2024
1 parent b9be601 commit e20228f
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 30 deletions.
102 changes: 102 additions & 0 deletions packages/auth/src/access/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,66 @@ describe('Access utilities', (): void => {
).toBe(true)
})

test('Can compare an access item on a grant and an access item from a request with different action ordering', async (): Promise<void> => {
const grantAccessItemSuperAction = await Access.query(trx).insertAndFetch({
grantId: grant.id,
type: AccessType.OutgoingPayment,
actions: [AccessAction.Create, AccessAction.ReadAll, AccessAction.List],
identifier,
limits: {
receiver,
debitAmount: {
value: '400',
assetCode: 'USD',
assetScale: 2
}
}
})

const requestAccessItem: AccessItem = {
type: 'outgoing-payment',
actions: ['read', 'list', 'create'],
identifier,
limits: {
receiver,
debitAmount: {
value: '400',
assetCode: 'USD',
assetScale: 2
}
}
}

expect(
compareRequestAndGrantAccessItems(
requestAccessItem,
toOpenPaymentsAccess(grantAccessItemSuperAction)
)
).toBe(true)
})

test('Can compare an access item on a grant without an identifier with a request with an identifier', async (): Promise<void> => {
const grantAccessItemSuperAction = await Access.query(trx).insertAndFetch({
grantId: grant.id,
type: AccessType.IncomingPayment,
actions: [AccessAction.ReadAll],
identifier: undefined
})

const requestAccessItem: AccessItem = {
type: 'incoming-payment',
actions: [AccessAction.ReadAll],
identifier
}

expect(
compareRequestAndGrantAccessItems(
requestAccessItem,
toOpenPaymentsAccess(grantAccessItemSuperAction)
)
).toBe(true)
})

test('access comparison fails if grant action items are insufficient', async (): Promise<void> => {
const identifier = `https://example.com/${v4()}`
const receiver =
Expand Down Expand Up @@ -206,4 +266,46 @@ describe('Access utilities', (): void => {
)
).toBe(false)
})

test('access comparison fails if identifier mismatch', async (): Promise<void> => {
const grantAccessItemSuperAction = await Access.query(trx).insertAndFetch({
grantId: grant.id,
type: AccessType.IncomingPayment,
actions: [AccessAction.ReadAll],
identifier
})

const requestAccessItem: AccessItem = {
type: 'incoming-payment',
actions: [AccessAction.ReadAll],
identifier: `https://example.com/${v4()}`
}

expect(
compareRequestAndGrantAccessItems(
requestAccessItem,
toOpenPaymentsAccess(grantAccessItemSuperAction)
)
).toBe(false)
})

test('access comparison fails if type mismatch', async (): Promise<void> => {
const grantAccessItemSuperAction = await Access.query(trx).insertAndFetch({
grantId: grant.id,
type: AccessType.Quote,
actions: [AccessAction.Read]
})

const requestAccessItem: AccessItem = {
type: 'incoming-payment',
actions: [AccessAction.Read]
}

expect(
compareRequestAndGrantAccessItems(
requestAccessItem,
toOpenPaymentsAccess(grantAccessItemSuperAction)
)
).toBe(false)
})
})
21 changes: 15 additions & 6 deletions packages/auth/src/access/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,22 @@ export function compareRequestAndGrantAccessItems(
return false
}

// Validate remaining keys
if (restOfRequestAccessItem.type !== restOfGrantAccessItem.type) {
return false
}

// Validate identifier, if present on the grant
const grantAccessIdentifier = (
restOfGrantAccessItem as OutgoingPaymentOrIncomingPaymentAccess
).identifier

const requestAccessIdentifier = (
restOfRequestAccessItem as OutgoingPaymentOrIncomingPaymentAccess
).identifier

if (
restOfRequestAccessItem.type !== restOfGrantAccessItem.type ||
(restOfRequestAccessItem as OutgoingPaymentOrIncomingPaymentAccess)
.identifier !==
(restOfGrantAccessItem as OutgoingPaymentOrIncomingPaymentAccess)
.identifier
grantAccessIdentifier &&
requestAccessIdentifier !== grantAccessIdentifier
) {
return false
}
Expand Down
33 changes: 17 additions & 16 deletions packages/backend/src/open_payments/auth/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type IntrospectionCallObject = {
access: {
type: AccessType
actions: AccessAction[]
identifier?: string
identifier: string
}[]
}

Expand Down Expand Up @@ -193,7 +193,8 @@ describe('Auth Middleware', (): void => {
access: [
{
type: type,
actions: [action]
actions: [action],
identifier: ctx.walletAddressUrl
}
]
})
Expand All @@ -217,7 +218,8 @@ describe('Auth Middleware', (): void => {
access: [
{
type: type,
actions: [action]
actions: [action],
identifier: ctx.walletAddressUrl
}
]
})
Expand Down Expand Up @@ -286,15 +288,12 @@ describe('Auth Middleware', (): void => {
access: [
{
type,
actions: [action]
actions: [action],
identifier: ctx.walletAddressUrl
}
]
}

if (type === AccessType.OutgoingPayment) {
expectedCallObject.access[0].identifier = ctx.walletAddressUrl
}

expect(introspectSpy).toHaveBeenCalledWith(expectedCallObject)
expect(next).not.toHaveBeenCalled()
}
Expand All @@ -313,7 +312,8 @@ describe('Auth Middleware', (): void => {
access: [
{
type,
actions: [action]
actions: [action],
identifier: ctx.walletAddressUrl
}
]
})
Expand Down Expand Up @@ -349,7 +349,8 @@ describe('Auth Middleware', (): void => {
access: [
{
type,
actions: [subAction]
actions: [subAction],
identifier: ctx.walletAddressUrl
}
]
})
Expand All @@ -376,7 +377,8 @@ describe('Auth Middleware', (): void => {
access: [
{
type,
actions: [superAction]
actions: [superAction],
identifier: ctx.walletAddressUrl
}
]
})
Expand Down Expand Up @@ -436,13 +438,11 @@ describe('Auth Middleware', (): void => {
access: [
{
type,
actions: [action]
actions: [action],
identifier: ctx.walletAddressUrl
}
]
}
if (type === AccessType.OutgoingPayment) {
expectedCallObject.access[0].identifier = ctx.walletAddressUrl
}

expect(introspectSpy).toHaveBeenCalledWith(expectedCallObject)
expect(next).toHaveBeenCalled()
Expand Down Expand Up @@ -473,7 +473,8 @@ describe('Auth Middleware', (): void => {
access: [
{
type,
actions: [action]
actions: [action],
identifier: ctx.walletAddressUrl
}
]
})
Expand Down
14 changes: 6 additions & 8 deletions packages/backend/src/open_payments/auth/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface Grant {
export interface Access {
type: string
actions: AccessAction[]
identifier?: string
identifier: string
}

function contextToRequestLike(ctx: HttpSigContext): RequestLike {
Expand Down Expand Up @@ -95,13 +95,11 @@ export function createTokenIntrospectionMiddleware({
tokenInfo = await tokenIntrospectionClient.introspect({
access_token: token,
access: [
requestType === AccessType.OutgoingPayment
? toOpenPaymentsAccess(
requestType,
requestAction,
ctx.walletAddressUrl
)
: toOpenPaymentsAccess(requestType, requestAction)
toOpenPaymentsAccess(
requestType,
requestAction,
ctx.walletAddressUrl
)
]
})
} catch (err) {
Expand Down

0 comments on commit e20228f

Please sign in to comment.