@atproto/oauth-types 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/atproto-loopback-client-id.d.ts +3 -3
- package/dist/atproto-loopback-client-id.d.ts.map +1 -1
- package/dist/atproto-loopback-client-id.js.map +1 -1
- package/dist/atproto-loopback-client-metadata.d.ts +5 -5
- package/dist/atproto-loopback-client-metadata.d.ts.map +1 -1
- package/dist/atproto-loopback-client-metadata.js.map +1 -1
- package/dist/atproto-oauth-scope.d.ts +2 -2
- package/dist/atproto-oauth-scope.d.ts.map +1 -1
- package/dist/atproto-oauth-scope.js.map +1 -1
- package/dist/atproto-oauth-token-response.d.ts +1 -1
- package/dist/atproto-oauth-token-response.d.ts.map +1 -1
- package/dist/atproto-oauth-token-response.js.map +1 -1
- package/dist/oauth-client-id-discoverable.d.ts +1 -1
- package/dist/oauth-client-id-discoverable.d.ts.map +1 -1
- package/dist/oauth-client-id-discoverable.js.map +1 -1
- package/dist/oauth-client-id-loopback.d.ts +2 -2
- package/dist/oauth-client-id-loopback.d.ts.map +1 -1
- package/dist/oauth-client-id-loopback.js.map +1 -1
- package/dist/oauth-introspection-response.d.ts +2 -2
- package/dist/oauth-introspection-response.d.ts.map +1 -1
- package/dist/oauth-introspection-response.js.map +1 -1
- package/dist/oauth-redirect-uri.d.ts +1 -1
- package/dist/oauth-redirect-uri.d.ts.map +1 -1
- package/dist/oauth-redirect-uri.js.map +1 -1
- package/dist/uri.d.ts +1 -1
- package/dist/uri.d.ts.map +1 -1
- package/dist/uri.js.map +1 -1
- package/dist/util.d.ts +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js.map +1 -1
- package/package.json +11 -7
- package/src/atproto-loopback-client-id.ts +0 -78
- package/src/atproto-loopback-client-metadata.ts +0 -47
- package/src/atproto-loopback-client-redirect-uris.ts +0 -4
- package/src/atproto-oauth-scope.ts +0 -34
- package/src/atproto-oauth-token-response.ts +0 -16
- package/src/constants.ts +0 -2
- package/src/index.ts +0 -50
- package/src/oauth-access-token.ts +0 -4
- package/src/oauth-authorization-code-grant-token-request.ts +0 -19
- package/src/oauth-authorization-details.ts +0 -48
- package/src/oauth-authorization-request-jar.ts +0 -16
- package/src/oauth-authorization-request-par.ts +0 -12
- package/src/oauth-authorization-request-parameters.ts +0 -100
- package/src/oauth-authorization-request-query.ts +0 -21
- package/src/oauth-authorization-request-uri.ts +0 -10
- package/src/oauth-authorization-response-error.ts +0 -25
- package/src/oauth-authorization-server-metadata.ts +0 -120
- package/src/oauth-client-credentials-grant-token-request.ts +0 -9
- package/src/oauth-client-credentials.ts +0 -53
- package/src/oauth-client-id-discoverable.ts +0 -130
- package/src/oauth-client-id-loopback.ts +0 -164
- package/src/oauth-client-id.ts +0 -4
- package/src/oauth-client-metadata.ts +0 -80
- package/src/oauth-code-challenge-method.ts +0 -3
- package/src/oauth-endpoint-auth-method.ts +0 -13
- package/src/oauth-endpoint-name.ts +0 -8
- package/src/oauth-grant-type.ts +0 -13
- package/src/oauth-introspection-response.ts +0 -23
- package/src/oauth-issuer-identifier.ts +0 -47
- package/src/oauth-par-response.ts +0 -8
- package/src/oauth-password-grant-token-request.ts +0 -11
- package/src/oauth-prompt-mode.ts +0 -18
- package/src/oauth-protected-resource-metadata.ts +0 -94
- package/src/oauth-redirect-uri.ts +0 -73
- package/src/oauth-refresh-token-grant-token-request.ts +0 -11
- package/src/oauth-refresh-token.ts +0 -4
- package/src/oauth-request-uri.ts +0 -5
- package/src/oauth-response-mode.ts +0 -9
- package/src/oauth-response-type.ts +0 -17
- package/src/oauth-scope.ts +0 -21
- package/src/oauth-token-identification.ts +0 -12
- package/src/oauth-token-request.ts +0 -14
- package/src/oauth-token-response.ts +0 -29
- package/src/oauth-token-type.ts +0 -15
- package/src/oidc-authorization-error-response.ts +0 -29
- package/src/oidc-claims-parameter.ts +0 -40
- package/src/oidc-claims-properties.ts +0 -11
- package/src/oidc-entity-type.ts +0 -5
- package/src/oidc-userinfo.ts +0 -15
- package/src/uri.ts +0 -221
- package/src/util.ts +0 -170
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -4
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { signedJwtSchema } from '@atproto/jwk'
|
|
3
|
-
import { CLIENT_ASSERTION_TYPE_JWT_BEARER } from './constants.js'
|
|
4
|
-
import { oauthClientIdSchema } from './oauth-client-id.js'
|
|
5
|
-
|
|
6
|
-
export const oauthClientCredentialsJwtBearerSchema = z.object({
|
|
7
|
-
client_id: oauthClientIdSchema,
|
|
8
|
-
client_assertion_type: z.literal(CLIENT_ASSERTION_TYPE_JWT_BEARER),
|
|
9
|
-
/**
|
|
10
|
-
* - "sub" the subject MUST be the "client_id" of the OAuth client
|
|
11
|
-
* - "iat" is required and MUST be less than one minute
|
|
12
|
-
* - "aud" must containing a value that identifies the authorization server
|
|
13
|
-
* - The JWT MAY contain a "jti" (JWT ID) claim that provides a unique identifier for the token.
|
|
14
|
-
* - Note that the authorization server may reject JWTs with an "exp" claim value that is unreasonably far in the future.
|
|
15
|
-
*
|
|
16
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc7523#section-3}
|
|
17
|
-
*/
|
|
18
|
-
client_assertion: signedJwtSchema,
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
export type OAuthClientCredentialsJwtBearer = z.infer<
|
|
22
|
-
typeof oauthClientCredentialsJwtBearerSchema
|
|
23
|
-
>
|
|
24
|
-
|
|
25
|
-
export const oauthClientCredentialsSecretPostSchema = z.object({
|
|
26
|
-
client_id: oauthClientIdSchema,
|
|
27
|
-
client_secret: z.string(),
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
export type OAuthClientCredentialsSecretPost = z.infer<
|
|
31
|
-
typeof oauthClientCredentialsSecretPostSchema
|
|
32
|
-
>
|
|
33
|
-
|
|
34
|
-
export const oauthClientCredentialsNoneSchema = z.object({
|
|
35
|
-
client_id: oauthClientIdSchema,
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
export type OAuthClientCredentialsNone = z.infer<
|
|
39
|
-
typeof oauthClientCredentialsNoneSchema
|
|
40
|
-
>
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
export const oauthClientCredentialsSchema = z.union([
|
|
45
|
-
oauthClientCredentialsJwtBearerSchema,
|
|
46
|
-
oauthClientCredentialsSecretPostSchema,
|
|
47
|
-
// Must be last since it is less specific
|
|
48
|
-
oauthClientCredentialsNoneSchema,
|
|
49
|
-
])
|
|
50
|
-
|
|
51
|
-
export type OAuthClientCredentials = z.infer<
|
|
52
|
-
typeof oauthClientCredentialsSchema
|
|
53
|
-
>
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { TypeOf, z } from 'zod'
|
|
2
|
-
import { oauthClientIdSchema } from './oauth-client-id.js'
|
|
3
|
-
import { httpsUriSchema } from './uri.js'
|
|
4
|
-
import { extractUrlPath, isHostnameIP } from './util.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @see {@link https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html}
|
|
8
|
-
*/
|
|
9
|
-
export const oauthClientIdDiscoverableSchema = z
|
|
10
|
-
.intersection(oauthClientIdSchema, httpsUriSchema)
|
|
11
|
-
.superRefine((value, ctx): value is `https://${string}/${string}` => {
|
|
12
|
-
const url = new URL(value)
|
|
13
|
-
|
|
14
|
-
if (url.username || url.password) {
|
|
15
|
-
ctx.addIssue({
|
|
16
|
-
code: z.ZodIssueCode.custom,
|
|
17
|
-
message: 'ClientID must not contain credentials',
|
|
18
|
-
})
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (url.hash) {
|
|
23
|
-
ctx.addIssue({
|
|
24
|
-
code: z.ZodIssueCode.custom,
|
|
25
|
-
message: 'ClientID must not contain a fragment',
|
|
26
|
-
})
|
|
27
|
-
return false
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (url.pathname === '/') {
|
|
31
|
-
ctx.addIssue({
|
|
32
|
-
code: z.ZodIssueCode.custom,
|
|
33
|
-
message:
|
|
34
|
-
'ClientID must contain a path component (e.g. "/client-metadata.json")',
|
|
35
|
-
})
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (url.pathname.endsWith('/')) {
|
|
40
|
-
ctx.addIssue({
|
|
41
|
-
code: z.ZodIssueCode.custom,
|
|
42
|
-
message: 'ClientID path must not end with a trailing slash',
|
|
43
|
-
})
|
|
44
|
-
return false
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (isHostnameIP(url.hostname)) {
|
|
48
|
-
ctx.addIssue({
|
|
49
|
-
code: z.ZodIssueCode.custom,
|
|
50
|
-
message: 'ClientID hostname must not be an IP address',
|
|
51
|
-
})
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// URL constructor normalizes the URL, so we extract the path manually to
|
|
56
|
-
// avoid normalization, then compare it to the normalized path to ensure
|
|
57
|
-
// that the URL does not contain path traversal or other unexpected characters
|
|
58
|
-
if (extractUrlPath(value) !== url.pathname) {
|
|
59
|
-
ctx.addIssue({
|
|
60
|
-
code: z.ZodIssueCode.custom,
|
|
61
|
-
message: `ClientID must be in canonical form ("${url.href}", got "${value}")`,
|
|
62
|
-
})
|
|
63
|
-
return false
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return true
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
export type OAuthClientIdDiscoverable = TypeOf<
|
|
70
|
-
typeof oauthClientIdDiscoverableSchema
|
|
71
|
-
>
|
|
72
|
-
|
|
73
|
-
export function isOAuthClientIdDiscoverable(
|
|
74
|
-
clientId: string,
|
|
75
|
-
): clientId is OAuthClientIdDiscoverable {
|
|
76
|
-
return oauthClientIdDiscoverableSchema.safeParse(clientId).success
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export const conventionalOAuthClientIdSchema =
|
|
80
|
-
oauthClientIdDiscoverableSchema.superRefine(
|
|
81
|
-
(value, ctx): value is `https://${string}/oauth-client-metadata.json` => {
|
|
82
|
-
const url = new URL(value)
|
|
83
|
-
|
|
84
|
-
if (url.port) {
|
|
85
|
-
ctx.addIssue({
|
|
86
|
-
code: z.ZodIssueCode.custom,
|
|
87
|
-
message: 'ClientID must not contain a port',
|
|
88
|
-
})
|
|
89
|
-
return false
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (url.search) {
|
|
93
|
-
ctx.addIssue({
|
|
94
|
-
code: z.ZodIssueCode.custom,
|
|
95
|
-
message: 'ClientID must not contain a query string',
|
|
96
|
-
})
|
|
97
|
-
return false
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (url.pathname !== '/oauth-client-metadata.json') {
|
|
101
|
-
ctx.addIssue({
|
|
102
|
-
code: z.ZodIssueCode.custom,
|
|
103
|
-
message: 'ClientID must be "/oauth-client-metadata.json"',
|
|
104
|
-
})
|
|
105
|
-
return false
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return true
|
|
109
|
-
},
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
export type ConventionalOAuthClientId = TypeOf<
|
|
113
|
-
typeof conventionalOAuthClientIdSchema
|
|
114
|
-
>
|
|
115
|
-
|
|
116
|
-
export function isConventionalOAuthClientId(
|
|
117
|
-
clientId: string,
|
|
118
|
-
): clientId is ConventionalOAuthClientId {
|
|
119
|
-
return conventionalOAuthClientIdSchema.safeParse(clientId).success
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function assertOAuthDiscoverableClientId(
|
|
123
|
-
value: string,
|
|
124
|
-
): asserts value is OAuthClientIdDiscoverable {
|
|
125
|
-
void oauthClientIdDiscoverableSchema.parse(value)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function parseOAuthDiscoverableClientId(clientId: string): URL {
|
|
129
|
-
return new URL(oauthClientIdDiscoverableSchema.parse(clientId))
|
|
130
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { oauthClientIdSchema } from './oauth-client-id.js'
|
|
2
|
-
import {
|
|
3
|
-
OAuthLoopbackRedirectURI,
|
|
4
|
-
oauthLoopbackClientRedirectUriSchema,
|
|
5
|
-
} from './oauth-redirect-uri.js'
|
|
6
|
-
import { OAuthScope, oauthScopeSchema } from './oauth-scope.js'
|
|
7
|
-
|
|
8
|
-
export const LOOPBACK_CLIENT_ID_ORIGIN = 'http://localhost'
|
|
9
|
-
|
|
10
|
-
// @NOTE This is not actually based on a standard, but rather a convention
|
|
11
|
-
// established by Bluesky in the Atproto specs and implementation. As such, and
|
|
12
|
-
// in order to respect the convention from this package, these should be
|
|
13
|
-
// prefixed with "Atproto" instead of "OAuth". For legacy reasons, we keep the
|
|
14
|
-
// current names, but we should rename them in a future major release, unless
|
|
15
|
-
// loopback client ids have since then been standardized.
|
|
16
|
-
|
|
17
|
-
export type OAuthClientIdLoopback =
|
|
18
|
-
`http://localhost${'' | `/`}${'' | `?${string}`}`
|
|
19
|
-
|
|
20
|
-
export type OAuthLoopbackClientIdParams = {
|
|
21
|
-
scope?: OAuthScope
|
|
22
|
-
redirect_uris?: [OAuthLoopbackRedirectURI, ...OAuthLoopbackRedirectURI[]]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const oauthClientIdLoopbackSchema = oauthClientIdSchema.superRefine(
|
|
26
|
-
(input, ctx): input is OAuthClientIdLoopback => {
|
|
27
|
-
const result = safeParseOAuthLoopbackClientId(input)
|
|
28
|
-
if (!result.success) {
|
|
29
|
-
ctx.addIssue({ code: 'custom', message: result.message })
|
|
30
|
-
}
|
|
31
|
-
return result.success
|
|
32
|
-
},
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
export function assertOAuthLoopbackClientId(
|
|
36
|
-
input: string,
|
|
37
|
-
): asserts input is OAuthClientIdLoopback {
|
|
38
|
-
void parseOAuthLoopbackClientId(input)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function isOAuthClientIdLoopback<T extends string>(
|
|
42
|
-
input: T,
|
|
43
|
-
): input is T & OAuthClientIdLoopback {
|
|
44
|
-
return safeParseOAuthLoopbackClientId(input).success
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function asOAuthClientIdLoopback<T extends string>(input: T) {
|
|
48
|
-
assertOAuthLoopbackClientId(input)
|
|
49
|
-
return input
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function parseOAuthLoopbackClientId(
|
|
53
|
-
input: string,
|
|
54
|
-
): OAuthLoopbackClientIdParams {
|
|
55
|
-
const result = safeParseOAuthLoopbackClientId(input)
|
|
56
|
-
if (result.success) return result.value
|
|
57
|
-
|
|
58
|
-
throw new TypeError(`Invalid loopback client ID: ${result.message}`)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Similar to Zod's {@link SafeParseReturnType} but uses a simple "message"
|
|
63
|
-
* string instead of an "error" Error object.
|
|
64
|
-
*/
|
|
65
|
-
type LightParseReturnType<T> =
|
|
66
|
-
| { success: true; value: T }
|
|
67
|
-
| { success: false; message: string }
|
|
68
|
-
|
|
69
|
-
export function safeParseOAuthLoopbackClientId(
|
|
70
|
-
input: string,
|
|
71
|
-
): LightParseReturnType<OAuthLoopbackClientIdParams> {
|
|
72
|
-
// @NOTE Not using "new URL" to ensure input indeed matches the type
|
|
73
|
-
// OAuthClientIdLoopback
|
|
74
|
-
|
|
75
|
-
if (!input.startsWith(LOOPBACK_CLIENT_ID_ORIGIN)) {
|
|
76
|
-
return {
|
|
77
|
-
success: false,
|
|
78
|
-
message: `Value must start with "${LOOPBACK_CLIENT_ID_ORIGIN}"`,
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (input.includes('#', LOOPBACK_CLIENT_ID_ORIGIN.length)) {
|
|
83
|
-
return {
|
|
84
|
-
success: false,
|
|
85
|
-
message: 'Value must not contain a hash component',
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Since we don't allow a path component (except for a single "/") the query
|
|
90
|
-
// string starts after the origin (+ 1 if there is a "/")
|
|
91
|
-
const queryStringIdx =
|
|
92
|
-
input.length > LOOPBACK_CLIENT_ID_ORIGIN.length &&
|
|
93
|
-
input.charCodeAt(LOOPBACK_CLIENT_ID_ORIGIN.length) === 0x2f /* '/' */
|
|
94
|
-
? LOOPBACK_CLIENT_ID_ORIGIN.length + 1
|
|
95
|
-
: LOOPBACK_CLIENT_ID_ORIGIN.length
|
|
96
|
-
|
|
97
|
-
// Since we determined the position of the query string based on the origin
|
|
98
|
-
// length (instead of looking for a "?"), we need to make sure the query
|
|
99
|
-
// string position (if any) indeed starts with a "?".
|
|
100
|
-
if (
|
|
101
|
-
input.length !== queryStringIdx &&
|
|
102
|
-
input.charCodeAt(queryStringIdx) !== 0x3f /* '?' */
|
|
103
|
-
) {
|
|
104
|
-
return {
|
|
105
|
-
success: false,
|
|
106
|
-
message: 'Value must not contain a path component',
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const queryString = input.slice(queryStringIdx + 1)
|
|
111
|
-
return safeParseOAuthLoopbackClientIdQueryString(queryString)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function safeParseOAuthLoopbackClientIdQueryString(
|
|
115
|
-
input: string | Iterable<[key: string, value: string]>,
|
|
116
|
-
): LightParseReturnType<OAuthLoopbackClientIdParams> {
|
|
117
|
-
// Parse query params
|
|
118
|
-
const params: OAuthLoopbackClientIdParams = {}
|
|
119
|
-
|
|
120
|
-
const it = typeof input === 'string' ? new URLSearchParams(input) : input
|
|
121
|
-
for (const [key, value] of it) {
|
|
122
|
-
if (key === 'scope') {
|
|
123
|
-
if ('scope' in params) {
|
|
124
|
-
return {
|
|
125
|
-
success: false,
|
|
126
|
-
message: 'Duplicate "scope" query parameter',
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const res = oauthScopeSchema.safeParse(value)
|
|
131
|
-
if (!res.success) {
|
|
132
|
-
const reason = res.error.issues.map((i) => i.message).join(', ')
|
|
133
|
-
return {
|
|
134
|
-
success: false,
|
|
135
|
-
message: `Invalid "scope" query parameter: ${reason || 'Validation failed'}`,
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
params.scope = res.data
|
|
140
|
-
} else if (key === 'redirect_uri') {
|
|
141
|
-
const res = oauthLoopbackClientRedirectUriSchema.safeParse(value)
|
|
142
|
-
if (!res.success) {
|
|
143
|
-
const reason = res.error.issues.map((i) => i.message).join(', ')
|
|
144
|
-
return {
|
|
145
|
-
success: false,
|
|
146
|
-
message: `Invalid "redirect_uri" query parameter: ${reason || 'Validation failed'}`,
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (params.redirect_uris == null) params.redirect_uris = [res.data]
|
|
151
|
-
else params.redirect_uris.push(res.data)
|
|
152
|
-
} else {
|
|
153
|
-
return {
|
|
154
|
-
success: false,
|
|
155
|
-
message: `Unexpected query parameter "${key}"`,
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
success: true,
|
|
162
|
-
value: params,
|
|
163
|
-
}
|
|
164
|
-
}
|
package/src/oauth-client-id.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { jwksPubSchema } from '@atproto/jwk'
|
|
3
|
-
import { oauthClientIdSchema } from './oauth-client-id.js'
|
|
4
|
-
import { oauthEndpointAuthMethod } from './oauth-endpoint-auth-method.js'
|
|
5
|
-
import { oauthGrantTypeSchema } from './oauth-grant-type.js'
|
|
6
|
-
import { oauthRedirectUriSchema } from './oauth-redirect-uri.js'
|
|
7
|
-
import { oauthResponseTypeSchema } from './oauth-response-type.js'
|
|
8
|
-
import { oauthScopeSchema } from './oauth-scope.js'
|
|
9
|
-
import { webUriSchema } from './uri.js'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @see {@link https://openid.net/specs/openid-connect-registration-1_0.html}
|
|
13
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc7591}
|
|
14
|
-
* @note we do not enforce https: scheme in URIs to support development
|
|
15
|
-
* environments. Make sure to validate the URIs before using it in a production
|
|
16
|
-
* environment.
|
|
17
|
-
*/
|
|
18
|
-
export const oauthClientMetadataSchema = z.object({
|
|
19
|
-
/**
|
|
20
|
-
* @note redirect_uris require additional validation
|
|
21
|
-
*/
|
|
22
|
-
// https://www.rfc-editor.org/rfc/rfc7591.html#section-2
|
|
23
|
-
redirect_uris: z.array(oauthRedirectUriSchema).nonempty(),
|
|
24
|
-
response_types: z
|
|
25
|
-
.array(oauthResponseTypeSchema)
|
|
26
|
-
.nonempty()
|
|
27
|
-
// > If omitted, the default is that the client will use only the "code"
|
|
28
|
-
// > response type.
|
|
29
|
-
.default(['code']),
|
|
30
|
-
grant_types: z
|
|
31
|
-
.array(oauthGrantTypeSchema)
|
|
32
|
-
.nonempty()
|
|
33
|
-
// > If omitted, the default behavior is that the client will use only the
|
|
34
|
-
// > "authorization_code" Grant Type.
|
|
35
|
-
.default(['authorization_code']),
|
|
36
|
-
scope: oauthScopeSchema.optional(),
|
|
37
|
-
// https://www.rfc-editor.org/rfc/rfc7591.html#section-2
|
|
38
|
-
token_endpoint_auth_method: oauthEndpointAuthMethod
|
|
39
|
-
// > If unspecified or omitted, the default is "client_secret_basic" [...].
|
|
40
|
-
.default('client_secret_basic'),
|
|
41
|
-
token_endpoint_auth_signing_alg: z.string().optional(),
|
|
42
|
-
userinfo_signed_response_alg: z.string().optional(),
|
|
43
|
-
userinfo_encrypted_response_alg: z.string().optional(),
|
|
44
|
-
jwks_uri: webUriSchema.optional(),
|
|
45
|
-
jwks: jwksPubSchema.optional(),
|
|
46
|
-
application_type: z.enum(['web', 'native']).default('web'), // default, per spec, is "web"
|
|
47
|
-
subject_type: z.enum(['public', 'pairwise']).default('public'),
|
|
48
|
-
request_object_signing_alg: z.string().optional(),
|
|
49
|
-
id_token_signed_response_alg: z.string().optional(),
|
|
50
|
-
authorization_signed_response_alg: z.string().default('RS256'),
|
|
51
|
-
authorization_encrypted_response_enc: z.enum(['A128CBC-HS256']).optional(),
|
|
52
|
-
authorization_encrypted_response_alg: z.string().optional(),
|
|
53
|
-
client_id: oauthClientIdSchema.optional(),
|
|
54
|
-
client_name: z.string().optional(),
|
|
55
|
-
client_uri: webUriSchema.optional(),
|
|
56
|
-
policy_uri: webUriSchema.optional(),
|
|
57
|
-
tos_uri: webUriSchema.optional(),
|
|
58
|
-
logo_uri: webUriSchema.optional(), // @TODO: allow data: uri ?
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Default Maximum Authentication Age. Specifies that the End-User MUST be
|
|
62
|
-
* actively authenticated if the End-User was authenticated longer ago than
|
|
63
|
-
* the specified number of seconds. The max_age request parameter overrides
|
|
64
|
-
* this default value. If omitted, no default Maximum Authentication Age is
|
|
65
|
-
* specified.
|
|
66
|
-
*/
|
|
67
|
-
default_max_age: z.number().optional(),
|
|
68
|
-
require_auth_time: z.boolean().optional(),
|
|
69
|
-
contacts: z.array(z.string().email()).optional(),
|
|
70
|
-
tls_client_certificate_bound_access_tokens: z.boolean().optional(),
|
|
71
|
-
|
|
72
|
-
// https://datatracker.ietf.org/doc/html/rfc9449#section-5.2
|
|
73
|
-
dpop_bound_access_tokens: z.boolean().optional(),
|
|
74
|
-
|
|
75
|
-
// https://datatracker.ietf.org/doc/html/rfc9396#section-14.5
|
|
76
|
-
authorization_details_types: z.array(z.string()).optional(),
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
export type OAuthClientMetadata = z.infer<typeof oauthClientMetadataSchema>
|
|
80
|
-
export type OAuthClientMetadataInput = z.input<typeof oauthClientMetadataSchema>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
export const oauthEndpointAuthMethod = z.enum([
|
|
4
|
-
'client_secret_basic',
|
|
5
|
-
'client_secret_jwt',
|
|
6
|
-
'client_secret_post',
|
|
7
|
-
'none',
|
|
8
|
-
'private_key_jwt',
|
|
9
|
-
'self_signed_tls_client_auth',
|
|
10
|
-
'tls_client_auth',
|
|
11
|
-
])
|
|
12
|
-
|
|
13
|
-
export type OauthEndpointAuthMethod = z.infer<typeof oauthEndpointAuthMethod>
|
package/src/oauth-grant-type.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
export const oauthGrantTypeSchema = z.enum([
|
|
4
|
-
'authorization_code',
|
|
5
|
-
'implicit',
|
|
6
|
-
'refresh_token',
|
|
7
|
-
'password', // Not part of OAuth 2.1
|
|
8
|
-
'client_credentials',
|
|
9
|
-
'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
10
|
-
'urn:ietf:params:oauth:grant-type:saml2-bearer',
|
|
11
|
-
])
|
|
12
|
-
|
|
13
|
-
export type OAuthGrantType = z.infer<typeof oauthGrantTypeSchema>
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { OAuthAuthorizationDetails } from './oauth-authorization-details.js'
|
|
2
|
-
import { OAuthTokenType } from './oauth-token-type.js'
|
|
3
|
-
|
|
4
|
-
// https://datatracker.ietf.org/doc/html/rfc7662#section-2.2
|
|
5
|
-
export type OAuthIntrospectionResponse =
|
|
6
|
-
| { active: false }
|
|
7
|
-
| {
|
|
8
|
-
active: true
|
|
9
|
-
|
|
10
|
-
scope?: string
|
|
11
|
-
client_id?: string
|
|
12
|
-
username?: string
|
|
13
|
-
token_type?: OAuthTokenType
|
|
14
|
-
authorization_details?: OAuthAuthorizationDetails
|
|
15
|
-
|
|
16
|
-
aud?: string | [string, ...string[]]
|
|
17
|
-
exp?: number
|
|
18
|
-
iat?: number
|
|
19
|
-
iss?: string
|
|
20
|
-
jti?: string
|
|
21
|
-
nbf?: number
|
|
22
|
-
sub?: string
|
|
23
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { webUriSchema } from './uri.js'
|
|
3
|
-
|
|
4
|
-
export const oauthIssuerIdentifierSchema = webUriSchema.superRefine(
|
|
5
|
-
(value, ctx) => {
|
|
6
|
-
// Validate the issuer (MIX-UP attacks)
|
|
7
|
-
|
|
8
|
-
if (value.endsWith('/')) {
|
|
9
|
-
ctx.addIssue({
|
|
10
|
-
code: z.ZodIssueCode.custom,
|
|
11
|
-
message: 'Issuer URL must not end with a slash',
|
|
12
|
-
})
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const url = new URL(value)
|
|
17
|
-
|
|
18
|
-
if (url.username || url.password) {
|
|
19
|
-
ctx.addIssue({
|
|
20
|
-
code: z.ZodIssueCode.custom,
|
|
21
|
-
message: 'Issuer URL must not contain a username or password',
|
|
22
|
-
})
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (url.hash || url.search) {
|
|
27
|
-
ctx.addIssue({
|
|
28
|
-
code: z.ZodIssueCode.custom,
|
|
29
|
-
message: 'Issuer URL must not contain a query or fragment',
|
|
30
|
-
})
|
|
31
|
-
return false
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const canonicalValue = url.pathname === '/' ? url.origin : url.href
|
|
35
|
-
if (value !== canonicalValue) {
|
|
36
|
-
ctx.addIssue({
|
|
37
|
-
code: z.ZodIssueCode.custom,
|
|
38
|
-
message: 'Issuer URL must be in the canonical form',
|
|
39
|
-
})
|
|
40
|
-
return false
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return true
|
|
44
|
-
},
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
export type OAuthIssuerIdentifier = z.infer<typeof oauthIssuerIdentifierSchema>
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
export const oauthPasswordGrantTokenRequestSchema = z.object({
|
|
4
|
-
grant_type: z.literal('password'),
|
|
5
|
-
username: z.string(),
|
|
6
|
-
password: z.string(),
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
export type OAuthPasswordGrantTokenRequest = z.infer<
|
|
10
|
-
typeof oauthPasswordGrantTokenRequestSchema
|
|
11
|
-
>
|
package/src/oauth-prompt-mode.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* - "none" will only be allowed if the user already allowed the client on the same device
|
|
5
|
-
* - "login" will force the user to login again, unless he very recently logged in
|
|
6
|
-
* - "consent" will force the user to consent again
|
|
7
|
-
* - "select_account" will force the user to select an account
|
|
8
|
-
* - "create" will force the user registration screen
|
|
9
|
-
*/
|
|
10
|
-
export const oauthPromptModeSchema = z.enum([
|
|
11
|
-
'none',
|
|
12
|
-
'login',
|
|
13
|
-
'consent',
|
|
14
|
-
'select_account',
|
|
15
|
-
'create',
|
|
16
|
-
])
|
|
17
|
-
|
|
18
|
-
export type OAuthPromptMode = z.infer<typeof oauthPromptModeSchema>
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import { oauthIssuerIdentifierSchema } from './oauth-issuer-identifier.js'
|
|
3
|
-
import { webUriSchema } from './uri.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2}
|
|
7
|
-
*/
|
|
8
|
-
export const oauthProtectedResourceMetadataSchema = z.object({
|
|
9
|
-
/**
|
|
10
|
-
* REQUIRED. The protected resource's resource identifier, which is a URL that
|
|
11
|
-
* uses the https scheme and has no query or fragment components. Using these
|
|
12
|
-
* well-known resources is described in Section 3.
|
|
13
|
-
*
|
|
14
|
-
* @note This schema allows non https URLs for testing & development purposes.
|
|
15
|
-
* Make sure to validate the URL before using it in a production environment.
|
|
16
|
-
*/
|
|
17
|
-
resource: webUriSchema
|
|
18
|
-
.refine((url) => !url.includes('?'), {
|
|
19
|
-
message: 'Resource URL must not contain query parameters',
|
|
20
|
-
})
|
|
21
|
-
.refine((url) => !url.includes('#'), {
|
|
22
|
-
message: 'Resource URL must not contain a fragment',
|
|
23
|
-
}),
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* OPTIONAL. JSON array containing a list of OAuth authorization server issuer
|
|
27
|
-
* identifiers, as defined in [RFC8414], for authorization servers that can be
|
|
28
|
-
* used with this protected resource. Protected resources MAY choose not to
|
|
29
|
-
* advertise some supported authorization servers even when this parameter is
|
|
30
|
-
* used. In some use cases, the set of authorization servers will not be
|
|
31
|
-
* enumerable, in which case this metadata parameter would not be used.
|
|
32
|
-
*/
|
|
33
|
-
authorization_servers: z.array(oauthIssuerIdentifierSchema).optional(),
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* OPTIONAL. URL of the protected resource's JWK Set [JWK] document. This
|
|
37
|
-
* contains public keys belonging to the protected resource, such as signing
|
|
38
|
-
* key(s) that the resource server uses to sign resource responses. This URL
|
|
39
|
-
* MUST use the https scheme. When both signing and encryption keys are made
|
|
40
|
-
* available, a use (public key use) parameter value is REQUIRED for all keys
|
|
41
|
-
* in the referenced JWK Set to indicate each key's intended usage.
|
|
42
|
-
*/
|
|
43
|
-
jwks_uri: webUriSchema.optional(),
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* RECOMMENDED. JSON array containing a list of the OAuth 2.0 [RFC6749] scope
|
|
47
|
-
* values that are used in authorization requests to request access to this
|
|
48
|
-
* protected resource. Protected resources MAY choose not to advertise some
|
|
49
|
-
* scope values supported even when this parameter is used.
|
|
50
|
-
*/
|
|
51
|
-
scopes_supported: z.array(z.string()).optional(),
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* OPTIONAL. JSON array containing a list of the supported methods of sending
|
|
55
|
-
* an OAuth 2.0 Bearer Token [RFC6750] to the protected resource. Defined
|
|
56
|
-
* values are ["header", "body", "query"], corresponding to Sections 2.1, 2.2,
|
|
57
|
-
* and 2.3 of RFC 6750.
|
|
58
|
-
*/
|
|
59
|
-
bearer_methods_supported: z
|
|
60
|
-
.array(z.enum(['header', 'body', 'query']))
|
|
61
|
-
.optional(),
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* OPTIONAL. JSON array containing a list of the JWS [JWS] signing algorithms
|
|
65
|
-
* (alg values) [JWA] supported by the protected resource for signing resource
|
|
66
|
-
* responses, for instance, as described in [FAPI.MessageSigning]. No default
|
|
67
|
-
* algorithms are implied if this entry is omitted. The value none MUST NOT be
|
|
68
|
-
* used.
|
|
69
|
-
*/
|
|
70
|
-
resource_signing_alg_values_supported: z.array(z.string()).optional(),
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* OPTIONAL. URL of a page containing human-readable information that
|
|
74
|
-
* developers might want or need to know when using the protected resource
|
|
75
|
-
*/
|
|
76
|
-
resource_documentation: webUriSchema.optional(),
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* OPTIONAL. URL that the protected resource provides to read about the
|
|
80
|
-
* protected resource's requirements on how the client can use the data
|
|
81
|
-
* provided by the protected resource
|
|
82
|
-
*/
|
|
83
|
-
resource_policy_uri: webUriSchema.optional(),
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* OPTIONAL. URL that the protected resource provides to read about the
|
|
87
|
-
* protected resource's terms of service
|
|
88
|
-
*/
|
|
89
|
-
resource_tos_uri: webUriSchema.optional(),
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
export type OAuthProtectedResourceMetadata = z.infer<
|
|
93
|
-
typeof oauthProtectedResourceMetadataSchema
|
|
94
|
-
>
|