@keycardai/oauth 0.4.1 → 0.6.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.
@@ -0,0 +1,169 @@
1
+ import { fetchAuthorizationServerMetadata } from "./discovery.js";
2
+ import { OAuthError } from "./errors.js";
3
+ /**
4
+ * Register a new OAuth 2.0 client with an authorization server (RFC 7591).
5
+ *
6
+ * Discovers `registration_endpoint` from the AS's
7
+ * `.well-known/oauth-authorization-server` metadata, POSTs the registration
8
+ * request as JSON, and returns the issued client credentials.
9
+ *
10
+ * Throws:
11
+ * - `Error` when the AS does not advertise `registration_endpoint`.
12
+ * - `OAuthError` when the AS returns an RFC 6749 §5.2 error response.
13
+ * - `Error` on non-OAuth HTTP failures or malformed responses.
14
+ */
15
+ export async function registerClient(issuerUrl, request, options) {
16
+ const signal = options?.signal ??
17
+ (options?.timeoutMs != null ? AbortSignal.timeout(options.timeoutMs) : undefined);
18
+ const metadata = await fetchAuthorizationServerMetadata(issuerUrl, { signal });
19
+ if (!metadata.registration_endpoint) {
20
+ throw new Error(`Authorization server "${issuerUrl}" does not advertise a registration_endpoint`);
21
+ }
22
+ const response = await fetch(metadata.registration_endpoint, {
23
+ method: "POST",
24
+ headers: {
25
+ "Content-Type": "application/json",
26
+ Accept: "application/json",
27
+ },
28
+ body: JSON.stringify(serializeRequest(request)),
29
+ signal,
30
+ });
31
+ if (!response.ok) {
32
+ let errorBody = null;
33
+ try {
34
+ const json = await response.json();
35
+ if (json && typeof json === "object" && !Array.isArray(json)) {
36
+ errorBody = json;
37
+ }
38
+ }
39
+ catch {
40
+ // non-JSON error body — fall through to generic error
41
+ }
42
+ if (errorBody && typeof errorBody.error === "string") {
43
+ const description = typeof errorBody.error_description === "string"
44
+ ? errorBody.error_description
45
+ : errorBody.error;
46
+ const errorUri = typeof errorBody.error_uri === "string" ? errorBody.error_uri : undefined;
47
+ throw new OAuthError(errorBody.error, description, errorUri);
48
+ }
49
+ throw new Error(`Client registration failed (HTTP ${response.status})`);
50
+ }
51
+ let json;
52
+ try {
53
+ json = await response.json();
54
+ }
55
+ catch {
56
+ throw new Error("Client registration response is not valid JSON");
57
+ }
58
+ if (!json || typeof json !== "object" || Array.isArray(json)) {
59
+ throw new Error("Client registration response is not a valid JSON object");
60
+ }
61
+ const body = json;
62
+ if (typeof body.client_id !== "string") {
63
+ throw new Error("Client registration response missing client_id");
64
+ }
65
+ return deserializeResponse(body);
66
+ }
67
+ function serializeRequest(request) {
68
+ // additionalMetadata goes in first so named fields always take precedence
69
+ // over vendor extensions — callers cannot accidentally override client_name etc.
70
+ const body = {};
71
+ if (request.additionalMetadata) {
72
+ for (const [key, value] of Object.entries(request.additionalMetadata)) {
73
+ body[key] = value;
74
+ }
75
+ }
76
+ if (request.clientName !== undefined)
77
+ body.client_name = request.clientName;
78
+ if (request.clientUri !== undefined)
79
+ body.client_uri = request.clientUri;
80
+ if (request.logoUri !== undefined)
81
+ body.logo_uri = request.logoUri;
82
+ if (request.tosUri !== undefined)
83
+ body.tos_uri = request.tosUri;
84
+ if (request.policyUri !== undefined)
85
+ body.policy_uri = request.policyUri;
86
+ if (request.softwareId !== undefined)
87
+ body.software_id = request.softwareId;
88
+ if (request.softwareVersion !== undefined)
89
+ body.software_version = request.softwareVersion;
90
+ if (request.jwksUri !== undefined)
91
+ body.jwks_uri = request.jwksUri;
92
+ if (request.jwks !== undefined)
93
+ body.jwks = request.jwks;
94
+ if (request.tokenEndpointAuthMethod !== undefined) {
95
+ body.token_endpoint_auth_method = request.tokenEndpointAuthMethod;
96
+ }
97
+ if (request.redirectUris !== undefined)
98
+ body.redirect_uris = [...request.redirectUris];
99
+ if (request.grantTypes !== undefined)
100
+ body.grant_types = [...request.grantTypes];
101
+ if (request.responseTypes !== undefined)
102
+ body.response_types = [...request.responseTypes];
103
+ if (request.scope !== undefined)
104
+ body.scope = request.scope;
105
+ return body;
106
+ }
107
+ function deserializeResponse(body) {
108
+ const response = {
109
+ clientId: body.client_id,
110
+ raw: body,
111
+ };
112
+ if (typeof body.client_secret === "string")
113
+ response.clientSecret = body.client_secret;
114
+ if (typeof body.client_id_issued_at === "number")
115
+ response.clientIdIssuedAt = body.client_id_issued_at;
116
+ if (typeof body.client_secret_expires_at === "number") {
117
+ response.clientSecretExpiresAt = body.client_secret_expires_at;
118
+ }
119
+ if (typeof body.client_name === "string")
120
+ response.clientName = body.client_name;
121
+ if (typeof body.client_uri === "string")
122
+ response.clientUri = body.client_uri;
123
+ if (typeof body.logo_uri === "string")
124
+ response.logoUri = body.logo_uri;
125
+ if (typeof body.tos_uri === "string")
126
+ response.tosUri = body.tos_uri;
127
+ if (typeof body.policy_uri === "string")
128
+ response.policyUri = body.policy_uri;
129
+ if (typeof body.software_id === "string")
130
+ response.softwareId = body.software_id;
131
+ if (typeof body.software_version === "string")
132
+ response.softwareVersion = body.software_version;
133
+ if (typeof body.jwks_uri === "string")
134
+ response.jwksUri = body.jwks_uri;
135
+ if (body.jwks && typeof body.jwks === "object") {
136
+ response.jwks = body.jwks;
137
+ }
138
+ if (typeof body.token_endpoint_auth_method === "string") {
139
+ response.tokenEndpointAuthMethod = body.token_endpoint_auth_method;
140
+ }
141
+ response.redirectUris = normalizeStringArray(body.redirect_uris);
142
+ response.grantTypes = normalizeStringArray(body.grant_types);
143
+ response.responseTypes = normalizeStringArray(body.response_types);
144
+ response.scope = normalizeScope(body.scope);
145
+ if (typeof body.registration_access_token === "string") {
146
+ response.registrationAccessToken = body.registration_access_token;
147
+ }
148
+ if (typeof body.registration_client_uri === "string") {
149
+ response.registrationClientUri = body.registration_client_uri;
150
+ }
151
+ return response;
152
+ }
153
+ function normalizeStringArray(value) {
154
+ if (typeof value === "string")
155
+ return [value];
156
+ if (Array.isArray(value)) {
157
+ const out = value.filter((v) => typeof v === "string");
158
+ return out.length > 0 ? out : undefined;
159
+ }
160
+ return undefined;
161
+ }
162
+ function normalizeScope(value) {
163
+ if (typeof value === "string") {
164
+ const parts = value.split(" ").filter(Boolean);
165
+ return parts.length > 0 ? parts : undefined;
166
+ }
167
+ return normalizeStringArray(value);
168
+ }
169
+ //# sourceMappingURL=registration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/registration.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keycardai/oauth",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "[Preview] OAuth 2.0 primitives for Keycard: JWKS keyring, JWT signing/verification, server-tier token verifier, AccessContext, ClientSecret credentials, and impersonation via RFC 8693 token exchange",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -60,6 +60,11 @@
60
60
  "require": "./dist/cjs/jwt/substituteUser.js",
