diff --git a/api/auth/verify.js b/api/auth/verify.js index c154dbb..22e3583 100644 --- a/api/auth/verify.js +++ b/api/auth/verify.js @@ -2,7 +2,7 @@ const { URL } = require('node:url'); -const REQUIRED_STATEMENT = 'CommandLayer Claim activation'; +const REQUIRED_STATEMENT = 'Authenticate with CommandLayer Claim activation.'; function isDev() { return process.env.NODE_ENV === 'development'; diff --git a/public/claim.html b/public/claim.html index b49852e..ec796d1 100644 --- a/public/claim.html +++ b/public/claim.html @@ -253,13 +253,13 @@

Bring one ENS name.
Generate 10 verifiable agents.
-
1
ENS Name
+
1
Sign-In
2
Mode
-
3
Capabilities
-
4
Generate Key
-
5
ENS Records
-
6
SDK Config
-
7
Agent Card
+
3
ENS Name
+
4
Capabilities
+
5
Generate Key
+
6
ENS Records
+
7
SDK + Card
8
Summary
@@ -267,16 +267,18 @@

Bring one ENS name.
Generate 10 verifiable agents.
Step 1 of 8
-
Enter your ENS name
-
This is your organization or agent identity. You will choose how capability sub-agents are structured in the next step.
-
- - - Please enter a valid .eth name - Must end in .eth · The name you own or control +
Sign in with Ethereum
+
Authenticate the wallet that will request this CommandLayer namespace activation.
+
+
Your Ethereum wallet authenticates the claim request.
Your Ed25519 key signs agent receipts later in the flow.
+ +
Not connected
+
+ +
- +
@@ -326,8 +328,8 @@

Bring one ENS name.
Generate 10 verifiable agents.
Step 3 of 8
-
Choose capabilities
-
Select a recommended pack of 10 verbs, or cherry-pick up to 10 from any group.
+
Enter tenant / ENS name
+
This is your organization or agent identity used for namespace generation.
@@ -337,6 +339,12 @@

Bring one ENS name.
Generate 10 verifiable agents.Selected: 0 / 10

+
+ + + Please enter a valid .eth name + Must end in .eth · The name you own or control +
@@ -429,15 +437,7 @@

Browser-side key generation

Payment/provisioning is required for CommandLayer-native namespace activation.
-
-
Sign in with Ethereum
-
Authenticate the wallet that will submit this activation request.
-
Your Ethereum wallet authenticates the claim request. Your Ed25519 key signs agent receipts.
- -
Status: Not authenticated
-
-
-
+
@@ -585,7 +585,7 @@

Payment and provisioning are coming next.

pubKeyB64:'', privKeyB64:'', kid:'', keyGenerated:false, keyDownloaded:false, keyAcked:false, _cardJson:'', - authenticatedAddress:'', authStatus:'NOT_AUTHENTICATED', authChainId:null + authenticatedAddress:'', authStatus:'NOT_AUTHENTICATED', authChainId:null, siweAuthenticated:false }; // ── HELPERS ─────────────────────────────────────────────────────────────────── @@ -644,24 +644,10 @@

Payment and provisioning are coming next.

// ── STEP 1 ──────────────────────────────────────────────────────────────────── function goStep1() { - const val = document.getElementById('ensInput').value.trim().toLowerCase(); - const err = document.getElementById('ensError'); - if (!val.endsWith('.eth') || val.length < 6) { - err.classList.add('show'); - document.getElementById('ensInput').classList.add('error'); - return; - } - err.classList.remove('show'); - document.getElementById('ensInput').classList.remove('error'); - document.getElementById('ensInput').classList.add('valid'); - state.ens = val; - // update mode examples - updateModeExamples(); - buildPacksGrid(); - buildCherryGroups(); + if (!state.siweAuthenticated) return; goToStep(2); } -document.getElementById('ensInput').addEventListener('keydown', e => { if(e.key==='Enter') goStep1(); }); +document.getElementById('ensInput').addEventListener('keydown', e => { if(e.key==='Enter') goStep3(); }); // ── STEP 2: MODE ────────────────────────────────────────────────────────────── function updateModeExamples() { @@ -761,6 +747,20 @@

