Skip to content

Commit 64e31a0

Browse files
committed
restrict url schemes allowed in metadata
1 parent 69d4eb1 commit 64e31a0

File tree

1 file changed

+31
-18
lines changed

1 file changed

+31
-18
lines changed

src/shared/auth.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
import { z } from "zod";
22

3+
/**
4+
* Reusable URL validation that disallows javascript: scheme
5+
*/
6+
export const SafeUrlSchema = z.string().url()
7+
.refine(
8+
(url) => URL.canParse(url),
9+
{message: "URL must be parseable"}
10+
).refine(
11+
(url) => !url.toLowerCase().startsWith('javascript:'),
12+
{ message: "URL cannot use javascript: scheme" }
13+
);
14+
15+
316
/**
417
* RFC 9728 OAuth Protected Resource Metadata
518
*/
619
export const OAuthProtectedResourceMetadataSchema = z
720
.object({
821
resource: z.string().url(),
9-
authorization_servers: z.array(z.string().url()).optional(),
22+
authorization_servers: z.array(SafeUrlSchema).optional(),
1023
jwks_uri: z.string().url().optional(),
1124
scopes_supported: z.array(z.string()).optional(),
1225
bearer_methods_supported: z.array(z.string()).optional(),
@@ -28,9 +41,9 @@ export const OAuthProtectedResourceMetadataSchema = z
2841
export const OAuthMetadataSchema = z
2942
.object({
3043
issuer: z.string(),
31-
authorization_endpoint: z.string(),
32-
token_endpoint: z.string(),
33-
registration_endpoint: z.string().optional(),
44+
authorization_endpoint: SafeUrlSchema,
45+
token_endpoint: SafeUrlSchema,
46+
registration_endpoint: SafeUrlSchema.optional(),
3447
scopes_supported: z.array(z.string()).optional(),
3548
response_types_supported: z.array(z.string()),
3649
response_modes_supported: z.array(z.string()).optional(),
@@ -39,8 +52,8 @@ export const OAuthMetadataSchema = z
3952
token_endpoint_auth_signing_alg_values_supported: z
4053
.array(z.string())
4154
.optional(),
42-
service_documentation: z.string().optional(),
43-
revocation_endpoint: z.string().optional(),
55+
service_documentation: SafeUrlSchema.optional(),
56+
revocation_endpoint: SafeUrlSchema.optional(),
4457
revocation_endpoint_auth_methods_supported: z.array(z.string()).optional(),
4558
revocation_endpoint_auth_signing_alg_values_supported: z
4659
.array(z.string())
@@ -63,11 +76,11 @@ export const OAuthMetadataSchema = z
6376
export const OpenIdProviderMetadataSchema = z
6477
.object({
6578
issuer: z.string(),
66-
authorization_endpoint: z.string(),
67-
token_endpoint: z.string(),
68-
userinfo_endpoint: z.string().optional(),
69-
jwks_uri: z.string(),
70-
registration_endpoint: z.string().optional(),
79+
authorization_endpoint: SafeUrlSchema,
80+
token_endpoint: SafeUrlSchema,
81+
userinfo_endpoint: SafeUrlSchema.optional(),
82+
jwks_uri: SafeUrlSchema,
83+
registration_endpoint: SafeUrlSchema.optional(),
7184
scopes_supported: z.array(z.string()).optional(),
7285
response_types_supported: z.array(z.string()),
7386
response_modes_supported: z.array(z.string()).optional(),
@@ -101,8 +114,8 @@ export const OpenIdProviderMetadataSchema = z
101114
request_parameter_supported: z.boolean().optional(),
102115
request_uri_parameter_supported: z.boolean().optional(),
103116
require_request_uri_registration: z.boolean().optional(),
104-
op_policy_uri: z.string().optional(),
105-
op_tos_uri: z.string().optional(),
117+
op_policy_uri: SafeUrlSchema.optional(),
118+
op_tos_uri: SafeUrlSchema.optional(),
106119
})
107120
.passthrough();
108121

@@ -146,18 +159,18 @@ export const OAuthErrorResponseSchema = z
146159
* RFC 7591 OAuth 2.0 Dynamic Client Registration metadata
147160
*/
148161
export const OAuthClientMetadataSchema = z.object({
149-
redirect_uris: z.array(z.string()).refine((uris) => uris.every((uri) => URL.canParse(uri)), { message: "redirect_uris must contain valid URLs" }),
162+
redirect_uris: z.array(SafeUrlSchema),
150163
token_endpoint_auth_method: z.string().optional(),
151164
grant_types: z.array(z.string()).optional(),
152165
response_types: z.array(z.string()).optional(),
153166
client_name: z.string().optional(),
154-
client_uri: z.string().optional(),
155-
logo_uri: z.string().optional(),
167+
client_uri: SafeUrlSchema.optional(),
168+
logo_uri: SafeUrlSchema.optional(),
156169
scope: z.string().optional(),
157170
contacts: z.array(z.string()).optional(),
158-
tos_uri: z.string().optional(),
171+
tos_uri: SafeUrlSchema.optional(),
159172
policy_uri: z.string().optional(),
160-
jwks_uri: z.string().optional(),
173+
jwks_uri: SafeUrlSchema.optional(),
161174
jwks: z.any().optional(),
162175
software_id: z.string().optional(),
163176
software_version: z.string().optional(),

0 commit comments

Comments
 (0)