@rakomi/node 0.0.0 → 0.1.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -1
  3. package/SECURITY.md +206 -0
  4. package/dist/agents.d.ts +90 -0
  5. package/dist/agents.js +203 -0
  6. package/dist/anonymous.d.ts +50 -0
  7. package/dist/anonymous.js +105 -0
  8. package/dist/ciba.d.ts +97 -0
  9. package/dist/ciba.js +282 -0
  10. package/dist/client.d.ts +93 -0
  11. package/dist/client.js +202 -0
  12. package/dist/credentials.d.ts +87 -0
  13. package/dist/credentials.js +104 -0
  14. package/dist/device.d.ts +76 -0
  15. package/dist/device.js +244 -0
  16. package/dist/doctor.d.ts +11 -0
  17. package/dist/doctor.js +135 -0
  18. package/dist/dpop-session.d.ts +90 -0
  19. package/dist/dpop-session.js +127 -0
  20. package/dist/dpop.d.ts +24 -0
  21. package/dist/dpop.js +51 -0
  22. package/dist/env-detect.d.ts +11 -0
  23. package/dist/env-detect.js +26 -0
  24. package/dist/errors.d.ts +307 -0
  25. package/dist/errors.js +385 -0
  26. package/dist/eudi.d.ts +23 -0
  27. package/dist/eudi.js +27 -0
  28. package/dist/flags.d.ts +50 -0
  29. package/dist/flags.js +173 -0
  30. package/dist/guards.d.ts +16 -0
  31. package/dist/guards.js +104 -0
  32. package/dist/index.d.ts +30 -0
  33. package/dist/index.js +18 -0
  34. package/dist/internal/canonical-url.d.ts +13 -0
  35. package/dist/internal/canonical-url.js +52 -0
  36. package/dist/internal/shared-constants.d.ts +3 -0
  37. package/dist/internal/shared-constants.js +3 -0
  38. package/dist/jwks-cache.d.ts +31 -0
  39. package/dist/jwks-cache.js +135 -0
  40. package/dist/link.d.ts +73 -0
  41. package/dist/link.js +262 -0
  42. package/dist/middleware.d.ts +21 -0
  43. package/dist/middleware.js +84 -0
  44. package/dist/oauth.d.ts +46 -0
  45. package/dist/oauth.js +457 -0
  46. package/dist/rbac.d.ts +12 -0
  47. package/dist/rbac.js +20 -0
  48. package/dist/token-exchange.d.ts +65 -0
  49. package/dist/token-exchange.js +163 -0
  50. package/dist/types.d.ts +436 -0
  51. package/dist/types.js +1 -0
  52. package/dist/verify-publisher-webhook.d.ts +25 -0
  53. package/dist/verify-publisher-webhook.js +47 -0
  54. package/dist/verify-token.d.ts +3 -0
  55. package/dist/verify-token.js +148 -0
  56. package/dist/verify-webhook.d.ts +7 -0
  57. package/dist/verify-webhook.js +101 -0
  58. package/package.json +61 -5
  59. package/sbom.cdx.json +52 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Node SDK surface for anonymous sign-ins.