Payment and provisioning are coming next.

} function goStep3() { + const val = document.getElementById('ensInput').value.trim().toLowerCase(); + const err = document.getElementById('ensError'); + if (!val.endsWith('.eth') || val.length < 6) { + err.classList.add('show'); + document.getElementById('ensInput').classList.add('error'); + return; + } + err.classList.remove('show'); + document.getElementById('ensInput').classList.remove('error'); + document.getElementById('ensInput').classList.add('valid'); + state.ens = val; + updateModeExamples(); + buildPacksGrid(); + buildCherryGroups(); if (getVerbs().length === 0) { alert('Please select a pack or cherry-pick at least one verb.'); return; } buildENSRecords(); buildSDKConfig(); @@ -783,26 +783,36 @@

Payment and provisioning are coming next.

} } -async function signInWithEthereum(){ +window.signInWithEthereum = async function signInWithEthereum(){ const statusEl=document.getElementById('siweStatus'); const walletEl=document.getElementById('siweWallet'); + const errorEl=document.getElementById('siweError'); + const btn=document.getElementById('siweAuthBtn'); + const nextBtn=document.getElementById('step1Next'); try { - if(!window.ethereum){ throw new Error('NO_WALLET'); } + errorEl.style.display='none'; + errorEl.textContent=''; + btn.disabled=true; + btn.textContent='Waiting for wallet...'; + statusEl.textContent='Requesting wallet signature...'; + if(!window.ethereum){ throw new Error('No Ethereum wallet detected. Install MetaMask, Rabby, or another EIP-1193 wallet.'); } updateSiweModeHint(); - statusEl.textContent='Status: Requesting signature...'; - const [address]=await window.ethereum.request({ method:'eth_requestAccounts' }); + const accounts=await window.ethereum.request({ method:'eth_requestAccounts' }); + const address=accounts && accounts[0]; + if(!address){ throw new Error('No wallet account selected.'); } + const chainIdHex=await window.ethereum.request({ method:'eth_chainId' }); + const chainId=parseInt(chainIdHex,16); const nonceRes=await fetch('/api/auth/nonce',{method:'GET',headers:{'Accept':'application/json'}}); + if(!nonceRes.ok){ throw new Error('Could not get SIWE nonce from /api/auth/nonce.'); } const nonceData=await nonceRes.json(); - if(!nonceData.ok){ throw new Error('NONCE_FAILED'); } + if(!nonceData.ok || !nonceData.nonce){ throw new Error('Could not get SIWE nonce from /api/auth/nonce.'); } const domain=window.location.host; const uri=window.location.origin; - const chainHex=await window.ethereum.request({ method:'eth_chainId' }); - const chainId=parseInt(chainHex,16); const issuedAt=new Date().toISOString(); const msg=`${domain} wants you to sign in with your Ethereum account: ${address} -CommandLayer Claim activation +Authenticate with CommandLayer Claim activation. URI: ${uri} Version: 1 @@ -813,19 +823,27 @@

Payment and provisioning are coming next.

const verifyRes=await fetch('/api/auth/verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,signature})}); const verify=await verifyRes.json(); if(!verify.ok){ throw new Error(verify.error||'Authentication failed'); } - state.authenticatedAddress=verify.address; state.authStatus=verify.status; state.authChainId=verify.chainId; - statusEl.textContent='Status: Authenticated'; + state.authenticatedAddress=verify.address; state.authStatus=verify.status; state.authChainId=verify.chainId; state.siweAuthenticated=true; + statusEl.textContent='Authenticated'; walletEl.textContent=`Connected wallet: ${shortAddr(verify.address)}`; + btn.textContent='Wallet authenticated'; + nextBtn.disabled=false; } catch (err) { - const msg=(err && err.message)||''; - if(msg==='NO_WALLET') statusEl.textContent='Status: No wallet found.'; - else if(msg.includes('User rejected') || msg.includes('rejected')) statusEl.textContent='Status: Wallet rejected signature.'; - else if(msg.includes('dependency unavailable')) statusEl.textContent='Status: Auth dependency unavailable.'; - else if(msg.includes('domain mismatch')) statusEl.textContent='Status: Domain mismatch.'; - else if(msg.includes('signature') || msg.includes('SIWE')) statusEl.textContent='Status: Signature invalid.'; - else statusEl.textContent='Status: Authentication failed.'; + const msg=((err && err.message)||'Authentication failed.').trim(); + if(err && err.code===4001){ + errorEl.textContent='Wallet signature rejected.'; + }else{ + errorEl.textContent=msg; + } + errorEl.style.display='block'; + statusEl.textContent='Not connected'; + btn.disabled=false; + btn.textContent='Sign-In with Ethereum'; + nextBtn.disabled=true; + state.siweAuthenticated=false; + console.error('SIWE auth failed', err); } -} +}; function goStep5Auth(){ updateSiweModeHint(); @@ -1145,7 +1163,7 @@

Payment and provisioning are coming next.

state = { step:1, ens:'', activationMode:'cl', capMode:'packs', selectedPack:null, cherryVerbs:[], pubKeyB64:'', privKeyB64:'', kid:'', keyGenerated:false, keyDownloaded:false, keyAcked:false, - _cardJson:'' + _cardJson:'', authenticatedAddress:'', authStatus:'NOT_AUTHENTICATED', authChainId:null, siweAuthenticated:false }; document.getElementById('ensInput').value=''; document.getElementById('ensInput').classList.remove('valid','error');