Skip to content

Commit bf27217

Browse files
authored
feat(protectedResourceHandler): support more protected resource identifiers (#84)
* feat(protectedResourceHandler): support more protected resource identifiers * chore: respond to PR feedback
1 parent 3071800 commit bf27217

File tree

2 files changed

+66
-5
lines changed

2 files changed

+66
-5
lines changed

src/auth/auth-metadata.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,20 @@ export function protectedResourceHandler({
2525
authServerUrls: string[];
2626
}) {
2727
return (req: Request) => {
28-
const origin = new URL(req.url).origin;
28+
const resourceUrl = new URL(req.url);
29+
30+
resourceUrl.pathname = resourceUrl.pathname
31+
.replace(/^\/\.well-known\/[^\/]+/, "");
32+
33+
// The URL class does not allow for empty `pathname` and will replace it
34+
// with "/". Here, we correct that.
35+
const resource = resourceUrl.pathname === '/'
36+
? resourceUrl.toString().replace(/\/$/, '')
37+
: resourceUrl.toString();
2938

3039
const metadata = generateProtectedResourceMetadata({
3140
authServerUrls,
32-
resourceUrl: origin,
41+
resourceUrl: resource,
3342
});
3443

3544
return new Response(JSON.stringify(metadata), {
@@ -43,13 +52,15 @@ export function protectedResourceHandler({
4352
}
4453

4554
/**
46-
* Generates protected resource metadata for the given auth server urls and
47-
* resource server url.
55+
* Generates protected resource metadata for the given auth server URLs and
56+
* protected resource identifier. The protected resource identifier, as defined
57+
* in RFC 9728, should be a a URL that uses the https scheme and has no fragment
58+
* component.
4859
*
4960
* @param authServerUrls - Array of issuer URLs of the authorization servers. Each URL should
5061
* match the "issuer" field in the respective authorization server's
5162
* OAuth metadata (RFC 8414).
52-
* @param resourceUrl - URL of the resource server
63+
* @param resourceUrl - the protected resource identifier
5364
* @param additionalMetadata - Additional metadata fields to include in the response
5465
* @returns Protected resource metadata, serializable to JSON
5566
*/

tests/auth.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { describe, it, expect } from "vitest";
2+
import { protectedResourceHandler } from "../src/index";
3+
4+
describe("auth", () => {
5+
describe("resource metadata URL to resource identifier mapping", () => {
6+
const handler = protectedResourceHandler({
7+
authServerUrls: ["https://auth-server.com"],
8+
});
9+
10+
const testCases = [
11+
// Default well-known URI suffix (oauth-protected-resource)
12+
{
13+
resourceMetadata: 'https://resource-server.com/.well-known/oauth-protected-resource',
14+
resource: 'https://resource-server.com',
15+
},
16+
{
17+
resourceMetadata: 'https://resource-server.com/.well-known/oauth-protected-resource/my-resource',
18+
resource: 'https://resource-server.com/my-resource',
19+
},
20+
{
21+
resourceMetadata: 'https://resource-server.com/.well-known/oauth-protected-resource/foo/bar',
22+
resource: 'https://resource-server.com/foo/bar',
23+
},
24+
// Ensure ports work
25+
{
26+
resourceMetadata: 'https://resource-server.com:8443/.well-known/oauth-protected-resource',
27+
resource: 'https://resource-server.com:8443',
28+
},
29+
// Example well-known URI suffix from RFC 9728 (example-protected-resource)
30+
{
31+
resourceMetadata: 'https://resource-server.com/.well-known/example-protected-resource',
32+
resource: 'https://resource-server.com',
33+
},
34+
{
35+
resourceMetadata: 'https://resource-server.com/.well-known/example-protected-resource/my-resource',
36+
resource: 'https://resource-server.com/my-resource',
37+
},
38+
] as const;
39+
40+
testCases.forEach(testCase => {
41+
it(`${testCase.resourceMetadata}${testCase.resource}`, async () => {
42+
const req = new Request(testCase.resourceMetadata);
43+
const res = handler(req);
44+
const json = await res.json();
45+
expect(json.resource).toBe(testCase.resource);
46+
});
47+
});
48+
});
49+
});
50+

0 commit comments

Comments
 (0)