Forward wallet session tokens in launcher API calls

This commit is contained in:
Joshua 2026-02-18 20:15:39 -08:00
parent b0c54660fb
commit 42bccf5ed2
3 changed files with 41 additions and 2 deletions

View File

@ -54,6 +54,7 @@ Wallet automation helpers remain available in advanced controls:
3. `Sign payer proof` signs distinct-payer ownership proof and fills `payerProof`. 3. `Sign payer proof` signs distinct-payer ownership proof and fills `payerProof`.
4. `Send membership tx` submits the quote transaction via `eth_sendTransaction` and fills `confirmTxHash`. 4. `Send membership tx` submits the quote transaction via `eth_sendTransaction` and fills `confirmTxHash`.
5. Membership confirm can optionally attach on-ramp attestation fields (`identity_assurance_level`, `identity_attested_by`, `identity_attestation_id`) for provider-integrated flows. 5. Membership confirm can optionally attach on-ramp attestation fields (`identity_assurance_level`, `identity_attested_by`, `identity_attestation_id`) for provider-integrated flows.
6. Wallet verify returns a session token; launcher forwards it on marketplace/member/governance API calls via bearer + `X-Edut-Session`.
Policy behavior in launcher shell: Policy behavior in launcher shell:

View File

@ -9,6 +9,8 @@ const state = {
lastCheckoutQuote: null, lastCheckoutQuote: null,
lastStatus: null, lastStatus: null,
channelReady: false, channelReady: false,
walletSessionToken: "",
walletSessionExpiresAt: "",
}; };
function nowISO() { function nowISO() {
@ -133,9 +135,14 @@ function sleep(ms) {
} }
async function request(method, path, body) { async function request(method, path, body) {
const headers = { "Content-Type": "application/json" };
if (state.walletSessionToken) {
headers["X-Edut-Session"] = state.walletSessionToken;
headers.Authorization = `Bearer ${state.walletSessionToken}`;
}
const opts = { const opts = {
method, method,
headers: { "Content-Type": "application/json" }, headers,
}; };
if (body !== undefined) { if (body !== undefined) {
opts.body = JSON.stringify(body); opts.body = JSON.stringify(body);
@ -260,12 +267,22 @@ function buildIntentTypedData(intent, origin) {
} }
async function onConnectWallet() { async function onConnectWallet() {
const previousWallet = normalizedAddress($("walletAddress").value);
const provider = await requireProvider(); const provider = await requireProvider();
const accounts = await provider.request({ method: "eth_requestAccounts" }); const accounts = await provider.request({ method: "eth_requestAccounts" });
if (!accounts || accounts.length === 0) { if (!accounts || accounts.length === 0) {
throw new Error("wallet provider returned no accounts"); throw new Error("wallet provider returned no accounts");
} }
$("walletAddress").value = normalizedAddress(accounts[0]); const nextWallet = normalizedAddress(accounts[0]);
$("walletAddress").value = nextWallet;
if (previousWallet && previousWallet !== nextWallet) {
state.walletSessionToken = "";
state.walletSessionExpiresAt = "";
logLine("wallet session reset", {
previous_wallet: previousWallet,
next_wallet: nextWallet,
});
}
const chainHex = await provider.request({ method: "eth_chainId" }); const chainHex = await provider.request({ method: "eth_chainId" });
const providerChainID = Number.parseInt(chainHex, 16); const providerChainID = Number.parseInt(chainHex, 16);
if (Number.isFinite(providerChainID) && providerChainID !== chainID()) { if (Number.isFinite(providerChainID) && providerChainID !== chainID()) {
@ -400,7 +417,15 @@ async function onVerify() {
if (out.designation_code) { if (out.designation_code) {
$("designationCode").value = out.designation_code; $("designationCode").value = out.designation_code;
} }
state.walletSessionToken = String(out.session_token || "").trim();
state.walletSessionExpiresAt = String(out.session_expires_at || "").trim();
logLine("wallet verify", out); logLine("wallet verify", out);
if (state.walletSessionToken) {
logLine("wallet session active", {
wallet: requireWallet(),
session_expires_at: state.walletSessionExpiresAt || "unknown",
});
}
refreshOverview(); refreshOverview();
} }

View File

@ -18,6 +18,19 @@ Launcher integrates with EDUT web/backend contracts as follows:
12. `GET /governance/install/status` 12. `GET /governance/install/status`
13. `GET /member/channel/events` 13. `GET /member/channel/events`
## Wallet Session Contract
1. `POST /secret/wallet/verify` returns `session_token` and `session_expires_at`.
2. Launcher must attach session token on wallet-scoped calls using:
- `Authorization: Bearer <session_token>` (preferred)
- `X-Edut-Session: <session_token>` (compatibility)
3. Wallet change must clear cached session token before further calls.
4. Endpoints that require membership/admin authority can fail with:
- `wallet_session_required`
- `wallet_session_invalid`
- `wallet_session_expired`
- `wallet_session_mismatch`
## Deterministic Requirements ## Deterministic Requirements
1. No runtime activation without entitlement proof. 1. No runtime activation without entitlement proof.