@seams/wallet-server 0.28.0 → 0.29.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/README.md +19 -4
- package/artifact-manifest.json +23 -13
- package/cloudflare-router-ab/build/deriver-a/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/deriver-b/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/router/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/signing-worker/build-stamp.json +1 -1
- package/cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json +1 -1
- package/dist/esm/application-auth.js +2 -0
- package/dist/esm/auth/applicationAuth.js +85 -0
- package/dist/esm/auth/externalAuthService.js +58 -7
- package/dist/esm/auth/externalAuthStore.js +157 -21
- package/dist/esm/auth/externalOwnerOperations.js +82 -0
- package/dist/esm/cloud-host.js +4 -4
- package/dist/esm/core/nearRelayerAccountProvisioning.js +1 -1
- package/dist/esm/router/cloudflare/d1/auth/d1AuthorizedOperationRouteService.js +2 -0
- package/dist/esm/router/cloudflare/d1/auth/d1RecordedOwnerOperation.js +43 -0
- package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +22 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecoveryService.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRegistrationEnrollmentFinalizer.js +1 -1
- package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpSessionResolver.js +1 -1
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1PasskeyCustodyRouteService.js +2 -2
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletCustodyCommitStore.js +2 -2
- package/dist/esm/router/cloudflare/d1/passkeyCustody/d1WalletRecoveryGoogleEmailOtpService.js +2 -2
- package/dist/esm/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.js +9 -0
- package/dist/esm/router/cloudflare/d1/registration/d1WalletExecutionAuthority.js +9 -16
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationService.js +43 -1
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationSetup.js +12 -0
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationSetupExecution.js +1 -0
- package/dist/esm/router/cloudflare/d1/registration/registrationCompletionWrite.js +1 -0
- package/dist/esm/router/cloudflare/d1/registration/walletRegistrationSessionCommitReceipt.js +35 -0
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodBoundary.js +1 -1
- package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodService.js +4 -3
- package/dist/esm/router/cloudflare.js +1785 -1489
- package/dist/esm/router/domains/ed25519Yao/export/routerAbEd25519YaoExport.js +2 -2
- package/dist/esm/router/domains/emailOtp/emailOtpRouteHandlers.js +1 -1
- package/dist/esm/router/domains/passkeyCustody/walletCustodyRegistrationCommit.js +1 -1
- package/dist/esm/router/domains/signingOperations/routerAbEd25519NormalSigningRoute.js +35 -1
- package/dist/esm/router/domains/walletRegistration/walletRegistrationReservation.js +17 -2
- package/dist/esm/router/domains/walletRegistration/walletRegistrationRoutes.js +9 -1
- package/dist/esm/router/domains/walletUnlock/walletUnlockRouteHandlers.js +1 -1
- package/dist/esm/router/transport/fetch/routes/ecdsaOperationStepUpFactor.js +2 -2
- package/dist/esm/router/transport/fetch/routes/externalAuth.js +139 -32
- package/dist/esm/router/transport/fetch/routes/passkeyCustody.js +44 -304
- package/dist/esm/router/transport/fetch/routes/sessions.js +2 -2
- package/dist/esm/router/transport/fetch/routes/thresholdEd25519.js +2 -2
- package/dist/esm/router/transport/fetch/routes/walletRegistration.js +2 -2
- package/dist/esm/router/transport/fetch/walletOwnerOperationAuthorization.js +306 -0
- package/dist/esm/shared-ts/src/auth/externalAuth.js +87 -14
- package/dist/esm/shared-ts/src/authorization/index.js +1 -1
- package/dist/esm/shared-ts/src/authorization/{walletCustodyOperation.js → walletOwnerOperation.js} +4 -4
- package/dist/esm/shared-ts/src/wallet-recovery/index.js +3 -3
- package/dist/types/shared-ts/src/auth/externalAuth.d.ts +51 -8
- package/dist/types/shared-ts/src/authorization/index.d.ts +1 -1
- package/dist/types/shared-ts/src/authorization/walletOwnerOperation.d.ts +10 -0
- package/dist/types/shared-ts/src/utils/registrationEstablishedSession.d.ts +4 -0
- package/dist/types/shared-ts/src/utils/registrationIntent.d.ts +2 -0
- package/dist/types/shared-ts/src/utils/walletManagementProtocol.d.ts +1 -1
- package/dist/types/wallet-server/src/application-auth.d.ts +3 -0
- package/dist/types/wallet-server/src/auth/applicationAuth.d.ts +29 -0
- package/dist/types/wallet-server/src/auth/externalAuthService.d.ts +29 -10
- package/dist/types/wallet-server/src/auth/externalAuthStore.d.ts +60 -6
- package/dist/types/wallet-server/src/auth/externalOwnerOperations.d.ts +18 -0
- package/dist/types/wallet-server/src/cloud-host.d.ts +1 -1
- package/dist/types/wallet-server/src/core/threeRouteRegistrationContracts.d.ts +12 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/auth/d1AuthorizedOperationRouteService.d.ts +5 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/auth/d1RecordedOwnerOperation.d.ts +16 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.d.ts +1 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationSetup.d.ts +2 -0
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/walletRegistrationSessionCommitReceipt.d.ts +7 -1
- package/dist/types/wallet-server/src/router/cloudflare/d1/wallet/d1WalletAuthMethodService.d.ts +1 -0
- package/dist/types/wallet-server/src/router/domains/signingOperations/routerAbEd25519NormalSigningRoute.d.ts +2 -2
- package/dist/types/wallet-server/src/router/domains/signingOperations/routerAbOperationStepUp.d.ts +1 -1
- package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationInputs.d.ts +1 -0
- package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationReservation.d.ts +11 -6
- package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +11 -0
- package/dist/types/wallet-server/src/router/transport/fetch/walletOwnerOperationAuthorization.d.ts +23 -0
- package/migrations/d1-signer/0086_application_auth.sql +67 -0
- package/migrations/d1-signer/0087_registration_without_session.sql +43 -0
- package/package.json +7 -2
- package/dist/types/shared-ts/src/authorization/walletCustodyOperation.d.ts +0 -10
package/README.md
CHANGED
|
@@ -4,14 +4,22 @@ This package owns the reusable signer server surface: route adapters,
|
|
|
4
4
|
WebAuthn verification policy, threshold signing services, signer storage
|
|
5
5
|
adapters, and server wasm bindings.
|
|
6
6
|
|
|
7
|
-
The current
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The current release is `@seams/wallet-server@0.29.0`. Pair it with
|
|
8
|
+
`@seams/wallet@0.29.0` and matching hosted assets. Use `@seams/wallet-cli@0.29.0`.
|
|
9
|
+
The wallet iframe protocol is **19.0.0**.
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
pnpm add @seams/wallet-server@0.
|
|
12
|
+
pnpm add @seams/wallet-server@0.29.0
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Google BYO authentication
|
|
16
|
+
|
|
17
|
+
Version 0.29.0 verifies a configured application's signed session assertion or the app's Google ID token or exchanges it for a browser-bound handoff.
|
|
18
|
+
An environment-specific Google client configuration and an owner-approved wallet association are required.
|
|
19
|
+
Apply the packaged signer migrations before enabling the feature.
|
|
20
|
+
External sessions grant settings reads and remain separate from owner and signing credentials.
|
|
21
|
+
See the [Google SSO guide](https://wallet.seams.sh/docs/guides/google-sso) for configuration, backend exchange, and revocation.
|
|
22
|
+
|
|
15
23
|
## Supported runtime and entry points
|
|
16
24
|
|
|
17
25
|
The packaged Wallet backend runs on Cloudflare Workers with Durable Objects,
|
|
@@ -56,3 +64,10 @@ and [release notes](https://wallet.seams.sh/docs/releases).
|
|
|
56
64
|
|
|
57
65
|
Hosted product services and their operational tooling live in the private
|
|
58
66
|
`seams-monorepo` repository.
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
Use `createApplicationAuthAssertion` from `@seams/wallet-server/application-auth` on the authenticated application backend.
|
|
70
|
+
Configure the issuer and public key through the existing environment trust setting.
|
|
71
|
+
The assertion binds the stable subject, environment, wallet, Seams attempt, purpose, and expiry.
|
|
72
|
+
Fresh wallet-origin owner proof remains required for identity changes and NEAR transactions.
|
|
73
|
+
See the [application-session guide](https://wallet.seams.sh/docs/guides/application-auth).
|
package/artifact-manifest.json
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
"walletSecurityPolicy": "scoped_seals_and_owner_proofs_v1",
|
|
4
4
|
"package": {
|
|
5
5
|
"name": "@seams/wallet-server",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.29.0"
|
|
7
7
|
},
|
|
8
8
|
"workers": {
|
|
9
9
|
"router": {
|
|
10
10
|
"entry": "cloudflare-router-ab/build/router/worker/shim.mjs",
|
|
11
|
-
"fingerprint": "
|
|
11
|
+
"fingerprint": "bc4c3fb74b4869b98d280dff20ea71488b6e41d9de440d10d26b292360243673",
|
|
12
12
|
"files": [
|
|
13
13
|
{
|
|
14
14
|
"path": "cloudflare-router-ab/build/router/build-stamp.json",
|
|
15
15
|
"bytes": 119,
|
|
16
|
-
"sha256": "
|
|
16
|
+
"sha256": "f1f625e28fd7c2de2ba5d7a5f84ec06d21411d0fcb59a0d6ac687373c4a2a0fd"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "cloudflare-router-ab/build/router/index.js",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
},
|
|
40
40
|
"deriver-a": {
|
|
41
41
|
"entry": "cloudflare-router-ab/build/deriver-a/worker/shim.mjs",
|
|
42
|
-
"fingerprint": "
|
|
42
|
+
"fingerprint": "5ed3fc53704436a171fc57434e2ad732eb008cf12f1a30c3c2c0d895aaec2b36",
|
|
43
43
|
"files": [
|
|
44
44
|
{
|
|
45
45
|
"path": "cloudflare-router-ab/build/deriver-a/build-stamp.json",
|
|
46
46
|
"bytes": 122,
|
|
47
|
-
"sha256": "
|
|
47
|
+
"sha256": "70c82f021615ac41e7b6e68ec7f64c21ba5202d97463710f3184489c693e5d67"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"path": "cloudflare-router-ab/build/deriver-a/index.js",
|
|
@@ -70,12 +70,12 @@
|
|
|
70
70
|
},
|
|
71
71
|
"deriver-b": {
|
|
72
72
|
"entry": "cloudflare-router-ab/build/deriver-b/worker/shim.mjs",
|
|
73
|
-
"fingerprint": "
|
|
73
|
+
"fingerprint": "8861d75c70c0660f85abdb2fb1d5e05f346c0185180de6482c676b16188e1d89",
|
|
74
74
|
"files": [
|
|
75
75
|
{
|
|
76
76
|
"path": "cloudflare-router-ab/build/deriver-b/build-stamp.json",
|
|
77
77
|
"bytes": 122,
|
|
78
|
-
"sha256": "
|
|
78
|
+
"sha256": "01cd377ff0a7934d33794174f1f9ddfe3e274bcf14d850516e3b0482f7593986"
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
"path": "cloudflare-router-ab/build/deriver-b/index.js",
|
|
@@ -101,12 +101,12 @@
|
|
|
101
101
|
},
|
|
102
102
|
"signing-worker": {
|
|
103
103
|
"entry": "cloudflare-router-ab/build/signing-worker/worker/shim.mjs",
|
|
104
|
-
"fingerprint": "
|
|
104
|
+
"fingerprint": "c88383e59192e1d8fe7424cd29b4d2045cac3d5fdb6be31770e825ff25c59358",
|
|
105
105
|
"files": [
|
|
106
106
|
{
|
|
107
107
|
"path": "cloudflare-router-ab/build/signing-worker/build-stamp.json",
|
|
108
108
|
"bytes": 127,
|
|
109
|
-
"sha256": "
|
|
109
|
+
"sha256": "3ed8b25f8604a6d815c5c2f83d82c7775c1d360840ea6f3d58f1335d3ddc0f23"
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
112
|
"path": "cloudflare-router-ab/build/signing-worker/index.js",
|
|
@@ -132,12 +132,12 @@
|
|
|
132
132
|
},
|
|
133
133
|
"tenant-root-control-plane": {
|
|
134
134
|
"entry": "cloudflare-router-ab/build/tenant-root-control-plane/worker/shim.mjs",
|
|
135
|
-
"fingerprint": "
|
|
135
|
+
"fingerprint": "87fcae5b88541e0f69333ae632568e286939ec22bf75395d490be3ba6aeb4272",
|
|
136
136
|
"files": [
|
|
137
137
|
{
|
|
138
138
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json",
|
|
139
139
|
"bytes": 139,
|
|
140
|
-
"sha256": "
|
|
140
|
+
"sha256": "ff8878e625a5202b8d8607c590a89c2b169ae385c826eb154b5797babb3bf73d"
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"path": "cloudflare-router-ab/build/tenant-root-control-plane/index.js",
|
|
@@ -165,8 +165,8 @@
|
|
|
165
165
|
"migrations": {
|
|
166
166
|
"signer": {
|
|
167
167
|
"directory": "migrations/d1-signer",
|
|
168
|
-
"head": "
|
|
169
|
-
"fingerprint": "
|
|
168
|
+
"head": "0087_registration_without_session.sql",
|
|
169
|
+
"fingerprint": "2901c4c1b711fde38af1371311fea9e8ae8133c5cc4f47fc0cd5588e1ddf23aa",
|
|
170
170
|
"files": [
|
|
171
171
|
{
|
|
172
172
|
"path": "migrations/d1-signer/0001_signer_d1_initial.sql",
|
|
@@ -592,6 +592,16 @@
|
|
|
592
592
|
"path": "migrations/d1-signer/0085_external_wallet_auth.sql",
|
|
593
593
|
"bytes": 9757,
|
|
594
594
|
"sha256": "62e98c8200f18cc1d6bc399891f2fd7c57e5b0639f251d10ad6cf7d7b5e30461"
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
"path": "migrations/d1-signer/0086_application_auth.sql",
|
|
598
|
+
"bytes": 4354,
|
|
599
|
+
"sha256": "a9ecaeec04383536993cb9052aebe188a5035f766fd1ef73b51b9e85736a6bb1"
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
"path": "migrations/d1-signer/0087_registration_without_session.sql",
|
|
603
|
+
"bytes": 2906,
|
|
604
|
+
"sha256": "1ed454bb25fa5a9b28567bc1f18aad718c086f8b182255fabe2ee01215292ff3"
|
|
595
605
|
}
|
|
596
606
|
]
|
|
597
607
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-a-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-a-entrypoint","started_at_ms":1793310989490}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-b-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-deriver-b-entrypoint","started_at_ms":1793311149098}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-router-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-router-entrypoint","started_at_ms":1793311295695}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":true,"features":"strict-worker-signing-worker-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":true,"features":"strict-worker-signing-worker-entrypoint","started_at_ms":1793310743369}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"profile":"release","wallet_objects":false,"features":"strict-worker-tenant-root-control-plane-entrypoint","started_at_ms":
|
|
1
|
+
{"profile":"release","wallet_objects":false,"features":"strict-worker-tenant-root-control-plane-entrypoint","started_at_ms":1793311468276}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { requireCanonicalString, requireRecord } from "../shared-ts/src/utils/validation.js";
|
|
2
|
+
import { base64UrlDecode, base64UrlEncode } from "../shared-ts/src/utils/base64.js";
|
|
3
|
+
import { externalAuthFailure } from "../shared-ts/src/auth/externalAuth.js";
|
|
4
|
+
//#region src/auth/applicationAuth.ts
|
|
5
|
+
const ASSERTION_TTL_MS = 12e4;
|
|
6
|
+
function parseClaims(raw) {
|
|
7
|
+
const value = requireRecord(raw, "application assertion claims");
|
|
8
|
+
if (typeof value.iat !== "number" || typeof value.exp !== "number" || typeof value.sessionExp !== "number") throw new Error("Application assertion timestamps are missing");
|
|
9
|
+
if (!Number.isSafeInteger(value.iat) || !Number.isSafeInteger(value.exp) || !Number.isSafeInteger(value.sessionExp)) throw new Error("Application assertion timestamps must be integer seconds");
|
|
10
|
+
if (value.purpose !== "authenticate" && value.purpose !== "associate") throw new Error("Application assertion purpose is invalid");
|
|
11
|
+
return {
|
|
12
|
+
iss: requireCanonicalString(value.iss, "iss"),
|
|
13
|
+
sub: requireCanonicalString(value.sub, "sub"),
|
|
14
|
+
aud: requireCanonicalString(value.aud, "aud"),
|
|
15
|
+
walletId: requireCanonicalString(value.walletId, "walletId"),
|
|
16
|
+
attemptId: requireCanonicalString(value.attemptId, "attemptId"),
|
|
17
|
+
iat: value.iat,
|
|
18
|
+
exp: value.exp,
|
|
19
|
+
sessionExp: value.sessionExp,
|
|
20
|
+
purpose: value.purpose
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function encodeJson(value) {
|
|
24
|
+
return base64UrlEncode(new TextEncoder().encode(JSON.stringify(value)));
|
|
25
|
+
}
|
|
26
|
+
function decodeJson(value) {
|
|
27
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(base64UrlDecode(value)));
|
|
28
|
+
}
|
|
29
|
+
function audience(environmentId) {
|
|
30
|
+
return `seams:external-auth:${environmentId}`;
|
|
31
|
+
}
|
|
32
|
+
/** Called only by the application's authenticated backend with its enrolled issuer key. */
|
|
33
|
+
async function createApplicationAuthAssertion(input) {
|
|
34
|
+
if (input.privateKey.type !== "private" || input.privateKey.algorithm.name !== "Ed25519") throw new Error("Application issuer requires an Ed25519 signing key");
|
|
35
|
+
const claims = parseClaims({
|
|
36
|
+
iss: input.issuer,
|
|
37
|
+
sub: input.subject,
|
|
38
|
+
aud: audience(requireCanonicalString(input.environmentId, "environmentId")),
|
|
39
|
+
walletId: input.walletId,
|
|
40
|
+
attemptId: input.attemptId,
|
|
41
|
+
iat: Math.floor(input.nowMs / 1e3),
|
|
42
|
+
exp: Math.floor(Math.min(input.nowMs + ASSERTION_TTL_MS, input.sessionExpiresAtMs) / 1e3),
|
|
43
|
+
sessionExp: Math.floor(input.sessionExpiresAtMs / 1e3),
|
|
44
|
+
purpose: input.purpose
|
|
45
|
+
});
|
|
46
|
+
const signed = `${encodeJson({
|
|
47
|
+
alg: "EdDSA",
|
|
48
|
+
typ: "JWT"
|
|
49
|
+
})}.${encodeJson(claims)}`;
|
|
50
|
+
return `${signed}.${base64UrlEncode(await crypto.subtle.sign("Ed25519", input.privateKey, new TextEncoder().encode(signed)))}`;
|
|
51
|
+
}
|
|
52
|
+
async function verifyApplicationAuthAssertion(input) {
|
|
53
|
+
try {
|
|
54
|
+
const segments = input.assertion.split(".");
|
|
55
|
+
if (segments.length !== 3) return externalAuthFailure("invalid_credential");
|
|
56
|
+
const [headerText, claimsText, signatureText] = segments;
|
|
57
|
+
if (!headerText || !claimsText || !signatureText) return externalAuthFailure("invalid_credential");
|
|
58
|
+
const header = requireRecord(decodeJson(headerText), "application assertion header");
|
|
59
|
+
if (header.alg !== "EdDSA" || header.typ !== "JWT" || "crit" in header) return externalAuthFailure("invalid_credential");
|
|
60
|
+
const key = await crypto.subtle.importKey("raw", base64UrlDecode(input.trust.publicKeyB64u), "Ed25519", false, ["verify"]);
|
|
61
|
+
if (!await crypto.subtle.verify("Ed25519", key, base64UrlDecode(signatureText), new TextEncoder().encode(`${headerText}.${claimsText}`))) return externalAuthFailure("invalid_credential");
|
|
62
|
+
const claims = parseClaims(decodeJson(claimsText));
|
|
63
|
+
const expiresAtMs = claims.exp * 1e3;
|
|
64
|
+
const issuedAtMs = claims.iat * 1e3;
|
|
65
|
+
if (expiresAtMs <= input.nowMs) return externalAuthFailure("expired");
|
|
66
|
+
if (claims.iss !== input.trust.issuer || claims.aud !== audience(input.environmentId) || claims.walletId !== input.walletId || claims.attemptId !== input.attemptId || claims.purpose !== input.purpose || issuedAtMs > input.nowMs + 3e4 || expiresAtMs <= issuedAtMs || expiresAtMs - issuedAtMs > ASSERTION_TTL_MS || claims.sessionExp < claims.exp) return externalAuthFailure("invalid_credential");
|
|
67
|
+
return {
|
|
68
|
+
ok: true,
|
|
69
|
+
value: {
|
|
70
|
+
identity: {
|
|
71
|
+
provider: "application",
|
|
72
|
+
issuer: claims.iss,
|
|
73
|
+
subject: claims.sub
|
|
74
|
+
},
|
|
75
|
+
expiresAtMs: claims.sessionExp * 1e3
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
} catch {
|
|
79
|
+
return externalAuthFailure("invalid_credential");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { createApplicationAuthAssertion, verifyApplicationAuthAssertion };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=applicationAuth.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { base64UrlEncode } from "../shared-ts/src/utils/base64.js";
|
|
2
2
|
import { sha256HexUtf8 } from "../shared-ts/src/utils/digests.js";
|
|
3
3
|
import { externalAuthFailure } from "../shared-ts/src/auth/externalAuth.js";
|
|
4
|
+
import { verifyApplicationAuthAssertion } from "./applicationAuth.js";
|
|
4
5
|
import { verifyGoogleOidcToken } from "../router/cloudflare/d1/oidc/googleOidcToken.js";
|
|
5
6
|
//#region src/auth/externalAuthService.ts
|
|
6
7
|
var ExternalAuthService = class {
|
|
@@ -16,9 +17,12 @@ var ExternalAuthService = class {
|
|
|
16
17
|
return {
|
|
17
18
|
ok: true,
|
|
18
19
|
value: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
identity: {
|
|
21
|
+
provider: "google",
|
|
22
|
+
issuer: "https://accounts.google.com",
|
|
23
|
+
subject: proof.sub
|
|
24
|
+
},
|
|
25
|
+
expiresAtMs: proof.expiresAtMs
|
|
22
26
|
}
|
|
23
27
|
};
|
|
24
28
|
}
|
|
@@ -26,6 +30,7 @@ var ExternalAuthService = class {
|
|
|
26
30
|
const attemptId = crypto.randomUUID();
|
|
27
31
|
const expiresAtMs = Date.now() + 12e4;
|
|
28
32
|
await this.store.prepareAttempt({
|
|
33
|
+
purpose: input.purpose,
|
|
29
34
|
attemptId,
|
|
30
35
|
walletId: input.walletId,
|
|
31
36
|
originBinding: input.originBinding,
|
|
@@ -36,6 +41,7 @@ var ExternalAuthService = class {
|
|
|
36
41
|
return {
|
|
37
42
|
ok: true,
|
|
38
43
|
value: {
|
|
44
|
+
purpose: input.purpose,
|
|
39
45
|
attemptId,
|
|
40
46
|
walletId: input.walletId,
|
|
41
47
|
expiresAtMs
|
|
@@ -43,23 +49,68 @@ var ExternalAuthService = class {
|
|
|
43
49
|
};
|
|
44
50
|
}
|
|
45
51
|
async exchange(input) {
|
|
46
|
-
const
|
|
52
|
+
const purpose = await this.store.readPreparedPurpose(input.walletId, input.attemptId, Date.now());
|
|
53
|
+
if (purpose === null) return externalAuthFailure("access_denied");
|
|
54
|
+
const verified = await this.verifyIdentity({
|
|
55
|
+
trust: input.trust,
|
|
56
|
+
proof: input.proof,
|
|
57
|
+
walletId: input.walletId,
|
|
58
|
+
attemptId: input.attemptId,
|
|
59
|
+
purpose
|
|
60
|
+
});
|
|
47
61
|
if (!verified.ok) return verified;
|
|
48
62
|
const code = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));
|
|
49
63
|
const nowMs = Date.now();
|
|
50
64
|
return await this.store.verifyAttempt({
|
|
51
65
|
attemptId: input.attemptId,
|
|
52
66
|
walletId: input.walletId,
|
|
53
|
-
|
|
54
|
-
trust:
|
|
67
|
+
identity: verified.value.identity,
|
|
68
|
+
trust: input.trust,
|
|
55
69
|
codeHash: await sha256HexUtf8(code),
|
|
56
70
|
nowMs,
|
|
57
|
-
sessionExpiresAtMs: Math.min(nowMs + 36e5, verified.value.expiresAtMs)
|
|
71
|
+
sessionExpiresAtMs: Math.min(nowMs + 36e5, verified.value.expiresAtMs),
|
|
72
|
+
purpose
|
|
58
73
|
}) ? {
|
|
59
74
|
ok: true,
|
|
60
75
|
value: { code }
|
|
61
76
|
} : externalAuthFailure("access_denied");
|
|
62
77
|
}
|
|
78
|
+
async prepareUnlink(input) {
|
|
79
|
+
const attemptId = crypto.randomUUID();
|
|
80
|
+
const code = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));
|
|
81
|
+
const expiresAtMs = Date.now() + 12e4;
|
|
82
|
+
return await this.store.prepareUnlink({
|
|
83
|
+
walletId: input.walletId,
|
|
84
|
+
associationId: input.associationId,
|
|
85
|
+
attemptId,
|
|
86
|
+
originBinding: input.originBinding,
|
|
87
|
+
bindingHash: await sha256HexUtf8(input.binding),
|
|
88
|
+
sessionTokenHash: await sha256HexUtf8(input.sessionToken),
|
|
89
|
+
codeHash: await sha256HexUtf8(code),
|
|
90
|
+
expiresAtMs,
|
|
91
|
+
trustRevision: input.trustRevision
|
|
92
|
+
}) ? {
|
|
93
|
+
ok: true,
|
|
94
|
+
value: {
|
|
95
|
+
attemptId,
|
|
96
|
+
code,
|
|
97
|
+
expiresAtMs
|
|
98
|
+
}
|
|
99
|
+
} : externalAuthFailure("access_denied");
|
|
100
|
+
}
|
|
101
|
+
async verifyIdentity(input) {
|
|
102
|
+
if (input.trust.kind === "google" && input.proof.kind === "google") return this.verifyGoogle(input.trust, input.proof.idToken);
|
|
103
|
+
if (input.trust.kind === "application" && input.proof.kind === "application") return verifyApplicationAuthAssertion({
|
|
104
|
+
trust: input.trust,
|
|
105
|
+
assertion: input.proof.assertion,
|
|
106
|
+
environmentId: this.store.scope.envId,
|
|
107
|
+
walletId: input.walletId,
|
|
108
|
+
attemptId: input.attemptId,
|
|
109
|
+
nowMs: Date.now(),
|
|
110
|
+
purpose: input.purpose
|
|
111
|
+
});
|
|
112
|
+
return externalAuthFailure("invalid_credential");
|
|
113
|
+
}
|
|
63
114
|
async settings(authentication) {
|
|
64
115
|
const walletId = authentication.walletId;
|
|
65
116
|
const [ed25519, ecdsa, methods, devices] = await Promise.all([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { requireCanonicalString } from "../shared-ts/src/utils/validation.js";
|
|
2
2
|
import { parseUnixMs } from "../shared-ts/src/passkey-custody/primitives.js";
|
|
3
|
-
import { parseExternalWalletAuthentication } from "../shared-ts/src/auth/externalAuth.js";
|
|
3
|
+
import { parseExternalWalletAuthentication, parseStoredExternalIdentity, serializeExternalIdentity } from "../shared-ts/src/auth/externalAuth.js";
|
|
4
4
|
import { d1ChangedRows } from "../storage/d1Sql.js";
|
|
5
5
|
import { prepareD1TenantStatement } from "../core/d1TenantStore.js";
|
|
6
6
|
//#region src/auth/externalAuthStore.ts
|
|
@@ -28,6 +28,11 @@ const LIVE_ASSOCIATION = `EXISTS (
|
|
|
28
28
|
AND w.lifecycle_state = 'active' AND w.revocation_epoch = a.authority_epoch
|
|
29
29
|
AND ${ACTIVE_HOME}
|
|
30
30
|
)`;
|
|
31
|
+
const AUTHENTICATION_COLUMNS = `attempt_id, association_id, wallet_id, session_expires_at_ms,
|
|
32
|
+
(SELECT a.provider_subject FROM external_auth_associations a
|
|
33
|
+
WHERE a.namespace = external_auth_attempts.namespace AND a.org_id = external_auth_attempts.org_id
|
|
34
|
+
AND a.project_id = external_auth_attempts.project_id AND a.env_id = external_auth_attempts.env_id
|
|
35
|
+
AND a.association_id = external_auth_attempts.association_id) AS provider_subject`;
|
|
31
36
|
var ExternalAuthStore = class {
|
|
32
37
|
constructor(database, scope) {
|
|
33
38
|
this.database = database;
|
|
@@ -50,6 +55,7 @@ var ExternalAuthStore = class {
|
|
|
50
55
|
]).run()) === 1;
|
|
51
56
|
}
|
|
52
57
|
async associate(input) {
|
|
58
|
+
const subject = serializeExternalIdentity(input.identity);
|
|
53
59
|
const revokePriorAuthority = this.prepare(`UPDATE external_auth_associations SET status = 'revoked'
|
|
54
60
|
WHERE ${SCOPE} AND wallet_id = ?5 AND provider_subject = ?6 AND status = 'active'
|
|
55
61
|
AND (authority_id != ?7 OR authority_epoch != ?8)
|
|
@@ -57,7 +63,7 @@ var ExternalAuthStore = class {
|
|
|
57
63
|
AND wallet_id = ?5 AND authority_id = ?7 AND lifecycle_state = 'active'
|
|
58
64
|
AND revocation_epoch = ?8)`, [
|
|
59
65
|
input.walletId,
|
|
60
|
-
|
|
66
|
+
subject,
|
|
61
67
|
input.authorityId,
|
|
62
68
|
input.authorityEpoch
|
|
63
69
|
]);
|
|
@@ -72,7 +78,7 @@ var ExternalAuthStore = class {
|
|
|
72
78
|
WHERE status = 'active' DO NOTHING`, [
|
|
73
79
|
crypto.randomUUID(),
|
|
74
80
|
input.walletId,
|
|
75
|
-
|
|
81
|
+
subject,
|
|
76
82
|
input.authorityId,
|
|
77
83
|
input.authorityEpoch
|
|
78
84
|
]);
|
|
@@ -86,14 +92,128 @@ var ExternalAuthStore = class {
|
|
|
86
92
|
AND wallet_id = ?5 AND authority_id = ?7 AND lifecycle_state = 'active'
|
|
87
93
|
AND revocation_epoch = ?8)`, [
|
|
88
94
|
input.walletId,
|
|
89
|
-
|
|
95
|
+
subject,
|
|
90
96
|
input.authorityId,
|
|
91
97
|
input.authorityEpoch
|
|
92
98
|
]).first() !== null;
|
|
93
99
|
}
|
|
94
|
-
async revokeAssociation(
|
|
100
|
+
async revokeAssociation(input) {
|
|
95
101
|
await this.prepare(`UPDATE external_auth_associations SET status = 'revoked'
|
|
96
|
-
WHERE ${SCOPE} AND wallet_id = ?5 AND association_id = ?6
|
|
102
|
+
WHERE ${SCOPE} AND wallet_id = ?5 AND association_id = ?6
|
|
103
|
+
AND EXISTS (SELECT 1 FROM wallet_authorities WHERE ${SCOPE}
|
|
104
|
+
AND wallet_id = ?5 AND authority_id = ?7 AND revocation_epoch = ?8 AND lifecycle_state = 'active')`, [
|
|
105
|
+
input.walletId,
|
|
106
|
+
input.associationId,
|
|
107
|
+
input.owner.authorityId,
|
|
108
|
+
input.owner.revocationEpoch
|
|
109
|
+
]).run();
|
|
110
|
+
}
|
|
111
|
+
async prepareUnlink(input) {
|
|
112
|
+
return d1ChangedRows(await this.prepare(`INSERT INTO external_auth_attempts
|
|
113
|
+
(namespace, org_id, project_id, env_id, attempt_id, wallet_id, origin, binding_hash,
|
|
114
|
+
session_token_hash, expires_at_ms, status, purpose, association_id, identity_subject, code_hash, trust_revision)
|
|
115
|
+
SELECT ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, 'verified', 'unlink', ?11, provider_subject, ?12, ?13
|
|
116
|
+
FROM external_auth_associations WHERE ${SCOPE} AND wallet_id = ?6 AND association_id = ?11`, [
|
|
117
|
+
input.attemptId,
|
|
118
|
+
input.walletId,
|
|
119
|
+
input.originBinding,
|
|
120
|
+
input.bindingHash,
|
|
121
|
+
input.sessionTokenHash,
|
|
122
|
+
input.expiresAtMs,
|
|
123
|
+
input.associationId,
|
|
124
|
+
input.codeHash,
|
|
125
|
+
input.trustRevision
|
|
126
|
+
]).run()) === 1;
|
|
127
|
+
}
|
|
128
|
+
async readOwnerAttempt(input) {
|
|
129
|
+
const row = await this.prepare(`SELECT purpose, status, identity_subject, association_id, expires_at_ms, owner_challenge_expires_at_ms
|
|
130
|
+
FROM external_auth_attempts WHERE ${SCOPE} AND wallet_id = ?5 AND attempt_id = ?6
|
|
131
|
+
AND origin = ?7 AND binding_hash = ?8 AND session_token_hash = ?9 AND code_hash = ?10
|
|
132
|
+
AND trust_revision = ?11 AND expires_at_ms > ?12 AND status IN ('verified', 'consumed')
|
|
133
|
+
AND purpose IN ('associate', 'unlink')`, [
|
|
134
|
+
input.walletId,
|
|
135
|
+
input.attemptId,
|
|
136
|
+
input.originBinding,
|
|
137
|
+
input.bindingHash,
|
|
138
|
+
input.sessionTokenHash,
|
|
139
|
+
input.codeHash,
|
|
140
|
+
input.trustRevision,
|
|
141
|
+
input.nowMs
|
|
142
|
+
]).first();
|
|
143
|
+
if (!row) return null;
|
|
144
|
+
if (row.status !== "verified" && row.status !== "consumed") throw new Error("Invalid owner attempt state");
|
|
145
|
+
const status = row.status;
|
|
146
|
+
const identity = parseStoredExternalIdentity(requireCanonicalString(row.identity_subject, "identity_subject"));
|
|
147
|
+
const expiresAtMs = parseUnixMs(row.expires_at_ms, "attempt expiry");
|
|
148
|
+
const challenge = row.owner_challenge_expires_at_ms === null ? { kind: "unprepared" } : {
|
|
149
|
+
kind: "ready",
|
|
150
|
+
expiresAtMs: parseUnixMs(row.owner_challenge_expires_at_ms, "owner challenge expiry")
|
|
151
|
+
};
|
|
152
|
+
switch (row.purpose) {
|
|
153
|
+
case "associate": return {
|
|
154
|
+
purpose: "associate",
|
|
155
|
+
walletId: input.walletId,
|
|
156
|
+
attemptId: input.attemptId,
|
|
157
|
+
identity,
|
|
158
|
+
originBinding: input.originBinding,
|
|
159
|
+
trustRevision: input.trustRevision,
|
|
160
|
+
expiresAtMs,
|
|
161
|
+
status,
|
|
162
|
+
challenge
|
|
163
|
+
};
|
|
164
|
+
case "unlink": return {
|
|
165
|
+
purpose: "unlink",
|
|
166
|
+
walletId: input.walletId,
|
|
167
|
+
attemptId: input.attemptId,
|
|
168
|
+
identity,
|
|
169
|
+
originBinding: input.originBinding,
|
|
170
|
+
trustRevision: input.trustRevision,
|
|
171
|
+
expiresAtMs,
|
|
172
|
+
status,
|
|
173
|
+
challenge,
|
|
174
|
+
associationId: requireCanonicalString(row.association_id, "association_id")
|
|
175
|
+
};
|
|
176
|
+
default: throw new Error("Invalid owner attempt purpose");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async prepareOwnerChallenge(input) {
|
|
180
|
+
await this.prepare(`UPDATE external_auth_attempts
|
|
181
|
+
SET owner_challenge_expires_at_ms = COALESCE(owner_challenge_expires_at_ms, MIN(expires_at_ms, ?13))
|
|
182
|
+
WHERE ${SCOPE} AND wallet_id = ?5 AND attempt_id = ?6 AND origin = ?7
|
|
183
|
+
AND binding_hash = ?8 AND session_token_hash = ?9 AND code_hash = ?10
|
|
184
|
+
AND trust_revision = ?11 AND expires_at_ms > ?12 AND status = 'verified'
|
|
185
|
+
AND purpose IN ('associate', 'unlink')`, [
|
|
186
|
+
input.walletId,
|
|
187
|
+
input.attemptId,
|
|
188
|
+
input.originBinding,
|
|
189
|
+
input.bindingHash,
|
|
190
|
+
input.sessionTokenHash,
|
|
191
|
+
input.codeHash,
|
|
192
|
+
input.trustRevision,
|
|
193
|
+
input.nowMs,
|
|
194
|
+
input.nowMs + 6e4
|
|
195
|
+
]).run();
|
|
196
|
+
return this.readOwnerAttempt(input);
|
|
197
|
+
}
|
|
198
|
+
async consumeOwnerAttempt(input, owner) {
|
|
199
|
+
return d1ChangedRows(await this.prepare(`UPDATE external_auth_attempts SET status = 'consumed'
|
|
200
|
+
WHERE ${SCOPE} AND wallet_id = ?5 AND attempt_id = ?6 AND origin = ?7
|
|
201
|
+
AND binding_hash = ?8 AND session_token_hash = ?9 AND code_hash = ?10
|
|
202
|
+
AND trust_revision = ?11 AND expires_at_ms > ?12 AND owner_challenge_expires_at_ms > ?12
|
|
203
|
+
AND status = 'verified' AND purpose IN ('associate', 'unlink')
|
|
204
|
+
AND EXISTS (SELECT 1 FROM wallet_authorities WHERE ${SCOPE}
|
|
205
|
+
AND wallet_id = ?5 AND authority_id = ?13 AND revocation_epoch = ?14 AND lifecycle_state = 'active')`, [
|
|
206
|
+
input.walletId,
|
|
207
|
+
input.attemptId,
|
|
208
|
+
input.originBinding,
|
|
209
|
+
input.bindingHash,
|
|
210
|
+
input.sessionTokenHash,
|
|
211
|
+
input.codeHash,
|
|
212
|
+
input.trustRevision,
|
|
213
|
+
input.nowMs,
|
|
214
|
+
owner.authorityId,
|
|
215
|
+
owner.revocationEpoch
|
|
216
|
+
]).run()) === 1;
|
|
97
217
|
}
|
|
98
218
|
async prepareAttempt(input) {
|
|
99
219
|
const nowMs = Date.now();
|
|
@@ -105,16 +225,31 @@ var ExternalAuthStore = class {
|
|
|
105
225
|
)`).bind(nowMs)]);
|
|
106
226
|
await this.prepare(`INSERT INTO external_auth_attempts
|
|
107
227
|
(namespace, org_id, project_id, env_id, attempt_id, wallet_id, origin,
|
|
108
|
-
binding_hash, session_token_hash, expires_at_ms, status)
|
|
109
|
-
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, 'prepared')`, [
|
|
228
|
+
binding_hash, session_token_hash, expires_at_ms, status, purpose)
|
|
229
|
+
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, 'prepared', ?11)`, [
|
|
110
230
|
input.attemptId,
|
|
111
231
|
input.walletId,
|
|
112
232
|
input.originBinding,
|
|
113
233
|
input.bindingHash,
|
|
114
234
|
input.sessionTokenHash,
|
|
115
|
-
input.expiresAtMs
|
|
235
|
+
input.expiresAtMs,
|
|
236
|
+
input.purpose
|
|
116
237
|
]).run();
|
|
117
238
|
}
|
|
239
|
+
async readPreparedPurpose(walletId, attemptId, nowMs) {
|
|
240
|
+
const row = await this.prepare(`SELECT purpose FROM external_auth_attempts WHERE ${SCOPE} AND wallet_id = ?5
|
|
241
|
+
AND attempt_id = ?6 AND status = 'prepared' AND expires_at_ms > ?7`, [
|
|
242
|
+
walletId,
|
|
243
|
+
attemptId,
|
|
244
|
+
nowMs
|
|
245
|
+
]).first();
|
|
246
|
+
if (!row) return null;
|
|
247
|
+
switch (row.purpose) {
|
|
248
|
+
case "authenticate":
|
|
249
|
+
case "associate": return row.purpose;
|
|
250
|
+
default: return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
118
253
|
async verifyAttempt(input) {
|
|
119
254
|
return d1ChangedRows(await this.prepare(`UPDATE external_auth_attempts SET status = 'verified',
|
|
120
255
|
association_id = (SELECT a.association_id FROM external_auth_associations a
|
|
@@ -125,31 +260,32 @@ var ExternalAuthStore = class {
|
|
|
125
260
|
AND a.wallet_id = ?6 AND a.provider_subject = ?7 AND a.status = 'active'
|
|
126
261
|
AND w.lifecycle_state = 'active' AND w.revocation_epoch = a.authority_epoch
|
|
127
262
|
AND ${ACTIVE_HOME}),
|
|
128
|
-
trust_revision = ?8, code_hash = ?9, session_expires_at_ms = ?11
|
|
263
|
+
trust_revision = ?8, code_hash = ?9, session_expires_at_ms = ?11, identity_subject = ?7
|
|
129
264
|
WHERE ${SCOPE} AND attempt_id = ?5 AND wallet_id = ?6
|
|
130
|
-
AND status = 'prepared' AND expires_at_ms > ?10
|
|
131
|
-
AND EXISTS (SELECT 1 FROM external_auth_associations a
|
|
265
|
+
AND status = 'prepared' AND expires_at_ms > ?10 AND purpose = ?12
|
|
266
|
+
AND (purpose = 'associate' OR EXISTS (SELECT 1 FROM external_auth_associations a
|
|
132
267
|
JOIN wallet_authorities w ON w.namespace = a.namespace AND w.org_id = a.org_id
|
|
133
268
|
AND w.project_id = a.project_id AND w.env_id = a.env_id
|
|
134
269
|
AND w.authority_id = a.authority_id AND w.wallet_id = a.wallet_id
|
|
135
270
|
WHERE a.namespace = ?1 AND a.org_id = ?2 AND a.project_id = ?3 AND a.env_id = ?4
|
|
136
271
|
AND a.wallet_id = ?6 AND a.provider_subject = ?7 AND a.status = 'active'
|
|
137
272
|
AND w.lifecycle_state = 'active' AND w.revocation_epoch = a.authority_epoch
|
|
138
|
-
AND ${ACTIVE_HOME})`, [
|
|
273
|
+
AND ${ACTIVE_HOME}))`, [
|
|
139
274
|
input.attemptId,
|
|
140
275
|
input.walletId,
|
|
141
|
-
input.
|
|
276
|
+
serializeExternalIdentity(input.identity),
|
|
142
277
|
input.trust.revision,
|
|
143
278
|
input.codeHash,
|
|
144
279
|
input.nowMs,
|
|
145
|
-
input.sessionExpiresAtMs
|
|
280
|
+
input.sessionExpiresAtMs,
|
|
281
|
+
input.purpose
|
|
146
282
|
]).run()) === 1;
|
|
147
283
|
}
|
|
148
284
|
async redeem(input) {
|
|
149
285
|
await this.prepare(`UPDATE external_auth_attempts SET status = 'consumed'
|
|
150
286
|
WHERE ${SCOPE} AND attempt_id = ?5 AND wallet_id = ?6 AND origin = ?7
|
|
151
287
|
AND binding_hash = ?8 AND session_token_hash = ?9 AND code_hash = ?10
|
|
152
|
-
AND status = 'verified' AND expires_at_ms > ?11 AND session_expires_at_ms > ?11
|
|
288
|
+
AND purpose = 'authenticate' AND status = 'verified' AND expires_at_ms > ?11 AND session_expires_at_ms > ?11
|
|
153
289
|
AND trust_revision = ?12 AND ${LIVE_ASSOCIATION}`, [
|
|
154
290
|
input.attemptId,
|
|
155
291
|
input.walletId,
|
|
@@ -160,10 +296,10 @@ var ExternalAuthStore = class {
|
|
|
160
296
|
input.nowMs,
|
|
161
297
|
input.trustRevision
|
|
162
298
|
]).run();
|
|
163
|
-
const row = await this.prepare(`SELECT
|
|
299
|
+
const row = await this.prepare(`SELECT ${AUTHENTICATION_COLUMNS}
|
|
164
300
|
FROM external_auth_attempts WHERE ${SCOPE} AND attempt_id = ?5 AND wallet_id = ?6
|
|
165
301
|
AND origin = ?7 AND binding_hash = ?8 AND session_token_hash = ?9 AND code_hash = ?10
|
|
166
|
-
AND status = 'consumed' AND session_expires_at_ms > ?11 AND trust_revision = ?12 AND ${LIVE_ASSOCIATION}`, [
|
|
302
|
+
AND purpose = 'authenticate' AND status = 'consumed' AND session_expires_at_ms > ?11 AND trust_revision = ?12 AND ${LIVE_ASSOCIATION}`, [
|
|
167
303
|
input.attemptId,
|
|
168
304
|
input.walletId,
|
|
169
305
|
input.originBinding,
|
|
@@ -176,9 +312,9 @@ var ExternalAuthStore = class {
|
|
|
176
312
|
return row ? this.authentication(row) : null;
|
|
177
313
|
}
|
|
178
314
|
async readSession(input) {
|
|
179
|
-
const row = await this.prepare(`SELECT
|
|
315
|
+
const row = await this.prepare(`SELECT ${AUTHENTICATION_COLUMNS}
|
|
180
316
|
FROM external_auth_attempts WHERE ${SCOPE} AND session_token_hash = ?5 AND origin = ?6
|
|
181
|
-
AND status = 'consumed' AND session_expires_at_ms > ?7
|
|
317
|
+
AND purpose = 'authenticate' AND status = 'consumed' AND session_expires_at_ms > ?7
|
|
182
318
|
AND trust_revision = ?8 AND ${LIVE_ASSOCIATION}`, [
|
|
183
319
|
input.tokenHash,
|
|
184
320
|
input.originBinding,
|
|
@@ -205,7 +341,7 @@ var ExternalAuthStore = class {
|
|
|
205
341
|
sessionId: row.attempt_id,
|
|
206
342
|
associationId: row.association_id,
|
|
207
343
|
environmentId: this.scope.envId,
|
|
208
|
-
provider: "
|
|
344
|
+
provider: parseStoredExternalIdentity(requireCanonicalString(row.provider_subject, "provider_subject")).provider,
|
|
209
345
|
permission: "wallet_settings_read",
|
|
210
346
|
expiresAtMs: parseUnixMs(row.session_expires_at_ms, "session expiry")
|
|
211
347
|
});
|