@atproto/oauth-client 0.7.6 → 0.7.8
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 +27 -0
- package/package.json +19 -14
- package/src/constants.ts +0 -4
- package/src/core-js.d.ts +0 -1
- package/src/errors/auth-method-unsatisfiable-error.ts +0 -1
- package/src/errors/token-invalid-error.ts +0 -9
- package/src/errors/token-refresh-error.ts +0 -9
- package/src/errors/token-revoked-error.ts +0 -9
- package/src/fetch-dpop.ts +0 -250
- package/src/identity-resolver.ts +0 -25
- package/src/index.ts +0 -32
- package/src/lock.ts +0 -33
- package/src/oauth-authorization-server-metadata-resolver.ts +0 -120
- package/src/oauth-callback-error.ts +0 -16
- package/src/oauth-client-auth.ts +0 -180
- package/src/oauth-client.ts +0 -541
- package/src/oauth-protected-resource-metadata-resolver.ts +0 -122
- package/src/oauth-resolver-error.ts +0 -21
- package/src/oauth-resolver.ts +0 -177
- package/src/oauth-response-error.ts +0 -33
- package/src/oauth-server-agent.ts +0 -289
- package/src/oauth-server-factory.ts +0 -64
- package/src/oauth-session.ts +0 -170
- package/src/runtime-implementation.ts +0 -25
- package/src/runtime.ts +0 -114
- package/src/session-getter.ts +0 -281
- package/src/state-store.ts +0 -24
- package/src/types.ts +0 -39
- package/src/util.test.ts +0 -83
- package/src/util.ts +0 -81
- package/src/validate-client-metadata.ts +0 -116
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
- package/vitest.config.ts +0 -5
package/src/oauth-client-auth.ts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { Keyset } from '@atproto/jwk'
|
|
2
|
-
import {
|
|
3
|
-
CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
|
4
|
-
OAuthAuthorizationServerMetadata,
|
|
5
|
-
OAuthClientCredentials,
|
|
6
|
-
} from '@atproto/oauth-types'
|
|
7
|
-
import { FALLBACK_ALG } from './constants.js'
|
|
8
|
-
import { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'
|
|
9
|
-
import { Runtime } from './runtime.js'
|
|
10
|
-
import { ClientMetadata } from './types.js'
|
|
11
|
-
import { Awaitable } from './util.js'
|
|
12
|
-
|
|
13
|
-
export type ClientAuthMethod =
|
|
14
|
-
| { method: 'none' }
|
|
15
|
-
| { method: 'private_key_jwt'; kid: string }
|
|
16
|
-
|
|
17
|
-
export function negotiateClientAuthMethod(
|
|
18
|
-
serverMetadata: OAuthAuthorizationServerMetadata,
|
|
19
|
-
clientMetadata: ClientMetadata,
|
|
20
|
-
keyset?: Keyset,
|
|
21
|
-
): ClientAuthMethod {
|
|
22
|
-
const method = clientMetadata.token_endpoint_auth_method
|
|
23
|
-
|
|
24
|
-
// @NOTE ATproto spec requires that AS support both "none" and
|
|
25
|
-
// "private_key_jwt", and that clients use one of the other. The following
|
|
26
|
-
// check ensures that the AS is indeed compliant with this client's
|
|
27
|
-
// configuration.
|
|
28
|
-
const methods = supportedMethods(serverMetadata)
|
|
29
|
-
if (!methods.includes(method)) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`The server does not support "${method}" authentication. Supported methods are: ${methods.join(
|
|
32
|
-
', ',
|
|
33
|
-
)}.`,
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (method === 'private_key_jwt') {
|
|
38
|
-
// Invalid client configuration. This should not happen as
|
|
39
|
-
// "validateClientMetadata" already check this.
|
|
40
|
-
if (!keyset) throw new Error('A keyset is required for private_key_jwt')
|
|
41
|
-
|
|
42
|
-
const alg = supportedAlgs(serverMetadata)
|
|
43
|
-
|
|
44
|
-
// @NOTE we can't use `keyset.findPrivateKey` here because we can't enforce
|
|
45
|
-
// that the returned key contains a "kid". The following implementation is
|
|
46
|
-
// more robust against keysets containing keys without a "kid" property.
|
|
47
|
-
for (const key of keyset.list({ alg, usage: 'sign' })) {
|
|
48
|
-
// Return the first key from the key set that matches the server's
|
|
49
|
-
// supported algorithms.
|
|
50
|
-
if (key.kid) return { method: 'private_key_jwt', kid: key.kid }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
throw new Error(
|
|
54
|
-
alg.includes(FALLBACK_ALG)
|
|
55
|
-
? `Client authentication method "${method}" requires at least one "${FALLBACK_ALG}" signing key with a "kid" property`
|
|
56
|
-
: // AS is not compliant with the ATproto OAuth spec.
|
|
57
|
-
`Authorization server requires "${method}" authentication method, but does not support "${FALLBACK_ALG}" algorithm.`,
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (method === 'none') {
|
|
62
|
-
return { method: 'none' }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
throw new Error(
|
|
66
|
-
`The ATProto OAuth spec requires that client use either "none" or "private_key_jwt" authentication method.` +
|
|
67
|
-
(method === 'client_secret_basic'
|
|
68
|
-
? ' You might want to explicitly set "token_endpoint_auth_method" to one of those values in the client metadata document.'
|
|
69
|
-
: ` You set "${method}" which is not allowed.`),
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export type ClientCredentialsFactory = () => Awaitable<{
|
|
74
|
-
headers?: Record<string, string>
|
|
75
|
-
payload?: OAuthClientCredentials
|
|
76
|
-
}>
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @throws {AuthMethodUnsatisfiableError} if the authentication method is no
|
|
80
|
-
* long usable (either because the AS changed, of because the key is no longer
|
|
81
|
-
* available in the keyset).
|
|
82
|
-
*/
|
|
83
|
-
export function createClientCredentialsFactory(
|
|
84
|
-
authMethod: ClientAuthMethod,
|
|
85
|
-
serverMetadata: OAuthAuthorizationServerMetadata,
|
|
86
|
-
clientMetadata: ClientMetadata,
|
|
87
|
-
runtime: Runtime,
|
|
88
|
-
keyset?: Keyset,
|
|
89
|
-
): ClientCredentialsFactory {
|
|
90
|
-
// Ensure the AS still supports the auth method.
|
|
91
|
-
if (!supportedMethods(serverMetadata).includes(authMethod.method)) {
|
|
92
|
-
throw new AuthMethodUnsatisfiableError(
|
|
93
|
-
`Client authentication method "${authMethod.method}" no longer supported`,
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (authMethod.method === 'none') {
|
|
98
|
-
return () => ({
|
|
99
|
-
payload: {
|
|
100
|
-
client_id: clientMetadata.client_id,
|
|
101
|
-
},
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (authMethod.method === 'private_key_jwt') {
|
|
106
|
-
try {
|
|
107
|
-
// The client used to be a confidential client but no longer has a keyset.
|
|
108
|
-
if (!keyset) throw new Error('A keyset is required for private_key_jwt')
|
|
109
|
-
|
|
110
|
-
// @NOTE throws if no matching key can be found
|
|
111
|
-
const { key, alg } = keyset.findPrivateKey({
|
|
112
|
-
usage: 'sign',
|
|
113
|
-
kid: authMethod.kid,
|
|
114
|
-
alg: supportedAlgs(serverMetadata),
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
// https://www.rfc-editor.org/rfc/rfc7523.html#section-3
|
|
118
|
-
return async () => ({
|
|
119
|
-
payload: {
|
|
120
|
-
client_id: clientMetadata.client_id,
|
|
121
|
-
client_assertion_type: CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
|
122
|
-
client_assertion: await key.createJwt(
|
|
123
|
-
{ alg },
|
|
124
|
-
{
|
|
125
|
-
// > The JWT MUST contain an "iss" (issuer) claim that contains a
|
|
126
|
-
// > unique identifier for the entity that issued the JWT.
|
|
127
|
-
iss: clientMetadata.client_id,
|
|
128
|
-
// > For client authentication, the subject MUST be the
|
|
129
|
-
// > "client_id" of the OAuth client.
|
|
130
|
-
sub: clientMetadata.client_id,
|
|
131
|
-
// > The JWT MUST contain an "aud" (audience) claim containing a value
|
|
132
|
-
// > that identifies the authorization server as an intended audience.
|
|
133
|
-
// > The token endpoint URL of the authorization server MAY be used as a
|
|
134
|
-
// > value for an "aud" element to identify the authorization server as an
|
|
135
|
-
// > intended audience of the JWT.
|
|
136
|
-
aud: serverMetadata.issuer,
|
|
137
|
-
// > The JWT MAY contain a "jti" (JWT ID) claim that provides a
|
|
138
|
-
// > unique identifier for the token.
|
|
139
|
-
jti: await runtime.generateNonce(),
|
|
140
|
-
// > The JWT MAY contain an "iat" (issued at) claim that
|
|
141
|
-
// > identifies the time at which the JWT was issued.
|
|
142
|
-
iat: Math.floor(Date.now() / 1000),
|
|
143
|
-
// > The JWT MUST contain an "exp" (expiration time) claim that
|
|
144
|
-
// > limits the time window during which the JWT can be used.
|
|
145
|
-
exp: Math.floor(Date.now() / 1000) + 60, // 1 minute
|
|
146
|
-
},
|
|
147
|
-
),
|
|
148
|
-
},
|
|
149
|
-
})
|
|
150
|
-
} catch (cause) {
|
|
151
|
-
throw new AuthMethodUnsatisfiableError('Failed to load private key', {
|
|
152
|
-
cause,
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
throw new AuthMethodUnsatisfiableError(
|
|
158
|
-
// @ts-expect-error
|
|
159
|
-
`Unsupported auth method ${authMethod.method}`,
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function supportedMethods(serverMetadata: OAuthAuthorizationServerMetadata) {
|
|
164
|
-
return serverMetadata['token_endpoint_auth_methods_supported']
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function supportedAlgs(serverMetadata: OAuthAuthorizationServerMetadata) {
|
|
168
|
-
return (
|
|
169
|
-
serverMetadata['token_endpoint_auth_signing_alg_values_supported'] ?? [
|
|
170
|
-
// @NOTE If not specified, assume that the server supports the ES256
|
|
171
|
-
// algorithm, as prescribed by the spec:
|
|
172
|
-
//
|
|
173
|
-
// > Clients and Authorization Servers currently must support the ES256
|
|
174
|
-
// > cryptographic system [for client authentication].
|
|
175
|
-
//
|
|
176
|
-
// https://atproto.com/specs/oauth#confidential-client-authentication
|
|
177
|
-
FALLBACK_ALG,
|
|
178
|
-
]
|
|
179
|
-
)
|
|
180
|
-
}
|