61
61
  "types": "./dist/esm/jwt/substituteUser.d.ts"
62
62
  },
63
+ "./registration": {
64
+ "import": "./dist/esm/registration.js",
65
+ "require": "./dist/cjs/registration.js",
66
+ "types": "./dist/esm/registration.d.ts"
67
+ },
63
68
  "./server": {
64
69
  "import": "./dist/esm/server/index.js",
65
70
  "require": "./dist/cjs/server/index.js",
@@ -84,6 +89,11 @@
84
89
  "import": "./dist/esm/server/clientSecret.js",
85
90
  "require": "./dist/cjs/server/clientSecret.js",
86
91
  "types": "./dist/esm/server/clientSecret.d.ts"
92
+ },
93
+ "./pkce": {
94
+ "import": "./dist/esm/pkce.js",
95
+ "require": "./dist/cjs/pkce.js",
96
+ "types": "./dist/esm/pkce.d.ts"
87
97
  }
88
98
  },
89
99
  "files": [
@@ -107,6 +117,7 @@
107
117
  },
108
118
  "devDependencies": {
109
119
  "@jest/globals": "^30.0.4",
120
+ "@types/node": "^25.6.0",
110
121
  "jest": "^30.0.4",
111
122
  "ts-jest": "^29.4.0",
112
123
  "typescript": "^5.8.3"