Skip to content

Commit bbf9550

Browse files
committed
feat: Move JSON conversion away from route handlers
1 parent 2b8939d commit bbf9550

18 files changed

Lines changed: 248 additions & 268 deletions

packages/uma/.componentsignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"AccessToken",
44
"Authorization",
55
"Authorizer",
6+
"Buffer",
67
"Error",
78
"EventEmitter",
89
"Map",
@@ -13,5 +14,6 @@
1314
"ReType",
1415
"ScopeDescription",
1516
"Ticket",
16-
"URL"
17+
"URL",
18+
"WeakMap"
1719
]

packages/uma/bin/demo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const launch: () => Promise<void> = async () => {
3838
await manager.configRegistry.register(configPath);
3939

4040
const umaServer: ServerInitializer = await manager.instantiate('urn:uma:default:NodeHttpServer',{variables});
41-
await umaServer.handle();
41+
await umaServer.handleSafe();
4242

4343
};
4444

packages/uma/bin/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const launch: () => Promise<void> = async () => {
3838
await manager.configRegistry.register(configPath);
3939

4040
const umaServer: ServerInitializer = await manager.instantiate('urn:uma:default:NodeHttpServer',{variables});
41-
await umaServer.handle();
41+
await umaServer.handleSafe();
4242

4343
};
4444

packages/uma/config/default.json

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,23 @@
8282
"includeQueryString": true
8383
},
8484
"httpHandler": {
85-
"@id": "urn:uma:default:RoutedHttpRequestHandler",
86-
"@type": "RoutedHttpRequestHandler",
87-
"routes": [
88-
{ "@id": "urn:uma:default:UmaConfigRoute" },
89-
{ "@id": "urn:uma:default:JwksRoute" },
90-
{ "@id": "urn:uma:default:TokenRoute" },
91-
{ "@id": "urn:uma:default:PermissionRegistrationRoute" },
92-
{ "@id": "urn:uma:default:ResourceRegistrationRoute" },
93-
{ "@id": "urn:uma:default:ResourceRegistrationOpsRoute" },
94-
{ "@id": "urn:uma:default:IntrospectionRoute" }
95-
],
96-
"defaultHandler": {
97-
"@type": "DefaultRequestHandler"
85+
"@id": "urm:uma:default:JsonFormHttpHandler",
86+
"@type": "JsonFormHttpHandler",
87+
"handler": {
88+
"@id": "urn:uma:default:RoutedHttpRequestHandler",
89+
"@type": "RoutedHttpRequestHandler",
90+
"routes": [
91+
{ "@id": "urn:uma:default:UmaConfigRoute" },
92+
{ "@id": "urn:uma:default:JwksRoute" },
93+
{ "@id": "urn:uma:default:TokenRoute" },
94+
{ "@id": "urn:uma:default:PermissionRegistrationRoute" },
95+
{ "@id": "urn:uma:default:ResourceRegistrationRoute" },
96+
{ "@id": "urn:uma:default:ResourceRegistrationOpsRoute" },
97+
{ "@id": "urn:uma:default:IntrospectionRoute" }
98+
],
99+
"defaultHandler": {
100+
"@type": "DefaultRequestHandler"
101+
}
98102
}
99103
}
100104
},

packages/uma/src/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,17 @@ export * from './views/Permission';
5050
export * from './views/ResourceDescription';
5151
export * from './views/ScopeDescription';
5252

53-
/* Replace the following with CSS types */
54-
5553
// HTTP
5654
export * from './util/http/identifier/BaseTargetExtractor';
5755
export * from './util/http/models/HttpHandler';
5856
export * from './util/http/models/HttpHandlerRoute';
5957
export * from './util/http/server/ErrorHandler';
58+
export * from './util/http/server/JsonFormHttpHandler';
6059
export * from './util/http/server/NodeHttpRequestResponseHandler';
6160
export * from './util/http/server/RoutedHttpRequestHandler';
61+
62+
// Util
63+
export * from './util/ConvertUtil';
64+
export * from './util/HttpMessageSignatures';
65+
export * from './util/Result';
66+
export * from './util/ReType';

packages/uma/src/routes/Config.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ export enum ResponseType {
88
Token = 'token',
99
Code = 'code',
1010
IDToken = 'id_token'
11-
};
11+
}
1212
// eslint-enable
1313

1414
export type OAuthConfiguration = {
15-
issuer: string,
16-
jwks_uri?: string,
17-
token_endpoint?: string,
18-
grant_types_supported?: string[],
19-
dpop_signing_alg_values_supported?: string[],
20-
response_types_supported?: ResponseType[]
21-
scopes_supported?: string[]
15+
issuer: string,
16+
jwks_uri?: string,
17+
token_endpoint?: string,
18+
grant_types_supported?: string[],
19+
dpop_signing_alg_values_supported?: string[],
20+
response_types_supported?: ResponseType[]
21+
scopes_supported?: string[]
2222
}
2323

