@_mustachio/openauth 0.11.0 → 0.13.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/dist/esm/adapters/d1/index.js +18 -9
- package/dist/esm/adapters/d1/migrations.js +7 -2
- package/dist/esm/adapters/d1/session-store.js +39 -0
- package/dist/esm/adapters/d1/session.js +2 -2
- package/dist/esm/adapters/durable-object/index.js +4 -0
- package/dist/esm/adapters/durable-object/session-store.js +42 -0
- package/dist/esm/adapters/dynamo/index.js +24 -7
- package/dist/esm/adapters/dynamo/session-store.js +49 -0
- package/dist/esm/adapters/kms/index.js +6 -2
- package/dist/esm/adapters/kv/index.js +6 -2
- package/dist/esm/adapters/memory/index.js +24 -7
- package/dist/esm/adapters/memory/session-store.js +23 -0
- package/dist/esm/adapters/postgres/executor.js +2 -2
- package/dist/esm/adapters/postgres/index.js +33 -10
- package/dist/esm/adapters/postgres/migrations.js +8 -2
- package/dist/esm/adapters/postgres/session-store.js +42 -0
- package/dist/esm/client.js +27 -27
- package/dist/esm/domain/authorize.js +2 -2
- package/dist/esm/domain/callback.js +121 -7
- package/dist/esm/domain/client-auth.js +2 -2
- package/dist/esm/domain/crypto.js +14 -14
- package/dist/esm/domain/discovery.js +2 -2
- package/dist/esm/domain/dpop.js +4 -4
- package/dist/esm/domain/id-token.js +5 -5
- package/dist/esm/domain/jwt.js +4 -4
- package/dist/esm/domain/method-dispatch.js +27 -1
- package/dist/esm/domain/method-route.js +80 -1
- package/dist/esm/domain/par.js +2 -2
- package/dist/esm/domain/pkce.js +2 -2
- package/dist/esm/domain/revoke.js +2 -2
- package/dist/esm/domain/scim/discovery.js +206 -0
- package/dist/esm/domain/scim/filter.js +177 -0
- package/dist/esm/domain/scim/handle.js +338 -0
- package/dist/esm/domain/scim/patch.js +568 -0
- package/dist/esm/domain/scim/resource.js +256 -0
- package/dist/esm/domain/state-envelope.js +2 -2
- package/dist/esm/domain/token.js +5 -5
- package/dist/esm/error.js +8 -8
- package/dist/esm/http/cookies.js +19 -2
- package/dist/esm/http/errors.js +4 -4
- package/dist/esm/http/handlers/authorize.js +3 -19
- package/dist/esm/http/handlers/callback.js +7 -1
- package/dist/esm/http/handlers/metadata.js +2 -2
- package/dist/esm/http/handlers/method-route.js +47 -2
- package/dist/esm/http/handlers/revocation.js +2 -2
- package/dist/esm/http/handlers/scim.js +59 -0
- package/dist/esm/http/middleware/tenant.js +31 -6
- package/dist/esm/http/router.js +3 -0
- package/dist/esm/http/schemas/authorize.js +2 -2
- package/dist/esm/http/schemas/revocation.js +2 -2
- package/dist/esm/http/schemas/token.js +7 -7
- package/dist/esm/index.js +34 -32
- package/dist/esm/methods/oauth2-factory.js +2 -2
- package/dist/esm/methods/password-hash.js +2 -2
- package/dist/esm/methods/providers/index.js +30 -14
- package/dist/esm/methods/saml-sp/acs.js +232 -0
- package/dist/esm/methods/saml-sp/attributes.js +77 -0
- package/dist/esm/methods/saml-sp/authnrequest.js +63 -0
- package/dist/esm/methods/saml-sp/cache-provider.js +33 -0
- package/dist/esm/methods/saml-sp/cert-rotation.js +13 -0
- package/dist/esm/methods/saml-sp/factory.js +81 -0
- package/dist/esm/methods/saml-sp/index.js +7 -0
- package/dist/esm/methods/saml-sp/metadata.js +71 -0
- package/dist/esm/methods/saml-sp/method.js +29 -0
- package/dist/esm/methods/saml-sp/parse-idp-metadata.js +102 -0
- package/dist/esm/methods/saml-sp/saml-instance.js +59 -0
- package/dist/esm/methods/saml-sp/slo-initiate.js +84 -0
- package/dist/esm/methods/saml-sp/sls.js +154 -0
- package/dist/esm/methods/saml-sp/types.js +0 -0
- package/dist/esm/ports/scim-directory.js +0 -0
- package/dist/esm/types/error.js +5 -0
- package/dist/esm/types/result.js +3 -3
- package/dist/esm/types/scim.js +0 -0
- package/dist/esm/ui/forms.js +3 -3
- package/dist/esm/ui/index.js +8 -5
- package/dist/esm/ui/picker.js +2 -2
- package/dist/types/adapters/d1/migrations.d.ts.map +1 -1
- package/dist/types/adapters/d1/session-store.d.ts +3 -0
- package/dist/types/adapters/d1/session-store.d.ts.map +1 -1
- package/dist/types/adapters/durable-object/session-store.d.ts +3 -0
- package/dist/types/adapters/durable-object/session-store.d.ts.map +1 -1
- package/dist/types/adapters/dynamo/session-store.d.ts +3 -0
- package/dist/types/adapters/dynamo/session-store.d.ts.map +1 -1
- package/dist/types/adapters/memory/session-store.d.ts +3 -0
- package/dist/types/adapters/memory/session-store.d.ts.map +1 -1
- package/dist/types/adapters/postgres/migrations.d.ts +1 -1
- package/dist/types/adapters/postgres/migrations.d.ts.map +1 -1
- package/dist/types/adapters/postgres/session-store.d.ts +3 -0
- package/dist/types/adapters/postgres/session-store.d.ts.map +1 -1
- package/dist/types/domain/callback.d.ts +16 -1
- package/dist/types/domain/callback.d.ts.map +1 -1
- package/dist/types/domain/method-dispatch.d.ts.map +1 -1
- package/dist/types/domain/method-route.d.ts +57 -1
- package/dist/types/domain/method-route.d.ts.map +1 -1
- package/dist/types/domain/scim/discovery.d.ts +11 -0
- package/dist/types/domain/scim/discovery.d.ts.map +1 -0
- package/dist/types/domain/scim/filter.d.ts +51 -0
- package/dist/types/domain/scim/filter.d.ts.map +1 -0
- package/dist/types/domain/scim/handle.d.ts +35 -0
- package/dist/types/domain/scim/handle.d.ts.map +1 -0
- package/dist/types/domain/scim/patch.d.ts +73 -0
- package/dist/types/domain/scim/patch.d.ts.map +1 -0
- package/dist/types/domain/scim/resource.d.ts +56 -0
- package/dist/types/domain/scim/resource.d.ts.map +1 -0
- package/dist/types/http/context.d.ts +6 -1
- package/dist/types/http/context.d.ts.map +1 -1
- package/dist/types/http/cookies.d.ts +9 -1
- package/dist/types/http/cookies.d.ts.map +1 -1
- package/dist/types/http/handlers/callback.d.ts.map +1 -1
- package/dist/types/http/handlers/method-route.d.ts.map +1 -1
- package/dist/types/http/handlers/scim.d.ts +3 -0
- package/dist/types/http/handlers/scim.d.ts.map +1 -0
- package/dist/types/http/middleware/tenant.d.ts.map +1 -1
- package/dist/types/http/router.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/methods/saml-sp/acs.d.ts +4 -0
- package/dist/types/methods/saml-sp/acs.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/attributes.d.ts +45 -0
- package/dist/types/methods/saml-sp/attributes.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/authnrequest.d.ts +4 -0
- package/dist/types/methods/saml-sp/authnrequest.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/cache-provider.d.ts +38 -0
- package/dist/types/methods/saml-sp/cache-provider.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/cert-rotation.d.ts +23 -0
- package/dist/types/methods/saml-sp/cert-rotation.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/factory.d.ts +920 -0
- package/dist/types/methods/saml-sp/factory.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/index.d.ts +24 -0
- package/dist/types/methods/saml-sp/index.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/metadata.d.ts +50 -0
- package/dist/types/methods/saml-sp/metadata.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/method.d.ts +17 -0
- package/dist/types/methods/saml-sp/method.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts +4 -0
- package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/saml-instance.d.ts +93 -0
- package/dist/types/methods/saml-sp/saml-instance.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/slo-initiate.d.ts +4 -0
- package/dist/types/methods/saml-sp/slo-initiate.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/sls.d.ts +4 -0
- package/dist/types/methods/saml-sp/sls.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/types.d.ts +299 -0
- package/dist/types/methods/saml-sp/types.d.ts.map +1 -0
- package/dist/types/ports/audit-log.d.ts +18 -5
- package/dist/types/ports/audit-log.d.ts.map +1 -1
- package/dist/types/ports/scim-directory.d.ts +104 -0
- package/dist/types/ports/scim-directory.d.ts.map +1 -0
- package/dist/types/ports/session-store.d.ts +23 -0
- package/dist/types/ports/session-store.d.ts.map +1 -1
- package/dist/types/types/error.d.ts +5 -0
- package/dist/types/types/error.d.ts.map +1 -1
- package/dist/types/types/idp.d.ts +83 -0
- package/dist/types/types/idp.d.ts.map +1 -1
- package/dist/types/types/method.d.ts +109 -0
- package/dist/types/types/method.d.ts.map +1 -1
- package/dist/types/types/scim.d.ts +269 -0
- package/dist/types/types/scim.d.ts.map +1 -0
- package/dist/types/types/tenant.d.ts +10 -0
- package/dist/types/types/tenant.d.ts.map +1 -1
- package/package.json +9 -1
- package/src/adapters/d1/migrations.ts +5 -0
- package/src/adapters/d1/session-store.ts +68 -0
- package/src/adapters/durable-object/session-store.ts +61 -0
- package/src/adapters/dynamo/session-store.ts +63 -0
- package/src/adapters/memory/session-store.ts +39 -0
- package/src/adapters/postgres/migrations.ts +6 -0
- package/src/adapters/postgres/session-store.ts +69 -0
- package/src/domain/callback.ts +215 -7
- package/src/domain/method-dispatch.ts +53 -1
- package/src/domain/method-route.ts +190 -1
- package/src/domain/scim/discovery.ts +262 -0
- package/src/domain/scim/filter.ts +295 -0
- package/src/domain/scim/handle.ts +597 -0
- package/src/domain/scim/patch.ts +807 -0
- package/src/domain/scim/resource.ts +358 -0
- package/src/http/context.ts +9 -0
- package/src/http/cookies.ts +19 -1
- package/src/http/handlers/authorize.ts +2 -22
- package/src/http/handlers/callback.ts +9 -1
- package/src/http/handlers/method-route.ts +72 -2
- package/src/http/handlers/scim.ts +107 -0
- package/src/http/middleware/tenant.ts +47 -1
- package/src/http/router.ts +9 -0
- package/src/index.ts +23 -0
- package/src/methods/saml-sp/acs.ts +449 -0
- package/src/methods/saml-sp/attributes.ts +146 -0
- package/src/methods/saml-sp/authnrequest.ts +129 -0
- package/src/methods/saml-sp/cache-provider.ts +76 -0
- package/src/methods/saml-sp/cert-rotation.ts +34 -0
- package/src/methods/saml-sp/factory.ts +139 -0
- package/src/methods/saml-sp/index.ts +34 -0
- package/src/methods/saml-sp/metadata.ts +215 -0
- package/src/methods/saml-sp/method.ts +65 -0
- package/src/methods/saml-sp/parse-idp-metadata.ts +158 -0
- package/src/methods/saml-sp/saml-instance.ts +210 -0
- package/src/methods/saml-sp/slo-initiate.ts +180 -0
- package/src/methods/saml-sp/sls.ts +307 -0
- package/src/methods/saml-sp/types.ts +298 -0
- package/src/ports/CONSISTENCY.md +52 -24
- package/src/ports/audit-log.ts +18 -5
- package/src/ports/scim-directory.ts +166 -0
- package/src/ports/session-store.ts +24 -0
- package/src/types/error.ts +11 -0
- package/src/types/idp.ts +86 -0
- package/src/types/method.ts +110 -0
- package/src/types/scim.ts +280 -0
- package/src/types/tenant.ts +10 -0
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
// src/adapters/d1/index.ts
|
|
2
|
+
import { D1AuditLog } from "./audit-log";
|
|
3
|
+
import { D1ConfigStore } from "./config-store";
|
|
4
|
+
import { D1KeyStore } from "./key-store";
|
|
5
|
+
import { D1MethodStore } from "./method-store";
|
|
6
|
+
import { D1SessionStore } from "./session-store";
|
|
7
|
+
import { D1TokenStore } from "./token-store";
|
|
8
|
+
import { INITIAL_SCHEMA_SQL, migrate } from "./migrations";
|
|
9
|
+
import { isSessionsCapable, primarySession } from "./session";
|
|
1
10
|
export {
|
|
2
|
-
|
|
3
|
-
migrate,
|
|
4
|
-
isSessionsCapable,
|
|
5
|
-
INITIAL_SCHEMA_SQL,
|
|
6
|
-
D1TokenStore,
|
|
7
|
-
D1SessionStore,
|
|
8
|
-
D1MethodStore,
|
|
9
|
-
D1KeyStore,
|
|
11
|
+
D1AuditLog,
|
|
10
12
|
D1ConfigStore,
|
|
11
|
-
|
|
13
|
+
D1KeyStore,
|
|
14
|
+
D1MethodStore,
|
|
15
|
+
D1SessionStore,
|
|
16
|
+
D1TokenStore,
|
|
17
|
+
INITIAL_SCHEMA_SQL,
|
|
18
|
+
isSessionsCapable,
|
|
19
|
+
migrate,
|
|
20
|
+
primarySession
|
|
12
21
|
};
|
|
@@ -57,6 +57,11 @@ var INITIAL_SCHEMA_SQL = [
|
|
|
57
57
|
payload TEXT NOT NULL,
|
|
58
58
|
expires_at INTEGER NOT NULL
|
|
59
59
|
)`,
|
|
60
|
+
`CREATE TABLE IF NOT EXISTS openauth_scratch (
|
|
61
|
+
scratch_key TEXT PRIMARY KEY,
|
|
62
|
+
value TEXT NOT NULL,
|
|
63
|
+
expires_at INTEGER NOT NULL
|
|
64
|
+
)`,
|
|
60
65
|
`CREATE TABLE IF NOT EXISTS openauth_audit_events (
|
|
61
66
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
62
67
|
ts INTEGER NOT NULL,
|
|
@@ -72,6 +77,6 @@ async function migrate(db) {
|
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
export {
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
INITIAL_SCHEMA_SQL,
|
|
81
|
+
migrate
|
|
77
82
|
};
|
|
@@ -128,6 +128,45 @@ class D1SessionStore {
|
|
|
128
128
|
}
|
|
129
129
|
return ok(undefined);
|
|
130
130
|
}
|
|
131
|
+
async saveScratch(key, value, ttlMs) {
|
|
132
|
+
if (ttlMs <= 0) {
|
|
133
|
+
return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
|
|
134
|
+
}
|
|
135
|
+
const expiresAt = this.#clock() + ttlMs;
|
|
136
|
+
try {
|
|
137
|
+
await primarySession(this.#db).prepare(`INSERT INTO openauth_scratch (scratch_key, value, expires_at) VALUES (?1, ?2, ?3)
|
|
138
|
+
ON CONFLICT(scratch_key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at`).bind(key, value, expiresAt).run();
|
|
139
|
+
} catch (e) {
|
|
140
|
+
return err(authError.internalError("saveScratch: upsert failed", e));
|
|
141
|
+
}
|
|
142
|
+
return ok(undefined);
|
|
143
|
+
}
|
|
144
|
+
async readScratch(key) {
|
|
145
|
+
let row;
|
|
146
|
+
try {
|
|
147
|
+
row = await primarySession(this.#db).prepare(`SELECT value, expires_at FROM openauth_scratch WHERE scratch_key = ?1`).bind(key).first();
|
|
148
|
+
} catch (e) {
|
|
149
|
+
return err(authError.internalError("readScratch: query failed", e));
|
|
150
|
+
}
|
|
151
|
+
if (!row) {
|
|
152
|
+
return err(authError.unknownState(`scratch "${key}" unknown`));
|
|
153
|
+
}
|
|
154
|
+
if (this.#clock() >= Number(row.expires_at)) {
|
|
155
|
+
try {
|
|
156
|
+
await primarySession(this.#db).prepare(`DELETE FROM openauth_scratch WHERE scratch_key = ?1`).bind(key).run();
|
|
157
|
+
} catch {}
|
|
158
|
+
return err(authError.unknownState(`scratch "${key}" expired`));
|
|
159
|
+
}
|
|
160
|
+
return ok(row.value);
|
|
161
|
+
}
|
|
162
|
+
async deleteScratch(key) {
|
|
163
|
+
try {
|
|
164
|
+
await primarySession(this.#db).prepare(`DELETE FROM openauth_scratch WHERE scratch_key = ?1`).bind(key).run();
|
|
165
|
+
} catch (e) {
|
|
166
|
+
return err(authError.internalError("deleteScratch: delete failed", e));
|
|
167
|
+
}
|
|
168
|
+
return ok(undefined);
|
|
169
|
+
}
|
|
131
170
|
}
|
|
132
171
|
export {
|
|
133
172
|
D1SessionStore
|
|
@@ -131,9 +131,51 @@ class DurableObjectSessionStore {
|
|
|
131
131
|
}
|
|
132
132
|
return ok(undefined);
|
|
133
133
|
}
|
|
134
|
+
async saveScratch(key, value, ttlMs) {
|
|
135
|
+
if (ttlMs <= 0) {
|
|
136
|
+
return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
|
|
137
|
+
}
|
|
138
|
+
const stored = {
|
|
139
|
+
value,
|
|
140
|
+
expiresAt: this.#clock() + ttlMs
|
|
141
|
+
};
|
|
142
|
+
try {
|
|
143
|
+
await this.#storage.put(scratchKey(key), stored);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
return err(authError.internalError("saveScratch: put failed", e));
|
|
146
|
+
}
|
|
147
|
+
return ok(undefined);
|
|
148
|
+
}
|
|
149
|
+
async readScratch(key) {
|
|
150
|
+
let stored;
|
|
151
|
+
try {
|
|
152
|
+
stored = await this.#storage.get(scratchKey(key));
|
|
153
|
+
} catch (e) {
|
|
154
|
+
return err(authError.internalError("readScratch: get failed", e));
|
|
155
|
+
}
|
|
156
|
+
if (!stored) {
|
|
157
|
+
return err(authError.unknownState(`scratch "${key}" unknown`));
|
|
158
|
+
}
|
|
159
|
+
if (this.#clock() >= stored.expiresAt) {
|
|
160
|
+
try {
|
|
161
|
+
await this.#storage.delete(scratchKey(key));
|
|
162
|
+
} catch {}
|
|
163
|
+
return err(authError.unknownState(`scratch "${key}" expired`));
|
|
164
|
+
}
|
|
165
|
+
return ok(stored.value);
|
|
166
|
+
}
|
|
167
|
+
async deleteScratch(key) {
|
|
168
|
+
try {
|
|
169
|
+
await this.#storage.delete(scratchKey(key));
|
|
170
|
+
} catch (e) {
|
|
171
|
+
return err(authError.internalError("deleteScratch: delete failed", e));
|
|
172
|
+
}
|
|
173
|
+
return ok(undefined);
|
|
174
|
+
}
|
|
134
175
|
}
|
|
135
176
|
var flowKey = (flowId) => `flow:${flowId}`;
|
|
136
177
|
var sessionKey = (sessionId) => `session:${sessionId}`;
|
|
178
|
+
var scratchKey = (key) => `scratch:${key}`;
|
|
137
179
|
export {
|
|
138
180
|
DurableObjectSessionStore
|
|
139
181
|
};
|
|
@@ -1,10 +1,27 @@
|
|
|
1
|
+
// src/adapters/dynamo/index.ts
|
|
2
|
+
import { DynamoAuditLog } from "./audit-log";
|
|
3
|
+
import {
|
|
4
|
+
DynamoConfigStore
|
|
5
|
+
} from "./config-store";
|
|
6
|
+
import { DynamoKeyStore } from "./key-store";
|
|
7
|
+
import {
|
|
8
|
+
DynamoMethodStore
|
|
9
|
+
} from "./method-store";
|
|
10
|
+
import {
|
|
11
|
+
DynamoSessionStore
|
|
12
|
+
} from "./session-store";
|
|
13
|
+
import { DynamoTokenStore } from "./token-store";
|
|
14
|
+
import {
|
|
15
|
+
DynamoPasskeyCredentialStore
|
|
16
|
+
} from "./passkey-credential-store";
|
|
17
|
+
import { fromDynamoDBClient } from "./sdk";
|
|
1
18
|
export {
|
|
2
|
-
|
|
3
|
-
DynamoTokenStore,
|
|
4
|
-
DynamoSessionStore,
|
|
5
|
-
DynamoPasskeyCredentialStore,
|
|
6
|
-
DynamoMethodStore,
|
|
7
|
-
DynamoKeyStore,
|
|
19
|
+
DynamoAuditLog,
|
|
8
20
|
DynamoConfigStore,
|
|
9
|
-
|
|
21
|
+
DynamoKeyStore,
|
|
22
|
+
DynamoMethodStore,
|
|
23
|
+
DynamoPasskeyCredentialStore,
|
|
24
|
+
DynamoSessionStore,
|
|
25
|
+
DynamoTokenStore,
|
|
26
|
+
fromDynamoDBClient
|
|
10
27
|
};
|
|
@@ -152,6 +152,55 @@ class DynamoSessionStore {
|
|
|
152
152
|
}
|
|
153
153
|
return ok(undefined);
|
|
154
154
|
}
|
|
155
|
+
async saveScratch(key, value, ttlMs) {
|
|
156
|
+
if (ttlMs <= 0) {
|
|
157
|
+
return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
|
|
158
|
+
}
|
|
159
|
+
const expiresAtMs = this.#clock() + ttlMs;
|
|
160
|
+
try {
|
|
161
|
+
await this.#exec.put({
|
|
162
|
+
item: {
|
|
163
|
+
pk: "scratch",
|
|
164
|
+
sk: key,
|
|
165
|
+
value,
|
|
166
|
+
expires_at: expiresAtMs,
|
|
167
|
+
ttl: Math.floor(expiresAtMs / 1000)
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
} catch (e) {
|
|
171
|
+
return err(authError.internalError("saveScratch: put failed", e));
|
|
172
|
+
}
|
|
173
|
+
return ok(undefined);
|
|
174
|
+
}
|
|
175
|
+
async readScratch(key) {
|
|
176
|
+
let row;
|
|
177
|
+
try {
|
|
178
|
+
row = await this.#exec.get({
|
|
179
|
+
key: { pk: "scratch", sk: key },
|
|
180
|
+
consistentRead: true
|
|
181
|
+
});
|
|
182
|
+
} catch (e) {
|
|
183
|
+
return err(authError.internalError("readScratch: get failed", e));
|
|
184
|
+
}
|
|
185
|
+
if (!row) {
|
|
186
|
+
return err(authError.unknownState(`scratch "${key}" unknown`));
|
|
187
|
+
}
|
|
188
|
+
if (this.#clock() >= Number(row.expires_at)) {
|
|
189
|
+
try {
|
|
190
|
+
await this.#exec.delete({ key: { pk: "scratch", sk: key } });
|
|
191
|
+
} catch {}
|
|
192
|
+
return err(authError.unknownState(`scratch "${key}" expired`));
|
|
193
|
+
}
|
|
194
|
+
return ok(String(row.value));
|
|
195
|
+
}
|
|
196
|
+
async deleteScratch(key) {
|
|
197
|
+
try {
|
|
198
|
+
await this.#exec.delete({ key: { pk: "scratch", sk: key } });
|
|
199
|
+
} catch (e) {
|
|
200
|
+
return err(authError.internalError("deleteScratch: delete failed", e));
|
|
201
|
+
}
|
|
202
|
+
return ok(undefined);
|
|
203
|
+
}
|
|
155
204
|
}
|
|
156
205
|
function parseFlowPayload(raw) {
|
|
157
206
|
if (typeof raw === "string")
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
// src/adapters/kms/index.ts
|
|
2
|
+
import { inMemoryKmsBackingStore } from "./in-memory-backing";
|
|
3
|
+
import { KmsKeyStore } from "./key-store";
|
|
4
|
+
import { fromKmsClient } from "./sdk";
|
|
1
5
|
export {
|
|
2
|
-
|
|
6
|
+
KmsKeyStore,
|
|
3
7
|
fromKmsClient,
|
|
4
|
-
|
|
8
|
+
inMemoryKmsBackingStore
|
|
5
9
|
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
// src/adapters/kv/index.ts
|
|
2
|
+
import { KvAuditLog } from "./audit-log";
|
|
3
|
+
import { KvConfigStore } from "./config-store";
|
|
4
|
+
import { KvMethodStore } from "./method-store";
|
|
1
5
|
export {
|
|
2
|
-
|
|
6
|
+
KvAuditLog,
|
|
3
7
|
KvConfigStore,
|
|
4
|
-
|
|
8
|
+
KvMethodStore
|
|
5
9
|
};
|
|
@@ -1,10 +1,27 @@
|
|
|
1
|
+
// src/adapters/memory/index.ts
|
|
2
|
+
import { MemoryAuditLog } from "./audit-log";
|
|
3
|
+
import {
|
|
4
|
+
MemoryConfigStore
|
|
5
|
+
} from "./config-store";
|
|
6
|
+
import { MemoryKeyStore } from "./key-store";
|
|
7
|
+
import {
|
|
8
|
+
MemoryMethodStore
|
|
9
|
+
} from "./method-store";
|
|
10
|
+
import {
|
|
11
|
+
MemorySessionStore
|
|
12
|
+
} from "./session-store";
|
|
13
|
+
import { MemoryTokenStore } from "./token-store";
|
|
14
|
+
import {
|
|
15
|
+
MemoryPasskeyCredentialStore
|
|
16
|
+
} from "./passkey-credential-store";
|
|
17
|
+
import { realClock } from "./clock";
|
|
1
18
|
export {
|
|
2
|
-
|
|
3
|
-
MemoryTokenStore,
|
|
4
|
-
MemorySessionStore,
|
|
5
|
-
MemoryPasskeyCredentialStore,
|
|
6
|
-
MemoryMethodStore,
|
|
7
|
-
MemoryKeyStore,
|
|
19
|
+
MemoryAuditLog,
|
|
8
20
|
MemoryConfigStore,
|
|
9
|
-
|
|
21
|
+
MemoryKeyStore,
|
|
22
|
+
MemoryMethodStore,
|
|
23
|
+
MemoryPasskeyCredentialStore,
|
|
24
|
+
MemorySessionStore,
|
|
25
|
+
MemoryTokenStore,
|
|
26
|
+
realClock
|
|
10
27
|
};
|
|
@@ -8,6 +8,7 @@ class MemorySessionStore {
|
|
|
8
8
|
#flows = new Map;
|
|
9
9
|
#sessions = new Map;
|
|
10
10
|
#par = new Map;
|
|
11
|
+
#scratch = new Map;
|
|
11
12
|
constructor(opts = {}) {
|
|
12
13
|
this.#clock = opts.clock ?? realClock;
|
|
13
14
|
}
|
|
@@ -98,6 +99,28 @@ class MemorySessionStore {
|
|
|
98
99
|
}
|
|
99
100
|
return ok(stored.record);
|
|
100
101
|
}
|
|
102
|
+
async saveScratch(key, value, ttlMs) {
|
|
103
|
+
if (ttlMs <= 0) {
|
|
104
|
+
return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
|
|
105
|
+
}
|
|
106
|
+
this.#scratch.set(key, { value, expiresAt: this.#clock() + ttlMs });
|
|
107
|
+
return ok(undefined);
|
|
108
|
+
}
|
|
109
|
+
async readScratch(key) {
|
|
110
|
+
const stored = this.#scratch.get(key);
|
|
111
|
+
if (!stored) {
|
|
112
|
+
return err(authError.unknownState(`scratch "${key}" unknown`));
|
|
113
|
+
}
|
|
114
|
+
if (this.#clock() >= stored.expiresAt) {
|
|
115
|
+
this.#scratch.delete(key);
|
|
116
|
+
return err(authError.unknownState(`scratch "${key}" expired`));
|
|
117
|
+
}
|
|
118
|
+
return ok(stored.value);
|
|
119
|
+
}
|
|
120
|
+
async deleteScratch(key) {
|
|
121
|
+
this.#scratch.delete(key);
|
|
122
|
+
return ok(undefined);
|
|
123
|
+
}
|
|
101
124
|
}
|
|
102
125
|
export {
|
|
103
126
|
MemorySessionStore
|
|
@@ -1,13 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
fromPostgresJs,
|
|
1
|
+
// src/adapters/postgres/index.ts
|
|
2
|
+
import {
|
|
4
3
|
fromPGlite,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
PostgresConfigStore
|
|
4
|
+
fromPostgresJs
|
|
5
|
+
} from "./executor";
|
|
6
|
+
import { INITIAL_SCHEMA_SQL, migrate } from "./migrations";
|
|
7
|
+
import { PostgresAuditLog } from "./audit-log";
|
|
8
|
+
import {
|
|
9
|
+
PostgresConfigStore
|
|
10
|
+
} from "./config-store";
|
|
11
|
+
import { PostgresKeyStore } from "./key-store";
|
|
12
|
+
import {
|
|
13
|
+
PostgresMethodStore
|
|
14
|
+
} from "./method-store";
|
|
15
|
+
import {
|
|
16
|
+
PostgresSessionStore
|
|
17
|
+
} from "./session-store";
|
|
18
|
+
import {
|
|
19
|
+
PostgresTokenStore
|
|
20
|
+
} from "./token-store";
|
|
21
|
+
import {
|
|
22
|
+
PostgresPasskeyCredentialStore
|
|
23
|
+
} from "./passkey-credential-store";
|
|
24
|
+
export {
|
|
25
|
+
INITIAL_SCHEMA_SQL,
|
|
11
26
|
PostgresAuditLog,
|
|
12
|
-
|
|
27
|
+
PostgresConfigStore,
|
|
28
|
+
PostgresKeyStore,
|
|
29
|
+
PostgresMethodStore,
|
|
30
|
+
PostgresPasskeyCredentialStore,
|
|
31
|
+
PostgresSessionStore,
|
|
32
|
+
PostgresTokenStore,
|
|
33
|
+
fromPGlite,
|
|
34
|
+
fromPostgresJs,
|
|
35
|
+
migrate
|
|
13
36
|
};
|
|
@@ -75,6 +75,12 @@ CREATE TABLE IF NOT EXISTS openauth_sessions (
|
|
|
75
75
|
expires_at bigint NOT NULL
|
|
76
76
|
);
|
|
77
77
|
|
|
78
|
+
CREATE TABLE IF NOT EXISTS openauth_scratch (
|
|
79
|
+
scratch_key text PRIMARY KEY,
|
|
80
|
+
value text NOT NULL,
|
|
81
|
+
expires_at bigint NOT NULL
|
|
82
|
+
);
|
|
83
|
+
|
|
78
84
|
CREATE TABLE IF NOT EXISTS openauth_audit_events (
|
|
79
85
|
id bigserial PRIMARY KEY,
|
|
80
86
|
ts bigint NOT NULL,
|
|
@@ -106,6 +112,6 @@ async function migrate(exec) {
|
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
114
|
export {
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
INITIAL_SCHEMA_SQL,
|
|
116
|
+
migrate
|
|
111
117
|
};
|
|
@@ -119,6 +119,48 @@ class PostgresSessionStore {
|
|
|
119
119
|
}
|
|
120
120
|
return ok(undefined);
|
|
121
121
|
}
|
|
122
|
+
async saveScratch(key, value, ttlMs) {
|
|
123
|
+
if (ttlMs <= 0) {
|
|
124
|
+
return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
|
|
125
|
+
}
|
|
126
|
+
const expiresAt = this.#clock() + ttlMs;
|
|
127
|
+
try {
|
|
128
|
+
await this.#exec.query(`INSERT INTO openauth_scratch (scratch_key, value, expires_at)
|
|
129
|
+
VALUES ($1, $2, $3)
|
|
130
|
+
ON CONFLICT (scratch_key)
|
|
131
|
+
DO UPDATE SET value = EXCLUDED.value, expires_at = EXCLUDED.expires_at`, [key, value, expiresAt]);
|
|
132
|
+
} catch (e) {
|
|
133
|
+
return err(authError.internalError("saveScratch: upsert failed", e));
|
|
134
|
+
}
|
|
135
|
+
return ok(undefined);
|
|
136
|
+
}
|
|
137
|
+
async readScratch(key) {
|
|
138
|
+
let row;
|
|
139
|
+
try {
|
|
140
|
+
const result = await this.#exec.query(`SELECT value, expires_at FROM openauth_scratch WHERE scratch_key = $1`, [key]);
|
|
141
|
+
row = result.rows[0];
|
|
142
|
+
} catch (e) {
|
|
143
|
+
return err(authError.internalError("readScratch: query failed", e));
|
|
144
|
+
}
|
|
145
|
+
if (!row) {
|
|
146
|
+
return err(authError.unknownState(`scratch "${key}" unknown`));
|
|
147
|
+
}
|
|
148
|
+
if (this.#clock() >= Number(row.expires_at)) {
|
|
149
|
+
try {
|
|
150
|
+
await this.#exec.query(`DELETE FROM openauth_scratch WHERE scratch_key = $1`, [key]);
|
|
151
|
+
} catch {}
|
|
152
|
+
return err(authError.unknownState(`scratch "${key}" expired`));
|
|
153
|
+
}
|
|
154
|
+
return ok(row.value);
|
|
155
|
+
}
|
|
156
|
+
async deleteScratch(key) {
|
|
157
|
+
try {
|
|
158
|
+
await this.#exec.query(`DELETE FROM openauth_scratch WHERE scratch_key = $1`, [key]);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
return err(authError.internalError("deleteScratch: delete failed", e));
|
|
161
|
+
}
|
|
162
|
+
return ok(undefined);
|
|
163
|
+
}
|
|
122
164
|
}
|
|
123
165
|
function parseFlowRecord(raw) {
|
|
124
166
|
if (typeof raw === "string")
|
package/dist/esm/client.js
CHANGED
|
@@ -33,49 +33,49 @@ function createClient(input) {
|
|
|
33
33
|
if (cached)
|
|
34
34
|
return cached;
|
|
35
35
|
const keyset = await (f || fetch)(wk.jwks_uri).then((r) => r.json());
|
|
36
|
-
const
|
|
37
|
-
jwksCache.set(issuer,
|
|
38
|
-
return
|
|
36
|
+
const result = createLocalJWKSet(keyset);
|
|
37
|
+
jwksCache.set(issuer, result);
|
|
38
|
+
return result;
|
|
39
39
|
}
|
|
40
40
|
const result = {
|
|
41
41
|
async authorize(redirectURI, response, opts) {
|
|
42
|
-
const
|
|
42
|
+
const result = new URL(issuer + "/authorize");
|
|
43
43
|
const challenge = {
|
|
44
44
|
state: crypto.randomUUID()
|
|
45
45
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
result.searchParams.set("client_id", input.clientID);
|
|
47
|
+
result.searchParams.set("redirect_uri", redirectURI);
|
|
48
|
+
result.searchParams.set("response_type", response);
|
|
49
|
+
result.searchParams.set("state", challenge.state);
|
|
50
50
|
if (opts?.provider)
|
|
51
|
-
|
|
51
|
+
result.searchParams.set("provider", opts.provider);
|
|
52
52
|
if (opts?.scope !== undefined) {
|
|
53
53
|
const scope = Array.isArray(opts.scope) ? opts.scope.join(" ") : opts.scope;
|
|
54
54
|
if (scope)
|
|
55
|
-
|
|
55
|
+
result.searchParams.set("scope", scope);
|
|
56
56
|
}
|
|
57
57
|
if (opts?.pkce && response === "code") {
|
|
58
58
|
const pkce = await generatePKCE();
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
result.searchParams.set("code_challenge_method", "S256");
|
|
60
|
+
result.searchParams.set("code_challenge", pkce.challenge);
|
|
61
61
|
challenge.verifier = pkce.verifier;
|
|
62
62
|
}
|
|
63
63
|
return {
|
|
64
64
|
challenge,
|
|
65
|
-
url:
|
|
65
|
+
url: result.toString()
|
|
66
66
|
};
|
|
67
67
|
},
|
|
68
68
|
async pkce(redirectURI, opts) {
|
|
69
|
-
const
|
|
69
|
+
const result = new URL(issuer + "/authorize");
|
|
70
70
|
if (opts?.provider)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
result.searchParams.set("provider", opts.provider);
|
|
72
|
+
result.searchParams.set("client_id", input.clientID);
|
|
73
|
+
result.searchParams.set("redirect_uri", redirectURI);
|
|
74
|
+
result.searchParams.set("response_type", "code");
|
|
75
75
|
const pkce = await generatePKCE();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return [pkce.verifier,
|
|
76
|
+
result.searchParams.set("code_challenge_method", "S256");
|
|
77
|
+
result.searchParams.set("code_challenge", pkce.challenge);
|
|
78
|
+
return [pkce.verifier, result.toString()];
|
|
79
79
|
},
|
|
80
80
|
async exchange(code, redirectURI, verifier) {
|
|
81
81
|
const tokens = await f(issuer + "/token", {
|
|
@@ -150,18 +150,18 @@ function createClient(input) {
|
|
|
150
150
|
async verify(subjects, token, options) {
|
|
151
151
|
const jwks = await getJWKS();
|
|
152
152
|
try {
|
|
153
|
-
const
|
|
153
|
+
const result = await jwtVerify(token, jwks, {
|
|
154
154
|
issuer
|
|
155
155
|
});
|
|
156
|
-
const claim =
|
|
156
|
+
const claim = result.payload.claim;
|
|
157
157
|
let subjectType;
|
|
158
158
|
let subjectProperties;
|
|
159
159
|
if (claim && typeof claim.type === "string") {
|
|
160
160
|
subjectType = claim.type;
|
|
161
161
|
subjectProperties = claim.properties;
|
|
162
|
-
} else if (
|
|
163
|
-
subjectType =
|
|
164
|
-
subjectProperties =
|
|
162
|
+
} else if (result.payload.mode === "access" && typeof result.payload.type === "string") {
|
|
163
|
+
subjectType = result.payload.type;
|
|
164
|
+
subjectProperties = result.payload.properties;
|
|
165
165
|
}
|
|
166
166
|
if (subjectType === undefined) {
|
|
167
167
|
return { err: new InvalidSubjectError };
|
|
@@ -175,7 +175,7 @@ function createClient(input) {
|
|
|
175
175
|
return { err: new InvalidSubjectError };
|
|
176
176
|
}
|
|
177
177
|
return {
|
|
178
|
-
aud:
|
|
178
|
+
aud: result.payload.aud,
|
|
179
179
|
subject: {
|
|
180
180
|
type: subjectType,
|
|
181
181
|
properties: validated.value
|