3
+ *
4
+ * Pure server-side: no browser globals (window/document/navigator/localStorage).
5
+ * Browser apps should use the React hook `useAnonymousSignin` from `@rakomi/react`
6
+ * which calls this under the hood via the user's backend, OR hit `/v1/auth/anonymous`
7
+ * directly from the browser with the tenant's public API key.
8
+ *
9
+ * Returns a Result shape consistent with the rest of the SDK: NEVER throws on
10
+ * expected API errors (403/429/402/401), throws ONLY on programmer errors.
11
+ */
12
+ import { RakomiError } from './errors.js';
13
+ import { ANONYMOUS_DISABLED, ANONYMOUS_MAU_EXHAUSTED, ANONYMOUS_NETWORK_ERROR, ANONYMOUS_RATE_LIMITED, AnonymousSessionExpiredError, } from './errors.js';
14
+ /**
15
+ * POST /v1/auth/anonymous. Returns Result — never throws on 4xx.
16
+ */
17
+ export async function anonymousSignin(ctx, options = {}) {
18
+ const fetchImpl = ctx.fetchImpl ?? fetch;
19
+ const url = `${ctx.baseUrl}/v1/auth/anonymous`;
20
+ let res;
21
+ try {
22
+ res = await fetchImpl(url, {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ 'X-API-Key': ctx.apiKey,
27
+ },
28
+ body: JSON.stringify(options.publicMetadata ? { public_metadata: options.publicMetadata } : {}),
29
+ });
30
+ }
31
+ catch (err) {
32
+ return {
33
+ ok: false,
34
+ error: ANONYMOUS_NETWORK_ERROR(err instanceof Error ? err.message : undefined),
35
+ };
36
+ }
37
+ if (res.status === 201) {
38
+ const body = (await res.json());
39
+ return {
40
+ ok: true,
41
+ data: {
42
+ accessToken: body.access_token,
43
+ refreshToken: body.refresh_token,
44
+ expiresIn: body.expires_in,
45
+ user: {
46
+ id: body.user.id,
47
+ isAnonymous: true,
48
+ createdAt: body.user.created_at,
49
+ },
50
+ },
51
+ };
52
+ }
53
+ let retryAfterSeconds;
54
+ const retryAfter = res.headers.get('retry-after');
55
+ if (retryAfter) {
56
+ const n = Number(retryAfter);
57
+ if (Number.isFinite(n) && n > 0)
58
+ retryAfterSeconds = Math.round(n);
59
+ }
60
+ let sdkError;
61
+ switch (res.status) {
62
+ case 403:
63
+ sdkError = ANONYMOUS_DISABLED();
64
+ break;
65
+ case 402:
66
+ sdkError = ANONYMOUS_MAU_EXHAUSTED();
67
+ break;
68
+ case 429:
69
+ sdkError = ANONYMOUS_RATE_LIMITED(retryAfterSeconds);
70
+ break;
71
+ default: {
72
+ const body = await res.text().catch(() => '');
73
+ sdkError = ANONYMOUS_NETWORK_ERROR(body.slice(0, 200));
74
+ }
75
+ }
76
+ return { ok: false, error: sdkError };
77
+ }
78
+ /**
79
+ * Decode an access token's `is_anonymous` claim WITHOUT verifying the signature.
80
+ * Used only to gate the `AnonymousSessionExpiredError` path — callers should still
81
+ * verify any trust-sensitive claim via `client.verifyToken()`.
82
+ */
83
+ export function isAnonymousTokenHeuristic(accessToken) {
84
+ try {
85
+ const parts = accessToken.split('.');
86
+ if (parts.length !== 3)
87
+ return false;
88
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
89
+ return payload.is_anonymous === true;
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ /**
96
+ * Classify a refresh failure: if the prior token was anonymous and the refresh
97
+ * returned 401, throw `AnonymousSessionExpiredError`. Otherwise the caller
98
+ * decides its own UX routing.
99
+ */
100
+ export function maybeThrowAnonymousExpired(priorAccessToken, refreshStatus) {
101
+ if (refreshStatus === 401 && priorAccessToken && isAnonymousTokenHeuristic(priorAccessToken)) {
102
+ throw new AnonymousSessionExpiredError();
103
+ }
104
+ }
105
+ export { AnonymousSessionExpiredError, RakomiError };
package/dist/ciba.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ import type { VerifyResult } from './types.js';
2
+ export interface CibaInitiateOptions {
3
+ /** Space-delimited or array-of-strings scopes. MUST include `openid`. */
4
+ scope: string | string[];
5
+ /** Email or user UUID identifying the human approver. */
6
+ loginHint: string;
7
+ /** Human-readable description of the action being authorized. ≤256 chars. */
8
+ bindingMessage: string;
9
+ /** Optional bound expiry (60–600s). Server default 120s. */
10
+ requestedExpiry?: number;
11
+ /** Optional IETF BCP 47 locale tag (e.g. `pl-PL`). */
12
+ locale?: string;
13
+ }
14
+ export interface CibaInitiateResponse {
15
+ authReqId: string;
16
+ expiresIn: number;
17
+ interval: number;
18
+ }
19
+ export interface CibaPollResponse {
20
+ accessToken: string;
21
+ tokenType: 'Bearer';
22
+ expiresIn: number;
23
+ scope: string;
24
+ idToken?: string;
25
+ }
26
+ export declare class CibaError extends Error {
27
+ readonly code: string;
28
+ readonly description: string;
29
+ constructor(code: string, description: string);
30
+ }
31
+ export declare class CibaAuthorizationPendingError extends CibaError {
32
+ constructor(d?: string);
33
+ }
34
+ export declare class CibaSlowDownError extends CibaError {
35
+ constructor(d?: string);
36
+ }
37
+ export declare class CibaAccessDeniedError extends CibaError {
38
+ constructor(d?: string);
39
+ }
40
+ export declare class CibaExpiredTokenError extends CibaError {
41
+ constructor(d?: string);
42
+ }
43
+ export declare class CibaReplayError extends CibaError {
44
+ constructor(d: string);
45
+ }
46
+ export declare class CibaInvalidClientError extends CibaError {
47
+ constructor(d: string);
48
+ }
49
+ export declare class CibaInvalidScopeError extends CibaError {
50
+ constructor(d: string);
51
+ }
52
+ export declare class CibaUnauthorizedClientError extends CibaError {
53
+ constructor(d: string);
54
+ }
55
+ export declare class CibaUnknownUserError extends CibaError {
56
+ constructor(d: string);
57
+ }
58
+ export declare class CibaUserCapReachedError extends CibaError {
59
+ constructor(d: string);
60
+ }
61
+ export declare class CibaInvalidRequestError extends CibaError {
62
+ constructor(d: string);
63
+ }
64
+ interface CibaContext {
65
+ baseUrl: string;
66
+ clientId: string;
67
+ clientSecret: string;
68
+ }
69
+ /**
70
+ * Initiate a CIBA authentication request via POST /oauth/bc-authorize.
71
+ *
72
+ * SSRF hardening: `redirect: 'error'` on fetch.
73
+ */
74
+ export declare function initiateCiba(ctx: CibaContext, options: CibaInitiateOptions): Promise<VerifyResult<CibaInitiateResponse>>;
75
+ /**
76
+ * Poll for CIBA approval via POST /oauth/token grant=urn:openid:params:grant-type:ciba.
77
+ *
78
+ * Returns Result so the SDK never throws on known API failures. Caller can
79
+ * branch on `result.error.code` (`ciba/authorization_pending` etc.) — the
80
+ * canonical mapping mirrors OIDC CIBA Core §11.
81
+ */
82
+ export declare function pollCiba(ctx: CibaContext, authReqId: string): Promise<VerifyResult<CibaPollResponse>>;
83
+ export interface CibaAwaitDecisionOptions {
84
+ authReqId: string;
85
+ /** Initial polling interval (ms). Server default = 5000. */
86
+ intervalMs?: number;
87
+ /** Optional AbortSignal — used to cancel the poll loop early. */
88
+ signal?: AbortSignal;
89
+ }
90
+ /**
91
+ * Poll loop. Resolves on token issuance; rejects on terminal status
92
+ * (denied / expired / replay / abort). On `slow_down`, doubles the interval
93
+ * up to 60s. SDK-managed in-flight guard — never issues two
94
+ * concurrent polls for the same `authReqId`.
95
+ */
96
+ export declare function awaitCibaDecision(ctx: CibaContext, options: CibaAwaitDecisionOptions): Promise<CibaPollResponse>;
97
+ export {};
package/dist/ciba.js ADDED
@@ -0,0 +1,282 @@
1
+ /**
2
+ * OIDC CIBA Core 1.0 (asynchronous user consent) helper.
3
+ *
4
+ * Three-step API for AI agents that need user approval out-of-band:
5
+ * - `initiate(options)` → POST /oauth/bc-authorize. Returns `auth_req_id`.
6
+ * - `poll(authReqId)` → POST /oauth/token grant=urn:openid:params:grant-type:ciba.
7
+ * - `awaitDecision(options)`→ poll-loop with adaptive interval; resolves on
8
+ * approve / deny / expiry / AbortSignal.
9
+ *
10
+ * Confidential-only: the SDK requires a non-empty `clientSecret` on the
11
+ * RakomiClient config. Browser/edge runtimes that cannot keep a secret MUST
12
+ * use the device-grant flow instead.
13
+ */
14
+ import { CIBA_GRANT_TYPE } from './internal/shared-constants.js';
15
+ export class CibaError extends Error {
16
+ code;
17
+ description;
18
+ constructor(code, description) {
19
+ super(`${code}: ${description}`);
20
+ this.code = code;
21
+ this.description = description;
22
+ }
23
+ }
24
+ export class CibaAuthorizationPendingError extends CibaError {
25
+ constructor(d = 'authorization_pending') { super('authorization_pending', d); }
26
+ }
27
+ export class CibaSlowDownError extends CibaError {
28
+ constructor(d = 'slow_down') { super('slow_down', d); }
29
+ }
30
+ export class CibaAccessDeniedError extends CibaError {
31
+ constructor(d = 'access_denied') { super('access_denied', d); }
32
+ }
33
+ export class CibaExpiredTokenError extends CibaError {
34
+ constructor(d = 'expired_token') { super('expired_token', d); }
35
+ }
36
+ export class CibaReplayError extends CibaError {
37
+ constructor(d) { super('invalid_grant', d); }
38
+ }
39
+ export class CibaInvalidClientError extends CibaError {
40
+ constructor(d) { super('invalid_client', d); }
41
+ }
42
+ export class CibaInvalidScopeError extends CibaError {
43
+ constructor(d) { super('invalid_scope', d); }
44
+ }
45
+ export class CibaUnauthorizedClientError extends CibaError {
46
+ constructor(d) { super('unauthorized_client', d); }
47
+ }
48
+ export class CibaUnknownUserError extends CibaError {
49
+ constructor(d) { super('unknown_user_id', d); }
50
+ }
51
+ export class CibaUserCapReachedError extends CibaError {
52
+ constructor(d) { super('user_cap_reached', d); }
53
+ }
54
+ export class CibaInvalidRequestError extends CibaError {
55
+ constructor(d) { super('invalid_request', d); }
56
+ }
57
+ function basicAuth(clientId, secret) {
58
+ const raw = `${clientId}:${secret}`;
59
+ if (typeof Buffer !== 'undefined')
60
+ return `Basic ${Buffer.from(raw).toString('base64')}`;
61
+ return `Basic ${btoa(raw)}`;
62
+ }
63
+ function makeError(code, description) {
64
+ return {
65
+ code: `ciba/${code}`,
66
+ message: description,
67
+ suggestion: code === 'unknown_user_id'
68
+ ? 'Verify the login_hint matches a real user in this tenant.'
69
+ : code === 'authorization_pending'
70
+ ? 'Continue polling at the interval the server returned.'
71
+ : code === 'slow_down'
72
+ ? 'Server-mandated back-off — increase polling interval by 5s.'
73
+ : code === 'invalid_scope'
74
+ ? 'Requested scope is empty after intersection. Reduce the scope set or extend the client allowlist.'
75
+ : 'See description; consult Starlight docs for CIBA grant.',
76
+ docs_url: 'https://docs.rakomi.dev/oauth/ciba',
77
+ };
78
+ }
79
+ /**
80
+ * Initiate a CIBA authentication request via POST /oauth/bc-authorize.
81
+ *
82
+ * SSRF hardening: `redirect: 'error'` on fetch.
83
+ */
84
+ export async function initiateCiba(ctx, options) {
85
+ const params = new URLSearchParams();
86
+ params.set('client_id', ctx.clientId);
87
+ const scopeStr = Array.isArray(options.scope) ? options.scope.join(' ') : options.scope;
88
+ params.set('scope', scopeStr);
89
+ params.set('login_hint', options.loginHint);
90
+ params.set('binding_message', options.bindingMessage);
91
+ if (options.requestedExpiry !== undefined) {
92
+ params.set('requested_expiry', String(options.requestedExpiry));
93
+ }
94
+ if (options.locale !== undefined) {
95
+ params.set('binding_message_locale', options.locale);
96
+ }
97
+ let res;
98
+ try {
99
+ res = await fetch(`${ctx.baseUrl}/oauth/bc-authorize`, {
100
+ method: 'POST',
101
+ redirect: 'error',
102
+ headers: {
103
+ 'Content-Type': 'application/x-www-form-urlencoded',
104
+ Authorization: basicAuth(ctx.clientId, ctx.clientSecret),
105
+ },
106
+ body: params.toString(),
107
+ });
108
+ }
109
+ catch (err) {
110
+ return {
111
+ ok: false,
112
+ error: {
113
+ code: 'ciba/network_error',
114
+ message: err?.message ?? 'Network error',
115
+ suggestion: 'Verify Rakomi base URL is reachable and that DNS / TLS is healthy.',
116
+ docs_url: 'https://docs.rakomi.dev/oauth/ciba',
117
+ },
118
+ };
119
+ }
120
+ let body = {};
121
+ try {
122
+ body = (await res.json());
123
+ }
124
+ catch {
125
+ }
126
+ if (res.ok) {
127
+ const authReqId = body.auth_req_id;
128
+ const expiresIn = body.expires_in;
129
+ const interval = body.interval;
130
+ if (typeof authReqId !== 'string' ||
131
+ typeof expiresIn !== 'number' ||
132
+ typeof interval !== 'number') {
133
+ return {
134
+ ok: false,
135
+ error: {
136
+ code: 'ciba/malformed_response',
137
+ message: 'Server returned 200 with a body that does not match OIDC CIBA Core §7.3 shape',
138
+ suggestion: 'Server-side bug — file an issue.',
139
+ docs_url: 'https://docs.rakomi.dev/oauth/ciba',
140
+ },
141
+ };
142
+ }
143
+ return { ok: true, data: { authReqId, expiresIn, interval } };
144
+ }
145
+ const code = body.error ?? `http_${res.status}`;
146
+ const description = body.error_description ?? `HTTP ${res.status}`;
147
+ return { ok: false, error: makeError(code, description) };
148
+ }
149
+ /**
150
+ * Poll for CIBA approval via POST /oauth/token grant=urn:openid:params:grant-type:ciba.
151
+ *
152
+ * Returns Result so the SDK never throws on known API failures. Caller can
153
+ * branch on `result.error.code` (`ciba/authorization_pending` etc.) — the
154
+ * canonical mapping mirrors OIDC CIBA Core §11.
155
+ */
156
+ export async function pollCiba(ctx, authReqId) {
157
+ const params = new URLSearchParams();
158
+ params.set('grant_type', CIBA_GRANT_TYPE);
159
+ params.set('auth_req_id', authReqId);
160
+ let res;
161
+ try {
162
+ res = await fetch(`${ctx.baseUrl}/oauth/token`, {
163
+ method: 'POST',
164
+ redirect: 'error',
165
+ headers: {
166
+ 'Content-Type': 'application/x-www-form-urlencoded',
167
+ Authorization: basicAuth(ctx.clientId, ctx.clientSecret),
168
+ },
169
+ body: params.toString(),
170
+ });
171
+ }
172
+ catch (err) {
173
+ return {
174
+ ok: false,
175
+ error: {
176
+ code: 'ciba/network_error',
177
+ message: err?.message ?? 'Network error',
178
+ suggestion: 'Verify Rakomi base URL is reachable.',
179
+ docs_url: 'https://docs.rakomi.dev/oauth/ciba',
180
+ },
181
+ };
182
+ }
183
+ let body = {};
184
+ try {
185
+ body = (await res.json());
186
+ }
187
+ catch {
188
+ }
189
+ if (res.ok) {
190
+ const accessToken = body.access_token;
191
+ const tokenType = body.token_type;
192
+ const expiresIn = body.expires_in;
193
+ const scope = body.scope;
194
+ const idToken = body.id_token;
195
+ if (typeof accessToken !== 'string' ||
196
+ tokenType !== 'Bearer' ||
197
+ typeof expiresIn !== 'number' ||
198
+ typeof scope !== 'string') {
199
+ return {
200
+ ok: false,
201
+ error: {
202
+ code: 'ciba/malformed_response',
203
+ message: 'Server returned 200 with body that does not match the OAuth token-response shape',
204
+ suggestion: 'Server-side bug — file an issue.',
205
+ docs_url: 'https://docs.rakomi.dev/oauth/ciba',
206
+ },
207
+ };
208
+ }
209
+ const out = {
210
+ accessToken,
211
+ tokenType: 'Bearer',
212
+ expiresIn,
213
+ scope,
214
+ ...(idToken ? { idToken } : {}),
215
+ };
216
+ return { ok: true, data: out };
217
+ }
218
+ const code = body.error ?? `http_${res.status}`;
219
+ const description = body.error_description ?? `HTTP ${res.status}`;
220
+ return { ok: false, error: makeError(code, description) };
221
+ }
222
+ /**
223
+ * Poll loop. Resolves on token issuance; rejects on terminal status
224
+ * (denied / expired / replay / abort). On `slow_down`, doubles the interval
225
+ * up to 60s. SDK-managed in-flight guard — never issues two
226
+ * concurrent polls for the same `authReqId`.
227
+ */
228
+ export async function awaitCibaDecision(ctx, options) {
229
+ let interval = Math.max(options.intervalMs ?? 5000, 1000);
230
+ const maxInterval = 60_000;
231
+ while (true) {
232
+ if (options.signal?.aborted) {
233
+ throw new CibaError('aborted', 'CIBA poll aborted');
234
+ }
235
+ const result = await pollCiba(ctx, options.authReqId);
236
+ if (result.ok)
237
+ return result.data;
238
+ const code = result.error.code.replace(/^ciba\//, '');
239
+ switch (code) {
240
+ case 'authorization_pending':
241
+ await sleep(interval, options.signal);
242
+ continue;
243
+ case 'slow_down':
244
+ interval = Math.min(interval + 5000, maxInterval);
245
+ await sleep(interval, options.signal);
246
+ continue;
247
+ case 'access_denied':
248
+ throw new CibaAccessDeniedError(result.error.message);
249
+ case 'expired_token':
250
+ throw new CibaExpiredTokenError(result.error.message);
251
+ case 'invalid_grant':
252
+ throw new CibaReplayError(result.error.message);
253
+ case 'invalid_scope':
254
+ throw new CibaInvalidScopeError(result.error.message);
255
+ case 'unauthorized_client':
256
+ throw new CibaUnauthorizedClientError(result.error.message);
257
+ case 'invalid_client':
258
+ throw new CibaInvalidClientError(result.error.message);
259
+ case 'invalid_request':
260
+ throw new CibaInvalidRequestError(result.error.message);
261
+ default:
262
+ throw new CibaError(code, result.error.message);
263
+ }
264
+ }
265
+ }
266
+ function sleep(ms, signal) {
267
+ return new Promise((resolve, reject) => {
268
+ if (signal?.aborted) {
269
+ reject(new CibaError('aborted', 'CIBA poll aborted'));
270
+ return;
271
+ }
272
+ const timer = setTimeout(() => {
273
+ signal?.removeEventListener('abort', onAbort);
274
+ resolve();
275
+ }, ms);
276
+ const onAbort = () => {
277
+ clearTimeout(timer);
278
+ reject(new CibaError('aborted', 'CIBA poll aborted'));
279
+ };
280
+ signal?.addEventListener('abort', onAbort, { once: true });
281
+ });
282
+ }
@@ -0,0 +1,93 @@
1
+ import { AgentsClient } from './agents.js';
2
+ import { type AnonymousSigninOptions, type AnonymousSigninResult } from './anonymous.js';
3
+ import { type CibaAwaitDecisionOptions, type CibaInitiateOptions, type CibaInitiateResponse, type CibaPollResponse } from './ciba.js';
4
+ import { CredentialsClient } from './credentials.js';
5
+ import { FlagsClient } from './flags.js';
6
+ import { LinkClient } from './link.js';
7
+ import type { MiddlewareRequest, MiddlewareResponse, NextFunction } from './middleware.js';
8
+ import { type TokenExchangeOptions, type TokenExchangeResponse } from './token-exchange.js';
9
+ import type { AuthorizeUrlOptions, MiddlewareOptions, OAuthExchangeOptions, OAuthRefreshOptions, OAuthTokenResponse, PkceChallenge, RakomiConfig, TokenPayload, VerifyResult, WebhookEvent, WebhookVerifyData } from './types.js';
10
+ export declare class RakomiClient {
11
+ private readonly apiKey;
12
+ private readonly baseUrl;
13
+ private readonly clockTolerance;
14
+ private readonly environment?;
15
+ private readonly webhookSecret?;
16
+ private readonly webhookTolerance;
17
+ private readonly clientId?;
18
+ private readonly clientSecret?;
19
+ private jwksCache;
20
+ readonly flags: FlagsClient;
21
+ /** Verifiable Credentials issuance (tenant server-to-server). */
22
+ readonly credentials: CredentialsClient;
23
+ /** c: user-scoped account-linking resource. Requires end-user JWT per call. */
24
+ readonly link: LinkClient;
25
+ /** end-user agent management (`users.me.agents.list/.revoke`). Requires end-user JWT per call. */
26
+ readonly users: {
27
+ readonly me: {
28
+ readonly agents: AgentsClient;
29
+ };
30
+ };
31
+ constructor(config: RakomiConfig);
32
+ verifyToken<T extends TokenPayload = TokenPayload>(token: string): Promise<VerifyResult<T>>;
33
+ verifyWebhook<T = WebhookEvent>(body: string | Buffer, headers: Record<string, string | string[] | undefined>, options?: {
34
+ tolerance?: number;
35
+ }): Promise<VerifyResult<WebhookVerifyData<T>>>;
36
+ middleware(options?: MiddlewareOptions): (req: MiddlewareRequest, res: MiddlewareResponse, next: NextFunction) => void;
37
+ generatePKCE(): PkceChallenge;
38
+ generateState(): string;
39
+ buildAuthorizeUrl(options: Omit<AuthorizeUrlOptions, 'clientId'> & {
40
+ clientId?: string;
41
+ }): string;
42
+ exchangeCode(options: Omit<OAuthExchangeOptions, 'clientId' | 'clientSecret'> & {
43
+ clientId?: string;
44
+ clientSecret?: string;
45
+ }): Promise<VerifyResult<OAuthTokenResponse>>;
46
+ refreshToken(options: Omit<OAuthRefreshOptions, 'clientId' | 'clientSecret'> & {
47
+ clientId?: string;
48
+ clientSecret?: string;
49
+ }): Promise<VerifyResult<OAuthTokenResponse>>;
50
+ /**
51
+ * RFC 8693 token-exchange resource.
52
+ *
53
+ * Exchange a user's currently-valid access token for a scoped-down agent
54
+ * token. Returns RFC 8693 §2.2.1 response. Throwing variant for ergonomic
55
+ * try/catch flows (typed errors per RFC 6749 §5.2 codes); the underlying
56
+ * Result-based variant is exposed as `tokens.exchangeViaApi(...)`.
57
+ *
58
+ * The SDK client MUST be constructed with `clientId` + `clientSecret`
59
+ * referencing an agent-type OAuth client (server-side only — never embed
60
+ * `clientSecret` in browser/mobile code). Invocations without those credentials
61
+ * throw `TokenExchangeInvalidClientError` synchronously.
62
+ */
63
+ readonly tokens: {
64
+ exchange: (options: TokenExchangeOptions) => Promise<TokenExchangeResponse>;
65
+ exchangeViaApi: (options: TokenExchangeOptions) => Promise<VerifyResult<TokenExchangeResponse>>;
66
+ };
67
+ /**
68
+ * OIDC CIBA Core 1.0 (asynchronous user consent) resource.
69
+ *
70
+ * Three operations:
71
+ * - `initiate(options)` → POST /oauth/bc-authorize.
72
+ * - `poll(authReqId)` → POST /oauth/token grant=ciba.
73
+ * - `awaitDecision(opts)` → poll-loop until decision / abort.
74
+ *
75
+ * The SDK client MUST be constructed with `clientId` + `clientSecret`
76
+ * referencing a confidential or agent-type OAuth client registered with
77
+ * `grantTypes` including `urn:openid:params:grant-type:ciba` and the
78
+ * Pro+ tier flag. Invocations without those credentials reject the
79
+ * Result with `OAUTH_MISSING_CLIENT_ID`.
80
+ */
81
+ readonly ciba: {
82
+ initiate: (options: CibaInitiateOptions) => Promise<VerifyResult<CibaInitiateResponse>>;
83
+ poll: (authReqId: string) => Promise<VerifyResult<CibaPollResponse>>;
84
+ awaitDecision: (options: CibaAwaitDecisionOptions) => Promise<CibaPollResponse>;
85
+ };
86
+ /**
87
+ * Create an anonymous user and return its token pair.
88
+ *
89
+ * Call from a trusted backend. Returns a Result (never throws on known API
90
+ * errors); on 403/402/429 the error is mapped to a stable SDK error code.
91
+ */
92
+ anonymous(options?: AnonymousSigninOptions): Promise<VerifyResult<AnonymousSigninResult>>;
93
+ }