auth.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import * as z from 'zod/v4';
  2. /**
  3. * Reusable URL validation that disallows javascript: scheme
  4. */
  5. export const SafeUrlSchema = z
  6. .url()
  7. .superRefine((val, ctx) => {
  8. if (!URL.canParse(val)) {
  9. ctx.addIssue({
  10. code: z.ZodIssueCode.custom,
  11. message: 'URL must be parseable',
  12. fatal: true
  13. });
  14. return z.NEVER;
  15. }
  16. })
  17. .refine(url => {
  18. const u = new URL(url);
  19. return u.protocol !== 'javascript:' && u.protocol !== 'data:' && u.protocol !== 'vbscript:';
  20. }, { message: 'URL cannot use javascript:, data:, or vbscript: scheme' });
  21. /**
  22. * RFC 9728 OAuth Protected Resource Metadata
  23. */
  24. export const OAuthProtectedResourceMetadataSchema = z.looseObject({
  25. resource: z.string().url(),
  26. authorization_servers: z.array(SafeUrlSchema).optional(),
  27. jwks_uri: z.string().url().optional(),
  28. scopes_supported: z.array(z.string()).optional(),
  29. bearer_methods_supported: z.array(z.string()).optional(),
  30. resource_signing_alg_values_supported: z.array(z.string()).optional(),
  31. resource_name: z.string().optional(),
  32. resource_documentation: z.string().optional(),
  33. resource_policy_uri: z.string().url().optional(),
  34. resource_tos_uri: z.string().url().optional(),
  35. tls_client_certificate_bound_access_tokens: z.boolean().optional(),
  36. authorization_details_types_supported: z.array(z.string()).optional(),
  37. dpop_signing_alg_values_supported: z.array(z.string()).optional(),
  38. dpop_bound_access_tokens_required: z.boolean().optional()
  39. });
  40. /**
  41. * RFC 8414 OAuth 2.0 Authorization Server Metadata
  42. */
  43. export const OAuthMetadataSchema = z.looseObject({
  44. issuer: z.string(),
  45. authorization_endpoint: SafeUrlSchema,
  46. token_endpoint: SafeUrlSchema,
  47. registration_endpoint: SafeUrlSchema.optional(),
  48. scopes_supported: z.array(z.string()).optional(),
  49. response_types_supported: z.array(z.string()),
  50. response_modes_supported: z.array(z.string()).optional(),
  51. grant_types_supported: z.array(z.string()).optional(),
  52. token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
  53. token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
  54. service_documentation: SafeUrlSchema.optional(),
  55. revocation_endpoint: SafeUrlSchema.optional(),
  56. revocation_endpoint_auth_methods_supported: z.array(z.string()).optional(),
  57. revocation_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
  58. introspection_endpoint: z.string().optional(),
  59. introspection_endpoint_auth_methods_supported: z.array(z.string()).optional(),
  60. introspection_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
  61. code_challenge_methods_supported: z.array(z.string()).optional(),
  62. client_id_metadata_document_supported: z.boolean().optional()
  63. });
  64. /**
  65. * OpenID Connect Discovery 1.0 Provider Metadata
  66. * see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
  67. */
  68. export const OpenIdProviderMetadataSchema = z.looseObject({
  69. issuer: z.string(),
  70. authorization_endpoint: SafeUrlSchema,
  71. token_endpoint: SafeUrlSchema,
  72. userinfo_endpoint: SafeUrlSchema.optional(),
  73. jwks_uri: SafeUrlSchema,
  74. registration_endpoint: SafeUrlSchema.optional(),
  75. scopes_supported: z.array(z.string()).optional(),
  76. response_types_supported: z.array(z.string()),
  77. response_modes_supported: z.array(z.string()).optional(),
  78. grant_types_supported: z.array(z.string()).optional(),
  79. acr_values_supported: z.array(z.string()).optional(),
  80. subject_types_supported: z.array(z.string()),
  81. id_token_signing_alg_values_supported: z.array(z.string()),
  82. id_token_encryption_alg_values_supported: z.array(z.string()).optional(),
  83. id_token_encryption_enc_values_supported: z.array(z.string()).optional(),
  84. userinfo_signing_alg_values_supported: z.array(z.string()).optional(),
  85. userinfo_encryption_alg_values_supported: z.array(z.string()).optional(),
  86. userinfo_encryption_enc_values_supported: z.array(z.string()).optional(),
  87. request_object_signing_alg_values_supported: z.array(z.string()).optional(),
  88. request_object_encryption_alg_values_supported: z.array(z.string()).optional(),
  89. request_object_encryption_enc_values_supported: z.array(z.string()).optional(),
  90. token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
  91. token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
  92. display_values_supported: z.array(z.string()).optional(),
  93. claim_types_supported: z.array(z.string()).optional(),
  94. claims_supported: z.array(z.string()).optional(),
  95. service_documentation: z.string().optional(),
  96. claims_locales_supported: z.array(z.string()).optional(),
  97. ui_locales_supported: z.array(z.string()).optional(),
  98. claims_parameter_supported: z.boolean().optional(),
  99. request_parameter_supported: z.boolean().optional(),
  100. request_uri_parameter_supported: z.boolean().optional(),
  101. require_request_uri_registration: z.boolean().optional(),
  102. op_policy_uri: SafeUrlSchema.optional(),
  103. op_tos_uri: SafeUrlSchema.optional(),
  104. client_id_metadata_document_supported: z.boolean().optional()
  105. });
  106. /**
  107. * OpenID Connect Discovery metadata that may include OAuth 2.0 fields
  108. * This schema represents the real-world scenario where OIDC providers
  109. * return a mix of OpenID Connect and OAuth 2.0 metadata fields
  110. */
  111. export const OpenIdProviderDiscoveryMetadataSchema = z.object({
  112. ...OpenIdProviderMetadataSchema.shape,
  113. ...OAuthMetadataSchema.pick({
  114. code_challenge_methods_supported: true
  115. }).shape
  116. });
  117. /**
  118. * OAuth 2.1 token response
  119. */
  120. export const OAuthTokensSchema = z
  121. .object({
  122. access_token: z.string(),
  123. id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
  124. token_type: z.string(),
  125. expires_in: z.coerce.number().optional(),
  126. scope: z.string().optional(),
  127. refresh_token: z.string().optional()
  128. })
  129. .strip();
  130. /**
  131. * OAuth 2.1 error response
  132. */
  133. export const OAuthErrorResponseSchema = z.object({
  134. error: z.string(),
  135. error_description: z.string().optional(),
  136. error_uri: z.string().optional()
  137. });
  138. /**
  139. * Optional version of SafeUrlSchema that allows empty string for retrocompatibility on tos_uri and logo_uri
  140. */
  141. export const OptionalSafeUrlSchema = SafeUrlSchema.optional().or(z.literal('').transform(() => undefined));
  142. /**
  143. * RFC 7591 OAuth 2.0 Dynamic Client Registration metadata
  144. */
  145. export const OAuthClientMetadataSchema = z
  146. .object({
  147. redirect_uris: z.array(SafeUrlSchema),
  148. token_endpoint_auth_method: z.string().optional(),
  149. grant_types: z.array(z.string()).optional(),
  150. response_types: z.array(z.string()).optional(),
  151. client_name: z.string().optional(),
  152. client_uri: SafeUrlSchema.optional(),
  153. logo_uri: OptionalSafeUrlSchema,
  154. scope: z.string().optional(),
  155. contacts: z.array(z.string()).optional(),
  156. tos_uri: OptionalSafeUrlSchema,
  157. policy_uri: z.string().optional(),
  158. jwks_uri: SafeUrlSchema.optional(),
  159. jwks: z.any().optional(),
  160. software_id: z.string().optional(),
  161. software_version: z.string().optional(),
  162. software_statement: z.string().optional()
  163. })
  164. .strip();
  165. /**
  166. * RFC 7591 OAuth 2.0 Dynamic Client Registration client information
  167. */
  168. export const OAuthClientInformationSchema = z
  169. .object({
  170. client_id: z.string(),
  171. client_secret: z.string().optional(),
  172. client_id_issued_at: z.number().optional(),
  173. client_secret_expires_at: z.number().optional()
  174. })
  175. .strip();
  176. /**
  177. * RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
  178. */
  179. export const OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(OAuthClientInformationSchema);
  180. /**
  181. * RFC 7591 OAuth 2.0 Dynamic Client Registration error response
  182. */
  183. export const OAuthClientRegistrationErrorSchema = z
  184. .object({
  185. error: z.string(),
  186. error_description: z.string().optional()
  187. })
  188. .strip();
  189. /**
  190. * RFC 7009 OAuth 2.0 Token Revocation request
  191. */
  192. export const OAuthTokenRevocationRequestSchema = z
  193. .object({
  194. token: z.string(),
  195. token_type_hint: z.string().optional()
  196. })
  197. .strip();
  198. //# sourceMappingURL=auth.js.map