Skip to content

Commit dc09cf6

Browse files
committed
fix: Return invalid_resource_id error for invalid ticket requests
1 parent f7bb12b commit dc09cf6

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

packages/uma/config/routes/tickets.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"handler": {
1111
"@type": "TicketRequestHandler",
1212
"ticketingStrategy": { "@id": "urn:uma:default:TicketingStrategy" },
13-
"ticketStore": { "@id": "urn:uma:default:TicketStore" }
13+
"ticketStore": { "@id": "urn:uma:default:TicketStore" },
14+
"resourceStore": { "@id": "urn:uma:default:ResourceRegistrationStore" }
1415
},
1516
"path": "/uma/ticket"
1617
}

packages/uma/src/routes/Ticket.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/ht
1212
import { verifyRequest } from '../util/HttpMessageSignatures';
1313
import { array, reType } from '../util/ReType';
1414
import { Permission } from '../views/Permission';
15+
import { ResourceDescription } from '../views/ResourceDescription';
1516

1617
/**
1718
* A TicketRequestHandler is tasked with implementing
@@ -25,6 +26,7 @@ export class TicketRequestHandler extends HttpHandler {
2526
constructor(
2627
protected readonly ticketingStrategy: TicketingStrategy,
2728
protected readonly ticketStore: KeyValueStorage<string, Ticket>,
29+
protected readonly resourceStore: KeyValueStorage<string, ResourceDescription>,
2830
) {
2931
super();
3032
}
@@ -40,6 +42,19 @@ export class TicketRequestHandler extends HttpHandler {
4042
throw new BadRequestHttpError(`Request has bad syntax: ${createErrorMessage(e)}`);
4143
}
4244

45+
for (const { resource_id } of request.body) {
46+
// https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-federated-authz-2.0.html#rfc.section.4.3
47+
if (!await this.resourceStore.has(resource_id)) {
48+
return {
49+
status: 400,
50+
body: {
51+
error: 'invalid_resource_id',
52+
error_description: `Unknown UMA ID ${resource_id}`,
53+
}
54+
}
55+
}
56+
}
57+
4358
const ticket = await this.ticketingStrategy.initializeTicket(request.body);
4459
const resolved = await this.ticketingStrategy.resolveTicket(ticket);
4560

packages/uma/test/unit/routes/Ticket.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { TicketingStrategy } from '../../../src/ticketing/strategy/TicketingStra
55
import { Ticket } from '../../../src/ticketing/Ticket';
66
import { HttpHandlerContext } from '../../../src/util/http/models/HttpHandler';
77
import * as signatures from '../../../src/util/HttpMessageSignatures';
8+
import { ResourceDescription } from '../../../src/views/ResourceDescription';
89

910
vi.mock('node:crypto', () => ({
1011
randomUUID: vi.fn().mockReturnValue('1-2-3-4-5'),
@@ -16,6 +17,7 @@ describe('Ticket', (): void => {
1617

1718
let ticketingStrategy: Mocked<TicketingStrategy>;
1819
let ticketStore: Mocked<KeyValueStorage<string, Ticket>>;
20+
let resourceStore: Mocked<KeyValueStorage<string, ResourceDescription>>;
1921
let handler: TicketRequestHandler;
2022

2123
beforeEach(async(): Promise<void> => {
@@ -33,11 +35,15 @@ describe('Ticket', (): void => {
3335
validateClaims: vi.fn(),
3436
};
3537

38+
resourceStore = {
39+
has: vi.fn().mockResolvedValue(true),
40+
} satisfies Partial<KeyValueStorage<string, ResourceDescription>> as any;
41+
3642
ticketStore = {
3743
set: vi.fn(),
3844
} satisfies Partial<KeyValueStorage<string, Ticket>> as any;
3945

40-
handler = new TicketRequestHandler(ticketingStrategy, ticketStore);
46+
handler = new TicketRequestHandler(ticketingStrategy, ticketStore, resourceStore);
4147
});
4248

4349
it('errors if the request is not authorized.', async(): Promise<void> => {
@@ -64,4 +70,16 @@ describe('Ticket', (): void => {
6470
expect(ticketStore.set).toHaveBeenCalledTimes(1);
6571
expect(ticketStore.set).toHaveBeenLastCalledWith('1-2-3-4-5', 'ticket');
6672
});
73+
74+
it('returns with invalid_resource_id if one of the targets is unknown.', async(): Promise<void> => {
75+
request.request.body = [
76+
{ resource_id: 'id1', resource_scopes: [ 'scope1' ]},
77+
{ resource_id: 'id2', resource_scopes: [ 'scope2' ]},
78+
];
79+
resourceStore.has.mockResolvedValueOnce(true);
80+
resourceStore.has.mockResolvedValueOnce(false);
81+
await expect(handler.handle(request)).resolves
82+
.toEqual({ status: 400, body: { error: 'invalid_resource_id', error_description: 'Unknown UMA ID id2' }});
83+
expect(ticketStore.set).toHaveBeenCalledTimes(0);
84+
});
6785
});

0 commit comments

Comments
 (0)