@atlasauth/backend 0.14.1 → 0.16.0
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 +42 -0
- package/dist/access-control.d.ts +53 -0
- package/dist/access-control.js +46 -0
- package/dist/access-control.js.map +1 -0
- package/dist/algs.d.ts +7 -0
- package/dist/algs.js +11 -0
- package/dist/algs.js.map +1 -0
- package/dist/botid.d.ts +70 -0
- package/dist/botid.js +88 -0
- package/dist/botid.js.map +1 -0
- package/dist/client/action-gallery.d.ts +215 -0
- package/dist/client/action-gallery.js +66 -0
- package/dist/client/action-gallery.js.map +1 -0
- package/dist/client/actions.d.ts +21 -0
- package/dist/client/actions.js.map +1 -1
- package/dist/client/api-keys.d.ts +49 -4
- package/dist/client/api-keys.js +6 -2
- package/dist/client/api-keys.js.map +1 -1
- package/dist/client/app-roles.d.ts +76 -0
- package/dist/client/app-roles.js +39 -0
- package/dist/client/app-roles.js.map +1 -0
- package/dist/client/attack-protection.d.ts +33 -1
- package/dist/client/attack-protection.js.map +1 -1
- package/dist/client/billing-plans.d.ts +93 -0
- package/dist/client/billing-plans.js +31 -0
- package/dist/client/billing-plans.js.map +1 -1
- package/dist/client/custom-oauth-providers.d.ts +243 -0
- package/dist/client/custom-oauth-providers.js +60 -0
- package/dist/client/custom-oauth-providers.js.map +1 -0
- package/dist/client/email-templates.d.ts +1 -1
- package/dist/client/email-templates.js.map +1 -1
- package/dist/client/index.d.ts +157 -3
- package/dist/client/index.js +11 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/instance-security.d.ts +104 -0
- package/dist/client/instance-security.js +28 -0
- package/dist/client/instance-security.js.map +1 -1
- package/dist/client/notifications.d.ts +1 -1
- package/dist/client/notifications.js.map +1 -1
- package/dist/client/oauth-clients.d.ts +84 -2
- package/dist/client/oauth-clients.js +13 -0
- package/dist/client/oauth-clients.js.map +1 -1
- package/dist/client/referrals.d.ts +1 -1
- package/dist/client/resource-servers.d.ts +34 -5
- package/dist/client/resource-servers.js.map +1 -1
- package/dist/client/risk-based-mfa.d.ts +3 -0
- package/dist/client/risk-based-mfa.js.map +1 -1
- package/dist/client/scim-tokens.d.ts +18 -0
- package/dist/client/scim-tokens.js +4 -0
- package/dist/client/scim-tokens.js.map +1 -1
- package/dist/client/sessions.d.ts +16 -0
- package/dist/client/sessions.js +15 -0
- package/dist/client/sessions.js.map +1 -1
- package/dist/client/sso-connections.d.ts +105 -0
- package/dist/client/sso-connections.js +29 -0
- package/dist/client/sso-connections.js.map +1 -1
- package/dist/client/users.d.ts +40 -4
- package/dist/client/users.js +8 -3
- package/dist/client/users.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/logout-token.d.ts +1 -1
- package/dist/logout-token.js +3 -2
- package/dist/logout-token.js.map +1 -1
- package/dist/oauth-resource.d.ts +225 -0
- package/dist/oauth-resource.js +469 -0
- package/dist/oauth-resource.js.map +1 -0
- package/dist/oidc.d.ts +1 -1
- package/dist/oidc.js +5 -3
- package/dist/oidc.js.map +1 -1
- package/dist/referral-test-calls.d.ts +7 -7
- package/dist/referral-test-calls.js +2 -2
- package/dist/testing.d.ts +6 -4
- package/dist/testing.js +7 -6
- package/dist/testing.js.map +1 -1
- package/dist/verify.d.ts +11 -0
- package/dist/verify.js +15 -1
- package/dist/verify.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { type Jwks } from './jwks-cache';
|
|
2
|
+
/**
|
|
3
|
+
* Protecting YOUR API — or your own MCP server — with access tokens Atlas
|
|
4
|
+
* issues to OAuth clients (authorization code, device, client credentials,
|
|
5
|
+
* token exchange). Atlas is the authorization server; your API is the
|
|
6
|
+
* resource server. This module:
|
|
7
|
+
* - verifies the access token locally against the instance JWKS (cached;
|
|
8
|
+
* no call to Atlas on the hot path): signature, `iss`, `exp`/`nbf`, that it
|
|
9
|
+
* IS an access token, and that its `aud` names your API (RFC 8707);
|
|
10
|
+
* - enforces required scopes;
|
|
11
|
+
* - enforces sender constraint: a DPoP-bound token (`cnf.jkt`) must arrive
|
|
12
|
+
* with a valid DPoP proof for the same key (RFC 9449), and is never accepted
|
|
13
|
+
* as a plain Bearer token;
|
|
14
|
+
* - answers failures with the RFC 6750 / RFC 9728 `WWW-Authenticate`
|
|
15
|
+
* challenge (`resource_metadata`, `insufficient_scope` + the scopes needed),
|
|
16
|
+
* which is exactly what an MCP client needs to discover how to sign in;
|
|
17
|
+
* - builds the RFC 9728 protected-resource metadata document to serve at
|
|
18
|
+
* `/.well-known/oauth-protected-resource` on your API's host.
|
|
19
|
+
* - accepts OPAQUE access tokens too (an API, client or instance set to the
|
|
20
|
+
* `opaque` format): with `introspection` configured, a token that is not a
|
|
21
|
+
* JWT is validated at Atlas's introspection endpoint (RFC 7662) with your
|
|
22
|
+
* API's own client credentials, and the answer is cached briefly;
|
|
23
|
+
* Framework-agnostic: hand it any request with `method`, `url` and `headers`.
|
|
24
|
+
*/
|
|
25
|
+
/** Clock skew tolerated on `exp`/`nbf` and on DPoP proof `iat`. */
|
|
26
|
+
export declare const ACCESS_TOKEN_CLOCK_SKEW_SECONDS = 5;
|
|
27
|
+
/** How old a DPoP proof may be. */
|
|
28
|
+
export declare const DPOP_PROOF_MAX_AGE_SECONDS = 300;
|
|
29
|
+
export interface AccessTokenClaims {
|
|
30
|
+
iss: string;
|
|
31
|
+
sub: string;
|
|
32
|
+
aud?: string | string[];
|
|
33
|
+
exp: number;
|
|
34
|
+
iat?: number;
|
|
35
|
+
scope?: string;
|
|
36
|
+
client_id?: string;
|
|
37
|
+
azp?: string;
|
|
38
|
+
permissions?: string[];
|
|
39
|
+
cnf?: {
|
|
40
|
+
jkt?: string;
|
|
41
|
+
'x5t#S256'?: string;
|
|
42
|
+
};
|
|
43
|
+
token_use?: string;
|
|
44
|
+
[claim: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
export interface ResourceRequestLike {
|
|
47
|
+
method?: string;
|
|
48
|
+
/** Absolute URL, or a path (combined with `resource` origin for the DPoP `htu` check). */
|
|
49
|
+
url?: string;
|
|
50
|
+
headers: Record<string, string | string[] | undefined> | {
|
|
51
|
+
get(name: string): string | null;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Replay guard for DPoP proof `jti`s. The in-memory one is for a single process. */
|
|
55
|
+
export interface DpopReplayStore {
|
|
56
|
+
/** Record `jti` until `expiresAt` (ms); return false when it was already seen. */
|
|
57
|
+
claim(jti: string, expiresAt: number): Promise<boolean> | boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Server-provided DPoP nonces (RFC 9449 §8). `current()` is the nonce the API
|
|
61
|
+
* hands out in the `DPoP-Nonce` response header; `valid()` accepts the nonces
|
|
62
|
+
* still in force. A verifier given a source requires every DPoP proof to carry
|
|
63
|
+
* one, answering `use_dpop_nonce` (with a fresh nonce) otherwise.
|
|
64
|
+
*/
|
|
65
|
+
export interface DpopNonceSource {
|
|
66
|
+
current(): string | Promise<string>;
|
|
67
|
+
valid(nonce: string): boolean | Promise<boolean>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* An in-process nonce source: a random nonce per window; the current and the
|
|
71
|
+
* previous window's nonce are accepted. Use it when one process serves the API
|
|
72
|
+
* (or with sticky routing); behind several processes, use
|
|
73
|
+
* {@link hmacDpopNonceSource} with a shared secret.
|
|
74
|
+
*/
|
|
75
|
+
export declare function memoryDpopNonceSource(options?: {
|
|
76
|
+
windowSeconds?: number;
|
|
77
|
+
now?: () => number;
|
|
78
|
+
}): DpopNonceSource;
|
|
79
|
+
/**
|
|
80
|
+
* A stateless nonce source shared by every process holding the same secret (at
|
|
81
|
+
* least 32 bytes): the nonce for a window is base64url(HMAC-SHA256(secret,
|
|
82
|
+
* "atlas-dpop-nonce:" ‖ uint64-BE(window number))). A nonce is good in the window
|
|
83
|
+
* it was issued in and the next one. The derivation is the same in every Atlas
|
|
84
|
+
* server SDK, so services in different languages sharing the secret agree.
|
|
85
|
+
*/
|
|
86
|
+
export declare function hmacDpopNonceSource(secret: string | Uint8Array, options?: {
|
|
87
|
+
windowSeconds?: number;
|
|
88
|
+
now?: () => number;
|
|
89
|
+
}): DpopNonceSource;
|
|
90
|
+
export declare function memoryDpopReplayStore(max?: number): DpopReplayStore;
|
|
91
|
+
export interface AccessTokenVerifierOptions {
|
|
92
|
+
/** Your Atlas instance's issuer (frontend API origin), e.g. https://clerk.example.com. */
|
|
93
|
+
issuer: string;
|
|
94
|
+
/**
|
|
95
|
+
* Your API's identifier (the Resource Server identifier registered in Atlas,
|
|
96
|
+
* RFC 8707 `resource`). The token's `aud` must contain one of these. Pass
|
|
97
|
+
* `false` only to accept tokens with no audience (userinfo-style tokens).
|
|
98
|
+
*/
|
|
99
|
+
audience: string | readonly string[] | false;
|
|
100
|
+
/** Scopes every request must carry (all of them). */
|
|
101
|
+
requiredScopes?: readonly string[];
|
|
102
|
+
/** The JWKS URL; default `${issuer}/.well-known/jwks.json`. */
|
|
103
|
+
jwksUrl?: string;
|
|
104
|
+
/** A static key set (tests / offline). Mutually exclusive with `jwksUrl`. */
|
|
105
|
+
jwks?: Jwks;
|
|
106
|
+
/**
|
|
107
|
+
* Where your protected-resource metadata lives, advertised in every 401 as
|
|
108
|
+
* `resource_metadata` so an MCP / OAuth client can discover Atlas. Typically
|
|
109
|
+
* `https://api.example.com/.well-known/oauth-protected-resource`.
|
|
110
|
+
*/
|
|
111
|
+
resourceMetadataUrl?: string;
|
|
112
|
+
/** Require a DPoP-bound token on every request (never accept plain Bearer). */
|
|
113
|
+
requireDpop?: boolean;
|
|
114
|
+
/** The public origin of this API, for DPoP `htu` when `request.url` is a path. */
|
|
115
|
+
publicOrigin?: string;
|
|
116
|
+
dpopReplayStore?: DpopReplayStore;
|
|
117
|
+
/**
|
|
118
|
+
* Require a server-provided nonce in every DPoP proof (RFC 9449 §8). A proof
|
|
119
|
+
* without a valid one is refused with `use_dpop_nonce`; every result carries
|
|
120
|
+
* `dpopNonce`, to send as the `DPoP-Nonce` response header (the framework
|
|
121
|
+
* adapters do). See {@link memoryDpopNonceSource} / {@link hmacDpopNonceSource}.
|
|
122
|
+
*/
|
|
123
|
+
dpopNonces?: DpopNonceSource;
|
|
124
|
+
/** `WWW-Authenticate` realm. */
|
|
125
|
+
realm?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Validate opaque access tokens (and, with `introspectJwts`, JWTs too) at
|
|
128
|
+
* Atlas's introspection endpoint. The client must be a confidential client
|
|
129
|
+
* your API lists in its `introspection_client_ids` (or the client the tokens
|
|
130
|
+
* are issued to).
|
|
131
|
+
*/
|
|
132
|
+
introspection?: IntrospectionOptions;
|
|
133
|
+
fetchImpl?: typeof fetch;
|
|
134
|
+
now?: () => number;
|
|
135
|
+
}
|
|
136
|
+
export interface IntrospectionOptions {
|
|
137
|
+
clientId: string;
|
|
138
|
+
clientSecret: string;
|
|
139
|
+
/** Default `${issuer}/oauth2/introspect`. */
|
|
140
|
+
endpoint?: string;
|
|
141
|
+
/** How long an active answer is reused, in seconds (default 60; never past the token's `exp`). */
|
|
142
|
+
cacheTtlSeconds?: number;
|
|
143
|
+
/** How long an inactive answer is reused, in seconds (default 10; 0 disables). */
|
|
144
|
+
negativeCacheTtlSeconds?: number;
|
|
145
|
+
/** Most answers kept (default 10 000). */
|
|
146
|
+
cacheMax?: number;
|
|
147
|
+
/**
|
|
148
|
+
* Also introspect JWT access tokens, so a revoked grant or a banned user is
|
|
149
|
+
* refused before the token expires (one cached call per token). Default false:
|
|
150
|
+
* JWTs are verified locally.
|
|
151
|
+
*/
|
|
152
|
+
introspectJwts?: boolean;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* RFC 7662 introspection with a small TTL cache keyed by the token's SHA-256
|
|
156
|
+
* (the token itself is never kept). Returns the claims of an active token, or
|
|
157
|
+
* null for an inactive one; throws when Atlas could not be reached.
|
|
158
|
+
*/
|
|
159
|
+
export declare function createIntrospector(issuer: string, options: IntrospectionOptions, deps?: {
|
|
160
|
+
fetchImpl?: typeof fetch;
|
|
161
|
+
now?: () => number;
|
|
162
|
+
}): (token: string) => Promise<AccessTokenClaims | null>;
|
|
163
|
+
export type AccessTokenErrorCode = 'invalid_request' | 'invalid_token' | 'insufficient_scope' | 'invalid_dpop_proof' | 'use_dpop_nonce';
|
|
164
|
+
export type AccessTokenResult = {
|
|
165
|
+
ok: true;
|
|
166
|
+
claims: AccessTokenClaims;
|
|
167
|
+
scopes: string[];
|
|
168
|
+
subject: string;
|
|
169
|
+
clientId: string | null;
|
|
170
|
+
dpopBound: boolean;
|
|
171
|
+
/** With `dpopNonces`: send as the `DPoP-Nonce` response header. */
|
|
172
|
+
dpopNonce?: string;
|
|
173
|
+
} | {
|
|
174
|
+
ok: false;
|
|
175
|
+
status: 400 | 401 | 403;
|
|
176
|
+
error: AccessTokenErrorCode | null;
|
|
177
|
+
description: string;
|
|
178
|
+
/** Set this response header verbatim. */
|
|
179
|
+
wwwAuthenticate: string;
|
|
180
|
+
/** With `dpopNonces`: send as the `DPoP-Nonce` response header (always, on `use_dpop_nonce`). */
|
|
181
|
+
dpopNonce?: string;
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Build an RFC 6750 / RFC 9449 / RFC 9728 `WWW-Authenticate` challenge.
|
|
185
|
+
* Without an `error`, it is the bare "authentication required" challenge
|
|
186
|
+
* (no token was sent), as RFC 6750 §3.1 asks.
|
|
187
|
+
*/
|
|
188
|
+
export declare function buildWwwAuthenticate(opts: {
|
|
189
|
+
scheme?: 'Bearer' | 'DPoP';
|
|
190
|
+
realm?: string;
|
|
191
|
+
error?: AccessTokenErrorCode | null;
|
|
192
|
+
description?: string;
|
|
193
|
+
scope?: readonly string[];
|
|
194
|
+
resourceMetadataUrl?: string;
|
|
195
|
+
dpopAlgs?: readonly string[];
|
|
196
|
+
}): string;
|
|
197
|
+
/**
|
|
198
|
+
* A reusable verifier (one JWKS cache for the life of the process). Call it per
|
|
199
|
+
* request; it never throws for a bad token — it returns the status + challenge.
|
|
200
|
+
*/
|
|
201
|
+
export declare function createAccessTokenVerifier(options: AccessTokenVerifierOptions): (request: ResourceRequestLike, extra?: {
|
|
202
|
+
requiredScopes?: readonly string[];
|
|
203
|
+
}) => Promise<AccessTokenResult>;
|
|
204
|
+
/** One-shot form of {@link createAccessTokenVerifier} (builds a fresh JWKS cache each call — prefer the factory). */
|
|
205
|
+
export declare function verifyAccessToken(request: ResourceRequestLike, options: AccessTokenVerifierOptions): Promise<AccessTokenResult>;
|
|
206
|
+
/**
|
|
207
|
+
* The RFC 9728 protected-resource metadata document for your API. Serve it as
|
|
208
|
+
* JSON at `https://<your api host>/.well-known/oauth-protected-resource` (and,
|
|
209
|
+
* for a resource with a path, at `/.well-known/oauth-protected-resource<path>`),
|
|
210
|
+
* and point `resourceMetadataUrl` at it. Atlas can also serve this document for
|
|
211
|
+
* you when the API is registered with metadata publishing turned on.
|
|
212
|
+
*/
|
|
213
|
+
export declare function protectedResourceMetadata(opts: {
|
|
214
|
+
/** Your API's identifier (the same value as the verifier's `audience`). */
|
|
215
|
+
resource: string;
|
|
216
|
+
/** Your Atlas instance issuer. */
|
|
217
|
+
issuer: string;
|
|
218
|
+
scopesSupported?: readonly string[];
|
|
219
|
+
resourceName?: string;
|
|
220
|
+
resourceDocumentation?: string;
|
|
221
|
+
/** Advertise that tokens must be DPoP-bound. */
|
|
222
|
+
dpopBoundAccessTokensRequired?: boolean;
|
|
223
|
+
}): Record<string, unknown>;
|
|
224
|
+
/** The RFC 9728 well-known path for a resource identifier (path-inserted form). */
|
|
225
|
+
export declare function protectedResourceMetadataPath(resource: string): string;
|