Forward wallet session tokens in launcher API calls
This commit is contained in:
parent
b0c54660fb
commit
42bccf5ed2
@ -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:
|
||||||
|
|
||||||
|
|||||||
29
app/app.js
29
app/app.js
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user