2424
export type UmaConfiguration = OAuthConfiguration & {
@@ -44,18 +44,11 @@ export class ConfigRequestHandler extends HttpHandler {
4444
super();
4545
}
4646

47-
/**
48-
* Returns the endpoint's UMA configuration
49-
*
50-
* @param {HttpHandlerContext} context - an irrelevant incoming context
51-
* @return {Observable<HttpHandlerResponse>} - the mock response
52-
*/
53-
async handle(context: HttpHandlerContext): Promise<HttpHandlerResponse> {
47+
public async handle(context: HttpHandlerContext): Promise<HttpHandlerResponse> {
5448
this.logger.info(`Received discovery request at '${context.request.url}'`);
5549

5650
return {
57-
body: JSON.stringify(this.getConfig()),
58-
headers: {'content-type': 'application/json'},
51+
body: this.getConfig(),
5952
status: 200,
6053
};
6154
}

packages/uma/src/routes/Default.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler';
22

33
/**
4-
* Default route handler
4+
* Default route handler that returns a 404.
55
*/
66
export class DefaultRequestHandler extends HttpHandler {
7-
/**
8-
* Default request handler returning a 404 error
9-
* @param {HttpHandlerContext} input
10-
* @return {Observable<HttpHandlerResponse<any>>}
11-
*/
127
async handle(input: HttpHandlerContext): Promise<HttpHandlerResponse<any>> {
138
return {
14-
body: JSON.stringify({
9+
body: {
1510
'status': 404,
1611
'error': 'Not Found',
17-
}),
18-
headers: {'content-type': 'application/json'},
12+
},
1913
status: 404,
2014
};
2115
}

packages/uma/src/routes/Introspection.ts

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
2-
import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler';
1+
import { BadRequestHttpError, getLoggerFor, KeyValueStorage, UnauthorizedHttpError } from '@solid/community-server';
32
import { AccessToken } from '../tokens/AccessToken';
43
import { JwtTokenFactory } from '../tokens/JwtTokenFactory';
54
import { SerializedToken } from '../tokens/TokenFactory';
6-
import {
7-
BadRequestHttpError,
8-
getLoggerFor,
9-
JwkGenerator, KeyValueStorage,
10-
UnauthorizedHttpError,
11-
UnsupportedMediaTypeHttpError
12-
} from '@solid/community-server';
5+
import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler';
136
import { verifyRequest } from '../util/HttpMessageSignatures';
147

158
/**
@@ -27,29 +20,13 @@ export class IntrospectionHandler extends HttpHandler {
2720
constructor(
2821
private readonly tokenStore: KeyValueStorage<string, AccessToken>,
2922
private readonly jwtTokenFactory: JwtTokenFactory,
30-
private readonly keyGen: JwkGenerator,
3123
) {
3224
super();
3325
}
3426

35-
/**
36-
* Handle incoming requests for token introspection
37-
* @param {HttpHandlerContext} param0
38-
* @return {Observable<HttpHandlerResponse<any>>}
39-
*/
4027
async handle({request}: HttpHandlerContext): Promise<HttpHandlerResponse<any>> {
4128
if (!await verifyRequest(request)) throw new UnauthorizedHttpError();
4229

43-
if (request.headers['content-type'] !== 'application/x-www-form-urlencoded') {
44-
throw new UnsupportedMediaTypeHttpError(
45-
'Only Media Type "application/x-www-form-urlencoded" is supported for this route.');
46-
}
47-
48-
if (request.headers['accept'] !== 'application/json') {
49-
throw new UnsupportedMediaTypeHttpError(
50-
'Only "application/json" can be served by this route.');
51-
}
52-
5330
if (!request.body || !(request.body instanceof Object)) {
5431
throw new BadRequestHttpError('Missing request body.');
5532
}
@@ -60,14 +37,12 @@ export class IntrospectionHandler extends HttpHandler {
6037

6138
const jwt = this.opaqueToJwt(opaqueToken);
6239
return {
63-
headers: {'content-type': 'application/json'},
6440
status: 200,
6541
body: jwt,
6642
};
6743
} catch (e) {
6844
throw new BadRequestHttpError('Invalid request body.');
6945
}
70-
7146
}
7247

7348
private async opaqueToJwt(opaque: string): Promise<SerializedToken> {

packages/uma/src/routes/Jwks.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler';
21
import { getLoggerFor, JwkGenerator } from '@solid/community-server';
2+
import { HttpHandler, HttpHandlerContext, HttpHandlerResponse } from '../util/http/models/HttpHandler';
33

44
/**
55
* An HttpHandler used for returning the configuration
@@ -8,32 +8,20 @@ import { getLoggerFor, JwkGenerator } from '@solid/community-server';
88
export class JwksRequestHandler extends HttpHandler {
99
protected readonly logger = getLoggerFor(this);
1010

11-
/**
12-
* Yields a new request handler for JWKS
13-
* @param {JwksKeyHolder} keyholder - the keyholder to be used for serving JWKS
14-
*/
1511
public constructor(
1612
private readonly generator: JwkGenerator
1713
) {
1814
super();
1915
}
2016

21-
/**
22-
* Returns the JSON Web KeySet for specified keyholder
23-
* @param {HttpHandlerContext} context - an irrelevant incoming context
24-
* @return {Observable<HttpHandlerResponse>} - the JWKS response
25-
*/
2617
async handle(context: HttpHandlerContext): Promise<HttpHandlerResponse> {
2718
this.logger.info(`Received JWKS request at '${context.request.url}'`);
2819

2920
const key = await this.generator.getPublicKey();
3021

3122
return {
3223
status: 200,
33-
headers: {
34-
'content-type': 'application/json'
35-
},
36-
body: JSON.stringify({ keys: [ key ] }),
24+
body: { keys: [ key ] },
3725
};
3826
}
3927
}

packages/uma/src/routes/ResourceRegistration.ts

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import {
44
getLoggerFor,
55
KeyValueStorage,
66
MethodNotAllowedHttpError,
7-
UnauthorizedHttpError,
8-
UnsupportedMediaTypeHttpError
7+
UnauthorizedHttpError
98
} from '@solid/community-server';
109
import { randomUUID } from 'node:crypto';
1110
import {
@@ -14,9 +13,9 @@ import {
1413
HttpHandlerRequest,
1514
HttpHandlerResponse
1615
} from '../util/http/models/HttpHandler';
17-
import { ResourceDescription } from '../views/ResourceDescription';
18-
import { reType } from '../util/ReType';
1916
import { extractRequestSigner, verifyRequest } from '../util/HttpMessageSignatures';
17+
import { reType } from '../util/ReType';
18+
import { ResourceDescription } from '../views/ResourceDescription';
2019

2120
type ErrorConstructor = { new(msg: string): Error };
2221

@@ -29,20 +28,12 @@ type ErrorConstructor = { new(msg: string): Error };
2928
export class ResourceRegistrationRequestHandler extends HttpHandler {
3029
protected readonly logger = getLoggerFor(this);
3130

32-
/**
33-
* @param {RequestingPartyRegistration[]} resourceServers - Pod Servers to be registered with the UMA AS
34-
*/
3531
constructor(
3632
private readonly resourceStore: KeyValueStorage<string, ResourceDescription>,
3733
) {
3834
super();
3935
}
4036

41-
/**
42-
* Handle incoming requests for resource registration
43-
* @param {HttpHandlerContext} param0
44-
* @return {Observable<HttpHandlerResponse<PermissionRegistrationResponse>>}
45-
*/
4637
async handle({ request }: HttpHandlerContext): Promise<HttpHandlerResponse<any>> {
4738
const signer = await extractRequestSigner(request);
4839

@@ -62,10 +53,6 @@ export class ResourceRegistrationRequestHandler extends HttpHandler {
6253
private async handlePost(request: HttpHandlerRequest): Promise<HttpHandlerResponse<any>> {
6354
const { headers, body } = request;
6455

65-
if (headers['content-type'] !== 'application/json') {
66-
throw new UnsupportedMediaTypeHttpError('Only Media Type "application/json" is supported for this route.');
67-
}
68-
6956
try {
7057
reType(body, ResourceDescription);
7158
} catch (e) {
@@ -80,13 +67,10 @@ export class ResourceRegistrationRequestHandler extends HttpHandler {
8067

8168
return ({
8269
status: 201,
83-
headers: {
84-
'content-type': 'application/json'
85-
},
86-
body: JSON.stringify({
70+
body: {
8771
_id: resource,
8872
user_access_policy_uri: 'TODO: implement policy UI',
89-
}),
73+
},
9074
})
9175
}
9276

@@ -99,10 +83,7 @@ export class ResourceRegistrationRequestHandler extends HttpHandler {
9983

10084
this.logger.info(`Deleted resource ${parameters.id}.`);
10185

102-
return ({
103-
status: 204,
104-
headers: {},
105-
});
86+
return { status: 204 };
10687
}
10788

10889
/**

0 commit comments

Comments
 (0)