auramaxx 1.0.0-alpha.4
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/LICENSE +26 -0
- package/README.md +112 -0
- package/bin/aurawallet.js +121 -0
- package/docs/ADAPTERS.md +467 -0
- package/docs/API.md +2679 -0
- package/docs/APPS.md +198 -0
- package/docs/ARCHITECTURE.md +350 -0
- package/docs/AUTH.md +698 -0
- package/docs/BEST-PRACTICES.md +121 -0
- package/docs/CLI.md +61 -0
- package/docs/DEVELOPING-APPS.md +452 -0
- package/docs/EXTENSION.md +97 -0
- package/docs/JOBS.md +33 -0
- package/docs/MCP.md +76 -0
- package/docs/PROTOCOL.md +142 -0
- package/docs/SETUP.md +219 -0
- package/docs/WORKSPACE.md +672 -0
- package/docs/agent-auth.md +63 -0
- package/docs/aura-file.md +48 -0
- package/docs/credentials.md +53 -0
- package/docs/external/getting-started.md +65 -0
- package/docs/external/overview.md +45 -0
- package/docs/external/use-cases.md +48 -0
- package/docs/external/why-aura.md +35 -0
- package/docs/jobs/connect-agent.md +77 -0
- package/docs/jobs/migrate-from-dotenv.md +79 -0
- package/docs/jobs/recover-from-lockout.md +72 -0
- package/docs/jobs/secure-ci.md +63 -0
- package/docs/oauth2.md +42 -0
- package/docs/passkeys.md +60 -0
- package/docs/security.md +540 -0
- package/docs/specs/aura-open-protocol.md +61 -0
- package/docs/specs/aura-provider-plugin.md +24 -0
- package/docs/specs/aura-registry-model.md +31 -0
- package/docs/specs/fixtures/invalid-bad-key.aura +1 -0
- package/docs/specs/fixtures/invalid-bad-unicode-escape.aura +1 -0
- package/docs/specs/fixtures/invalid-duplicate-key.aura +2 -0
- package/docs/specs/fixtures/valid-basic.aura +4 -0
- package/docs/specs/fixtures/valid-provider-ref.aura +1 -0
- package/docs/specs/fixtures/valid-quoted-escapes.aura +2 -0
- package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
- package/docs/totp.md +40 -0
- package/docs/wallet/AI.md +508 -0
- package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
- package/docs/wallet/README.md +47 -0
- package/docs/wallet/STRATEGY.md +89 -0
- package/next.config.ts +21 -0
- package/package.json +151 -0
- package/postcss.config.mjs +8 -0
- package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
- package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
- package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +447 -0
- package/public/logo-chevron.svg +31 -0
- package/public/logo-concentric.svg +31 -0
- package/public/logo-crosshatch.svg +39 -0
- package/public/logo-dashed.svg +39 -0
- package/public/logo-horizontal.svg +31 -0
- package/public/logo-m56.svg +64 -0
- package/public/logo.webp +0 -0
- package/scripts/add-app.js +245 -0
- package/scripts/init.sh +57 -0
- package/scripts/migrate-apikeys-to-credentials.ts +35 -0
- package/scripts/sandbox-agent-flow.sh +235 -0
- package/scripts/sandbox.sh +175 -0
- package/scripts/validate-job-docs.mjs +125 -0
- package/server/abi/SwapHelper.json +438 -0
- package/server/cli/approval.ts +447 -0
- package/server/cli/commands/app.ts +204 -0
- package/server/cli/commands/cron.ts +24 -0
- package/server/cli/commands/doctor.ts +1007 -0
- package/server/cli/commands/env.ts +456 -0
- package/server/cli/commands/init.ts +752 -0
- package/server/cli/commands/mcp.ts +125 -0
- package/server/cli/commands/restore.ts +314 -0
- package/server/cli/commands/shell-hook.ts +468 -0
- package/server/cli/commands/start.ts +62 -0
- package/server/cli/commands/status.ts +59 -0
- package/server/cli/commands/stop.ts +14 -0
- package/server/cli/commands/token.ts +180 -0
- package/server/cli/commands/unlock.ts +49 -0
- package/server/cli/commands/vault.ts +417 -0
- package/server/cli/index.ts +328 -0
- package/server/cli/lib/aura-parser.ts +64 -0
- package/server/cli/lib/credential-create.ts +74 -0
- package/server/cli/lib/credential-resolve.ts +254 -0
- package/server/cli/lib/dotenv-migrate.ts +116 -0
- package/server/cli/lib/dotenv-parser.ts +146 -0
- package/server/cli/lib/http.ts +91 -0
- package/server/cli/lib/init-steps.ts +76 -0
- package/server/cli/lib/local-agent-trust.ts +45 -0
- package/server/cli/lib/process.ts +136 -0
- package/server/cli/lib/prompt.ts +85 -0
- package/server/cli/lib/theme.ts +240 -0
- package/server/cli/socket.ts +570 -0
- package/server/cli/transport-client.ts +50 -0
- package/server/cron/index.ts +137 -0
- package/server/cron/job.ts +31 -0
- package/server/cron/jobs/balance-sync.ts +436 -0
- package/server/cron/jobs/incoming-scan.ts +506 -0
- package/server/cron/jobs/native-price.ts +70 -0
- package/server/cron/jobs/orphan-cleanup.ts +40 -0
- package/server/cron/jobs/strategy-runner.ts +175 -0
- package/server/cron/scheduler.ts +125 -0
- package/server/index.ts +406 -0
- package/server/lib/adapters/factory.ts +110 -0
- package/server/lib/adapters/index.ts +19 -0
- package/server/lib/adapters/router.ts +297 -0
- package/server/lib/adapters/telegram.ts +645 -0
- package/server/lib/adapters/types.ts +89 -0
- package/server/lib/adapters/webhook.ts +95 -0
- package/server/lib/address.ts +49 -0
- package/server/lib/agent-auth/contracts.ts +1194 -0
- package/server/lib/agent-profiles.ts +328 -0
- package/server/lib/ai.ts +285 -0
- package/server/lib/api-registry/contracts.ts +86 -0
- package/server/lib/api-registry/validation.ts +172 -0
- package/server/lib/apikey-migration.ts +189 -0
- package/server/lib/app-installer.ts +505 -0
- package/server/lib/app-tokens.ts +247 -0
- package/server/lib/auth.ts +314 -0
- package/server/lib/batch.ts +242 -0
- package/server/lib/cold.ts +874 -0
- package/server/lib/config.ts +381 -0
- package/server/lib/credential-access-audit.ts +85 -0
- package/server/lib/credential-access-policy.ts +110 -0
- package/server/lib/credential-health.ts +343 -0
- package/server/lib/credential-import.ts +487 -0
- package/server/lib/credential-scope.ts +87 -0
- package/server/lib/credential-shares.ts +190 -0
- package/server/lib/credential-transport.ts +342 -0
- package/server/lib/credential-vault.ts +77 -0
- package/server/lib/credentials.ts +333 -0
- package/server/lib/crypto.ts +8 -0
- package/server/lib/db.ts +15 -0
- package/server/lib/defaults.ts +366 -0
- package/server/lib/dex/index.ts +80 -0
- package/server/lib/dex/relay.ts +235 -0
- package/server/lib/dex/types.ts +59 -0
- package/server/lib/dex/uniswap.ts +370 -0
- package/server/lib/e2e-agent/artifacts.ts +36 -0
- package/server/lib/e2e-agent/contracts.ts +112 -0
- package/server/lib/e2e-agent/validation.ts +135 -0
- package/server/lib/encrypt.ts +128 -0
- package/server/lib/error.ts +20 -0
- package/server/lib/events.ts +205 -0
- package/server/lib/hot.ts +357 -0
- package/server/lib/key-fingerprint.ts +28 -0
- package/server/lib/logger.ts +331 -0
- package/server/lib/network.ts +137 -0
- package/server/lib/notifications.ts +219 -0
- package/server/lib/oauth2-refresh.ts +241 -0
- package/server/lib/oursecret.ts +54 -0
- package/server/lib/passkey-credential.ts +360 -0
- package/server/lib/passkey.ts +68 -0
- package/server/lib/permissions.ts +248 -0
- package/server/lib/pino.ts +24 -0
- package/server/lib/policy-preview.ts +138 -0
- package/server/lib/price.ts +338 -0
- package/server/lib/prices.ts +34 -0
- package/server/lib/project-scope.ts +239 -0
- package/server/lib/resolve-action.ts +427 -0
- package/server/lib/resolve.ts +36 -0
- package/server/lib/sessions.ts +632 -0
- package/server/lib/solana/connection.ts +26 -0
- package/server/lib/solana/jupiter.ts +128 -0
- package/server/lib/solana/transfer.ts +108 -0
- package/server/lib/solana/wallet.ts +136 -0
- package/server/lib/strategy/emits.ts +21 -0
- package/server/lib/strategy/engine.ts +1305 -0
- package/server/lib/strategy/executor.ts +115 -0
- package/server/lib/strategy/hook-context.ts +158 -0
- package/server/lib/strategy/hooks.ts +990 -0
- package/server/lib/strategy/index.ts +28 -0
- package/server/lib/strategy/installer.ts +305 -0
- package/server/lib/strategy/loader.ts +256 -0
- package/server/lib/strategy/message.ts +235 -0
- package/server/lib/strategy/repository.ts +218 -0
- package/server/lib/strategy/session-logger.ts +693 -0
- package/server/lib/strategy/sources.ts +288 -0
- package/server/lib/strategy/state.ts +189 -0
- package/server/lib/strategy/templates.ts +403 -0
- package/server/lib/strategy/tick.ts +404 -0
- package/server/lib/strategy/types.ts +230 -0
- package/server/lib/swap.ts +3 -0
- package/server/lib/temp.ts +86 -0
- package/server/lib/token-metadata.ts +86 -0
- package/server/lib/token-safety.ts +200 -0
- package/server/lib/token-search.ts +444 -0
- package/server/lib/totp.ts +194 -0
- package/server/lib/transactions.ts +123 -0
- package/server/lib/transport.ts +75 -0
- package/server/lib/txhistory/decoder.ts +262 -0
- package/server/lib/txhistory/enricher.ts +652 -0
- package/server/lib/txhistory/index.ts +391 -0
- package/server/lib/txhistory/signatures.ts +59 -0
- package/server/lib/verified-summary.ts +421 -0
- package/server/mcp/profile-policy.ts +30 -0
- package/server/mcp/server.ts +619 -0
- package/server/mcp/tools.ts +523 -0
- package/server/middleware/auth.ts +119 -0
- package/server/middleware/requestLogger.ts +84 -0
- package/server/routes/actions.ts +459 -0
- package/server/routes/adapters.ts +703 -0
- package/server/routes/addressbook.ts +113 -0
- package/server/routes/ai.ts +34 -0
- package/server/routes/apikeys.ts +295 -0
- package/server/routes/apps.ts +601 -0
- package/server/routes/auth.ts +457 -0
- package/server/routes/backup.ts +340 -0
- package/server/routes/batch.ts +270 -0
- package/server/routes/bookmarks.ts +162 -0
- package/server/routes/credential-shares.ts +198 -0
- package/server/routes/credential-vaults.ts +154 -0
- package/server/routes/credentials.ts +1290 -0
- package/server/routes/dashboard.ts +71 -0
- package/server/routes/defaults.ts +124 -0
- package/server/routes/fund.ts +229 -0
- package/server/routes/import.ts +352 -0
- package/server/routes/launch.ts +665 -0
- package/server/routes/lock.ts +54 -0
- package/server/routes/logs.ts +68 -0
- package/server/routes/nuke.ts +111 -0
- package/server/routes/passkey-credentials.ts +99 -0
- package/server/routes/passkey.ts +346 -0
- package/server/routes/portfolio.ts +217 -0
- package/server/routes/price.ts +63 -0
- package/server/routes/resolve.ts +31 -0
- package/server/routes/security.ts +45 -0
- package/server/routes/send-evm.ts +241 -0
- package/server/routes/send-solana.ts +281 -0
- package/server/routes/send.ts +178 -0
- package/server/routes/setup.ts +210 -0
- package/server/routes/strategy.ts +894 -0
- package/server/routes/swap-evm.ts +353 -0
- package/server/routes/swap-solana.ts +177 -0
- package/server/routes/swap.ts +356 -0
- package/server/routes/token.ts +247 -0
- package/server/routes/unlock.ts +403 -0
- package/server/routes/wallet-assets.ts +361 -0
- package/server/routes/wallet-transactions.ts +515 -0
- package/server/routes/wallet.ts +710 -0
- package/server/types.ts +146 -0
- package/skills/aurawallet/SKILL.md +739 -0
- package/skills/aurawallet-setup/SKILL.md +74 -0
- package/skills/security-review/SKILL.md +148 -0
- package/src/app/api/agent-requests/route.ts +30 -0
- package/src/app/api/apps/install/route.ts +126 -0
- package/src/app/api/apps/manifests/route.ts +16 -0
- package/src/app/api/apps/static/[...path]/route.ts +57 -0
- package/src/app/api/events/route.ts +92 -0
- package/src/app/api/page.tsx +212 -0
- package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
- package/src/app/api/workspace/[id]/apps/route.ts +81 -0
- package/src/app/api/workspace/[id]/export/route.ts +67 -0
- package/src/app/api/workspace/[id]/route.ts +168 -0
- package/src/app/api/workspace/auth.ts +34 -0
- package/src/app/api/workspace/config/route.ts +106 -0
- package/src/app/api/workspace/import/route.ts +127 -0
- package/src/app/api/workspace/route.ts +116 -0
- package/src/app/app/page.tsx +2122 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/docs/page.tsx +178 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +572 -0
- package/src/app/health/page.tsx +5 -0
- package/src/app/hello/page.tsx +15 -0
- package/src/app/icon.png +0 -0
- package/src/app/layout.tsx +34 -0
- package/src/app/page.tsx +986 -0
- package/src/app/providers.tsx +90 -0
- package/src/app/share/[token]/page.tsx +295 -0
- package/src/components/ChainSelector.tsx +144 -0
- package/src/components/HumanActionBar.tsx +695 -0
- package/src/components/NotificationDrawer.tsx +129 -0
- package/src/components/apps/AgentKeysApp.tsx +490 -0
- package/src/components/apps/App.tsx +153 -0
- package/src/components/apps/AppGrid.tsx +15 -0
- package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
- package/src/components/apps/DraggableApp.tsx +562 -0
- package/src/components/apps/IFrameApp.tsx +73 -0
- package/src/components/apps/LogsApp.tsx +360 -0
- package/src/components/apps/SendApp.tsx +394 -0
- package/src/components/apps/SetupWizardApp.tsx +1004 -0
- package/src/components/apps/SystemDefaultsApp.tsx +845 -0
- package/src/components/apps/ThirdPartyApp.tsx +428 -0
- package/src/components/apps/TokenApp.tsx +319 -0
- package/src/components/apps/TransactionsApp.tsx +438 -0
- package/src/components/apps/WalletDetailApp.tsx +1505 -0
- package/src/components/apps/index.ts +13 -0
- package/src/components/design-system/Button.tsx +53 -0
- package/src/components/design-system/ChainIndicator.tsx +65 -0
- package/src/components/design-system/ChainSelector.tsx +137 -0
- package/src/components/design-system/ConfirmationModal.tsx +106 -0
- package/src/components/design-system/ConfirmationPopover.tsx +81 -0
- package/src/components/design-system/Drawer.tsx +123 -0
- package/src/components/design-system/FilterDropdown.tsx +72 -0
- package/src/components/design-system/Modal.tsx +206 -0
- package/src/components/design-system/Popover.tsx +142 -0
- package/src/components/design-system/TextInput.tsx +85 -0
- package/src/components/design-system/Toggle.tsx +58 -0
- package/src/components/design-system/index.ts +11 -0
- package/src/components/docs/DocsThemeToggle.tsx +49 -0
- package/src/components/health/CredentialHealthDashboard.tsx +214 -0
- package/src/components/icons/ChainIcons.tsx +72 -0
- package/src/components/layout/AppStoreDrawer.tsx +369 -0
- package/src/components/layout/ContentArea.tsx +21 -0
- package/src/components/layout/TabBar.tsx +278 -0
- package/src/components/layout/WalletSidebar.tsx +1033 -0
- package/src/components/layout/index.ts +4 -0
- package/src/components/marketing/AuraWalletSpecOverlay.tsx +635 -0
- package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
- package/src/components/vault/ApiKeysConsole.tsx +1080 -0
- package/src/components/vault/AuditConsole.tsx +584 -0
- package/src/components/vault/CredentialDetail.tsx +455 -0
- package/src/components/vault/CredentialEmpty.tsx +55 -0
- package/src/components/vault/CredentialField.tsx +361 -0
- package/src/components/vault/CredentialForm.tsx +1212 -0
- package/src/components/vault/CredentialList.tsx +165 -0
- package/src/components/vault/CredentialRow.tsx +97 -0
- package/src/components/vault/CredentialShareModal.tsx +178 -0
- package/src/components/vault/CredentialVault.tsx +754 -0
- package/src/components/vault/CredentialWalletWidget.tsx +103 -0
- package/src/components/vault/ImportCredentialsModal.tsx +515 -0
- package/src/components/vault/LargeTypeModal.tsx +64 -0
- package/src/components/vault/PasswordGenerator.tsx +224 -0
- package/src/components/vault/TOTPDisplay.tsx +123 -0
- package/src/components/vault/VaultSidebar.tsx +413 -0
- package/src/components/vault/types.ts +54 -0
- package/src/context/AuthContext.tsx +337 -0
- package/src/context/PriceContext.tsx +113 -0
- package/src/context/ThemeContext.tsx +164 -0
- package/src/context/WebSocketContext.tsx +269 -0
- package/src/context/WorkspaceContext.tsx +668 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useAgentActions.ts +368 -0
- package/src/hooks/useBalance.ts +103 -0
- package/src/hooks/useBalances.ts +129 -0
- package/src/instrumentation.ts +12 -0
- package/src/lib/api.ts +449 -0
- package/src/lib/app-loader.ts +148 -0
- package/src/lib/app-registry.ts +178 -0
- package/src/lib/app-sdk.ts +157 -0
- package/src/lib/audit-console-adapter.ts +151 -0
- package/src/lib/auth-client.ts +75 -0
- package/src/lib/config.ts +74 -0
- package/src/lib/crypto.ts +112 -0
- package/src/lib/db.ts +21 -0
- package/src/lib/docs.ts +390 -0
- package/src/lib/events.ts +361 -0
- package/src/lib/pino.ts +24 -0
- package/src/lib/theme-handlers.ts +168 -0
- package/src/lib/theme.ts +351 -0
- package/src/lib/tokenData.ts +378 -0
- package/src/lib/vault-crypto.ts +129 -0
- package/src/lib/websocket-server.ts +302 -0
- package/src/lib/websocket-setup.ts +79 -0
- package/src/lib/wordlist.ts +2050 -0
- package/src/lib/workspace-handlers.ts +285 -0
- package/start.sh +80 -0
- package/tailwind.config.ts +99 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { Router, Request, Response } from 'express';
|
|
2
|
+
import { unlock, isUnlocked, hasColdWallet, getColdWalletAddress, unlockVault, isVaultUnlocked, getVaultAddress, autoUnlockLinkedVaults } from '../lib/cold';
|
|
3
|
+
import { log } from '../lib/pino';
|
|
4
|
+
import { createAdminToken, createToken, validateToken, getTokenHash } from '../lib/auth';
|
|
5
|
+
import { revokeToken } from '../lib/sessions';
|
|
6
|
+
import { parseEncryptedPassword } from '../lib/transport';
|
|
7
|
+
import { isValidAgentPubkey, normalizeAgentPubkey } from '../lib/credential-transport';
|
|
8
|
+
import { logger } from '../lib/logger';
|
|
9
|
+
import { events } from '../lib/events';
|
|
10
|
+
import { getErrorMessage, HttpError } from '../lib/error';
|
|
11
|
+
import { getDefaultSync } from '../lib/defaults';
|
|
12
|
+
|
|
13
|
+
const router = Router();
|
|
14
|
+
|
|
15
|
+
// GET /unlock - Self-contained HTML unlock page
|
|
16
|
+
// Served from Express so it's same-origin with /auth/connect and POST /unlock
|
|
17
|
+
export function unlockPageHandler(_req: Request, res: Response) {
|
|
18
|
+
res.type('html').send(UNLOCK_PAGE_HTML);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const UNLOCK_PAGE_HTML = /* html */ `<!DOCTYPE html>
|
|
22
|
+
<html lang="en">
|
|
23
|
+
<head>
|
|
24
|
+
<meta charset="utf-8">
|
|
25
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
26
|
+
<title>AuraWallet — Unlock</title>
|
|
27
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
28
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
29
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
30
|
+
<style>
|
|
31
|
+
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
32
|
+
html{font-family:'Inter',system-ui,sans-serif;background:#0a0a0a;color:#ffffff;font-size:14px;-webkit-font-smoothing:antialiased}
|
|
33
|
+
body{min-height:100vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}
|
|
34
|
+
body::before{content:'';position:fixed;inset:0;background:radial-gradient(#fff 1px,transparent 1px);background-size:4px 4px;opacity:0.02;pointer-events:none}
|
|
35
|
+
|
|
36
|
+
.container{width:100%;max-width:380px;padding:24px}
|
|
37
|
+
|
|
38
|
+
.badge{display:inline-block;font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:0.15em;text-transform:uppercase;color:#71717a;margin-bottom:24px}
|
|
39
|
+
|
|
40
|
+
.card{position:relative;border:1px solid #27272a;padding:32px 24px;background:#0a0a0a}
|
|
41
|
+
.card::before,.card::after{content:'';position:absolute;width:12px;height:12px;border-color:#ccff00;border-style:solid}
|
|
42
|
+
.card::before{top:-1px;left:-1px;border-width:1px 0 0 1px}
|
|
43
|
+
.card::after{bottom:-1px;right:-1px;border-width:0 1px 1px 0}
|
|
44
|
+
|
|
45
|
+
h1{font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:0.15em;text-transform:uppercase;color:#a1a1aa;margin-bottom:24px}
|
|
46
|
+
|
|
47
|
+
.field{margin-bottom:20px}
|
|
48
|
+
.field label{display:block;font-family:'JetBrains Mono',monospace;font-size:9px;letter-spacing:0.15em;text-transform:uppercase;color:#71717a;margin-bottom:8px}
|
|
49
|
+
.field input{width:100%;padding:10px 12px;background:#141414;border:1px solid #27272a;color:#ffffff;font-family:'Inter',sans-serif;font-size:14px;outline:none;border-radius:0;transition:border-color 0.15s,box-shadow 0.15s}
|
|
50
|
+
.field input:focus{border-color:#ccff00;box-shadow:2px 2px 0 rgba(204,255,0,0.2)}
|
|
51
|
+
.field input::placeholder{color:#3f3f46}
|
|
52
|
+
|
|
53
|
+
button{width:100%;padding:10px 12px;background:#ffffff;color:#0a0a0a;font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:0.1em;text-transform:uppercase;font-weight:500;border:none;cursor:pointer;border-radius:0;transition:color 0.15s}
|
|
54
|
+
button:hover{color:#ccff00}
|
|
55
|
+
button:disabled{opacity:0.4;cursor:not-allowed}
|
|
56
|
+
|
|
57
|
+
.status{margin-top:16px;font-family:'JetBrains Mono',monospace;font-size:11px;min-height:20px}
|
|
58
|
+
.status.error{color:#ff4d00}
|
|
59
|
+
.status.success{color:#ccff00}
|
|
60
|
+
.status.loading{color:#71717a}
|
|
61
|
+
|
|
62
|
+
.address{margin-top:12px;font-family:'JetBrains Mono',monospace;font-size:10px;color:#52525b;word-break:break-all}
|
|
63
|
+
</style>
|
|
64
|
+
</head>
|
|
65
|
+
<body>
|
|
66
|
+
<div class="container">
|
|
67
|
+
<div class="badge">AuraWallet</div>
|
|
68
|
+
<div class="card">
|
|
69
|
+
<h1>Unlock Vault</h1>
|
|
70
|
+
<form id="form">
|
|
71
|
+
<div class="field">
|
|
72
|
+
<label for="pw">Password</label>
|
|
73
|
+
<input type="password" id="pw" placeholder="Enter vault password" autocomplete="current-password" autofocus>
|
|
74
|
+
</div>
|
|
75
|
+
<button type="submit" id="btn">Unlock</button>
|
|
76
|
+
</form>
|
|
77
|
+
<div class="status" id="status"></div>
|
|
78
|
+
<div class="address" id="addr"></div>
|
|
79
|
+
<div id="biometric-section" style="display:none;margin-top:20px;padding-top:16px;border-top:1px solid #27272a">
|
|
80
|
+
<button type="button" id="bio-btn" style="background:#0a0a0a;color:#ccff00;border:1px solid #27272a">Unlock with Face ID</button>
|
|
81
|
+
<label style="display:flex;align-items:center;justify-content:center;gap:8px;margin-top:12px;color:#a1a1aa;font-size:10px;letter-spacing:0.08em;font-family:'JetBrains Mono',monospace;text-transform:uppercase;">
|
|
82
|
+
<input type="checkbox" id="bio-auto" checked style="margin:0;" />
|
|
83
|
+
Auto-unlock on load
|
|
84
|
+
</label>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<script>
|
|
89
|
+
(function(){
|
|
90
|
+
var form=document.getElementById('form');
|
|
91
|
+
var pw=document.getElementById('pw');
|
|
92
|
+
var btn=document.getElementById('btn');
|
|
93
|
+
var status=document.getElementById('status');
|
|
94
|
+
var addr=document.getElementById('addr');
|
|
95
|
+
|
|
96
|
+
function setStatus(msg,cls){status.textContent=msg;status.className='status '+(cls||'')}
|
|
97
|
+
|
|
98
|
+
function pemToArrayBuffer(pem){
|
|
99
|
+
var b64=pem.replace(/-----BEGIN PUBLIC KEY-----/,'').replace(/-----END PUBLIC KEY-----/,'').replace(/\\s/g,'');
|
|
100
|
+
var bin=atob(b64);var bytes=new Uint8Array(bin.length);
|
|
101
|
+
for(var i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);
|
|
102
|
+
return bytes.buffer;
|
|
103
|
+
}
|
|
104
|
+
function arrayBufferToBase64(buf){
|
|
105
|
+
var bytes=new Uint8Array(buf);var bin='';
|
|
106
|
+
for(var i=0;i<bytes.length;i++)bin+=String.fromCharCode(bytes[i]);
|
|
107
|
+
return btoa(bin);
|
|
108
|
+
}
|
|
109
|
+
var agentPubkeyB64=null;
|
|
110
|
+
async function ensureAgentPubkey(){
|
|
111
|
+
if(agentPubkeyB64)return agentPubkeyB64;
|
|
112
|
+
var pair=await crypto.subtle.generateKey(
|
|
113
|
+
{name:'RSA-OAEP',modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:'SHA-256'},
|
|
114
|
+
true,
|
|
115
|
+
['encrypt','decrypt']
|
|
116
|
+
);
|
|
117
|
+
var spki=await crypto.subtle.exportKey('spki',pair.publicKey);
|
|
118
|
+
agentPubkeyB64=arrayBufferToBase64(spki);
|
|
119
|
+
return agentPubkeyB64;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function encryptPassword(password,pemKey){
|
|
123
|
+
var keyData=pemToArrayBuffer(pemKey);
|
|
124
|
+
var publicKey=await crypto.subtle.importKey('spki',keyData,{name:'RSA-OAEP',hash:'SHA-256'},false,['encrypt']);
|
|
125
|
+
var encoded=new TextEncoder().encode(password);
|
|
126
|
+
var encrypted=await crypto.subtle.encrypt({name:'RSA-OAEP'},publicKey,encoded);
|
|
127
|
+
var bytes=new Uint8Array(encrypted);var bin='';
|
|
128
|
+
for(var i=0;i<bytes.length;i++)bin+=String.fromCharCode(bytes[i]);
|
|
129
|
+
return btoa(bin);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
form.addEventListener('submit',async function(e){
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
var password=pw.value;
|
|
135
|
+
if(!password){setStatus('Password required','error');return}
|
|
136
|
+
btn.disabled=true;
|
|
137
|
+
setStatus('Encrypting...','loading');
|
|
138
|
+
try{
|
|
139
|
+
var connectRes=await fetch('/auth/connect');
|
|
140
|
+
if(!connectRes.ok)throw new Error('Failed to fetch public key');
|
|
141
|
+
var connectData=await connectRes.json();
|
|
142
|
+
var pubkey=await ensureAgentPubkey();
|
|
143
|
+
var encrypted=await encryptPassword(password,connectData.publicKey);
|
|
144
|
+
setStatus('Unlocking...','loading');
|
|
145
|
+
var unlockRes=await fetch('/unlock',{
|
|
146
|
+
method:'POST',
|
|
147
|
+
headers:{'Content-Type':'application/json'},
|
|
148
|
+
body:JSON.stringify({encrypted:encrypted,pubkey:pubkey})
|
|
149
|
+
});
|
|
150
|
+
var data=await unlockRes.json();
|
|
151
|
+
if(unlockRes.ok&&data.success){
|
|
152
|
+
setStatus('Vault unlocked','success');
|
|
153
|
+
if(data.address)addr.textContent=data.address;
|
|
154
|
+
pw.value='';
|
|
155
|
+
}else{
|
|
156
|
+
setStatus(data.error||'Unlock failed','error');
|
|
157
|
+
}
|
|
158
|
+
}catch(err){
|
|
159
|
+
setStatus(err.message||'Network error','error');
|
|
160
|
+
}finally{
|
|
161
|
+
btn.disabled=false;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// ─── Biometric / Passkey Support ──────────────────────────
|
|
166
|
+
var bioSection=document.getElementById('biometric-section');
|
|
167
|
+
var bioBtn=document.getElementById('bio-btn');
|
|
168
|
+
var bioAuto=document.getElementById('bio-auto');
|
|
169
|
+
var autoPrompt=localStorage.getItem('aura:biometric:autoPrompt')!=='false';
|
|
170
|
+
if (bioAuto) {
|
|
171
|
+
bioAuto.checked = autoPrompt;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function base64urlToBuffer(b){var s=b.replace(/-/g,'+').replace(/_/g,'/');while(s.length%4)s+='=';var bin=atob(s);var a=new Uint8Array(bin.length);for(var i=0;i<bin.length;i++)a[i]=bin.charCodeAt(i);return a.buffer}
|
|
175
|
+
function bufferToBase64url(b){var bytes=new Uint8Array(b);var bin='';for(var i=0;i<bytes.length;i++)bin+=String.fromCharCode(bytes[i]);return btoa(bin).replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'')}
|
|
176
|
+
|
|
177
|
+
async function doBiometricAuth(){
|
|
178
|
+
bioBtn.disabled=true;
|
|
179
|
+
setStatus('Requesting biometric...','loading');
|
|
180
|
+
try{
|
|
181
|
+
var optRes=await fetch('/auth/passkey/authenticate/options',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({})});
|
|
182
|
+
var optData=await optRes.json();
|
|
183
|
+
if(!optRes.ok){
|
|
184
|
+
if(optData.error==='vault_locked'){setStatus('Password required after server restart','error');return}
|
|
185
|
+
setStatus(optData.error||'Failed to get options','error');return;
|
|
186
|
+
}
|
|
187
|
+
// Convert for WebAuthn API
|
|
188
|
+
var pubkeyOpts={challenge:base64urlToBuffer(optData.challenge),rpId:optData.rpId,timeout:optData.timeout,userVerification:optData.userVerification,allowCredentials:(optData.allowCredentials||[]).map(function(c){return{type:'public-key',id:base64urlToBuffer(c.id),transports:c.transports}})};
|
|
189
|
+
var assertion=await navigator.credentials.get({publicKey:pubkeyOpts});
|
|
190
|
+
setStatus('Verifying...','loading');
|
|
191
|
+
var pubkey=await ensureAgentPubkey();
|
|
192
|
+
var cred={id:bufferToBase64url(assertion.rawId),rawId:bufferToBase64url(assertion.rawId),type:assertion.type,response:{clientDataJSON:bufferToBase64url(assertion.response.clientDataJSON),authenticatorData:bufferToBase64url(assertion.response.authenticatorData),signature:bufferToBase64url(assertion.response.signature),userHandle:assertion.response.userHandle?bufferToBase64url(assertion.response.userHandle):null}};
|
|
193
|
+
var verRes=await fetch('/auth/passkey/authenticate/verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({credential:cred,pubkey:pubkey})});
|
|
194
|
+
var verData=await verRes.json();
|
|
195
|
+
if(verRes.ok&&verData.success){
|
|
196
|
+
setStatus('Vault unlocked via biometric','success');
|
|
197
|
+
pw.value='';
|
|
198
|
+
}else{
|
|
199
|
+
setStatus(verData.error||'Biometric auth failed','error');
|
|
200
|
+
}
|
|
201
|
+
}catch(err){
|
|
202
|
+
if(err.name==='NotAllowedError'){setStatus('Biometric cancelled','error');}
|
|
203
|
+
else{setStatus(err.message||'Biometric error','error');}
|
|
204
|
+
}finally{bioBtn.disabled=false;}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check if passkeys registered and WebAuthn available
|
|
208
|
+
if(window.PublicKeyCredential){
|
|
209
|
+
fetch('/auth/passkey/status').then(function(r){return r.json()}).then(function(d){
|
|
210
|
+
if(d.registered){
|
|
211
|
+
bioSection.style.display='block';
|
|
212
|
+
bioBtn.addEventListener('click',doBiometricAuth);
|
|
213
|
+
if (bioAuto) {
|
|
214
|
+
bioAuto.addEventListener('change', function () {
|
|
215
|
+
localStorage.setItem('aura:biometric:autoPrompt', String(!bioAuto.checked ? 'false' : 'true'));
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if(autoPrompt)doBiometricAuth();
|
|
219
|
+
}
|
|
220
|
+
}).catch(function(){});
|
|
221
|
+
}
|
|
222
|
+
})();
|
|
223
|
+
</script>
|
|
224
|
+
</body>
|
|
225
|
+
</html>`;
|
|
226
|
+
|
|
227
|
+
// POST /unlock/rekey - Re-key session with new RSA pubkey (no password required)
|
|
228
|
+
// Used after page refresh: token survives in sessionStorage but keypair is lost.
|
|
229
|
+
// Client generates a new keypair and exchanges the old token for a new one.
|
|
230
|
+
router.post('/rekey', async (req: Request, res: Response) => {
|
|
231
|
+
try {
|
|
232
|
+
// Require Bearer token
|
|
233
|
+
const authHeader = req.headers.authorization;
|
|
234
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
235
|
+
res.status(401).json({ error: 'Authorization required' });
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const rawToken = authHeader.slice(7);
|
|
240
|
+
const payload = validateToken(rawToken);
|
|
241
|
+
if (!payload) {
|
|
242
|
+
res.status(401).json({ error: 'Invalid or expired token' });
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Vault must still be unlocked server-side
|
|
247
|
+
if (!isUnlocked()) {
|
|
248
|
+
res.status(403).json({ error: 'Vault is locked' });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Validate new pubkey
|
|
253
|
+
const pubkey = typeof req.body?.pubkey === 'string' ? req.body.pubkey : '';
|
|
254
|
+
if (!pubkey.trim()) {
|
|
255
|
+
res.status(400).json({ error: 'pubkey is required' });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (!isValidAgentPubkey(pubkey)) {
|
|
259
|
+
res.status(400).json({ error: 'pubkey must be a valid RSA public key (PEM or base64)' });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const normalizedPubkey = normalizeAgentPubkey(pubkey);
|
|
263
|
+
|
|
264
|
+
// Revoke old token
|
|
265
|
+
const oldHash = getTokenHash(rawToken);
|
|
266
|
+
revokeToken(oldHash);
|
|
267
|
+
|
|
268
|
+
// Issue new admin token with new pubkey
|
|
269
|
+
const newToken = await createAdminToken(normalizedPubkey);
|
|
270
|
+
|
|
271
|
+
logger.unlocked(getColdWalletAddress() || 'rekey');
|
|
272
|
+
|
|
273
|
+
res.json({
|
|
274
|
+
success: true,
|
|
275
|
+
message: 'Session re-keyed with new public key',
|
|
276
|
+
token: newToken,
|
|
277
|
+
});
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (error instanceof HttpError) { res.status(error.status).json({ error: error.message }); return; }
|
|
280
|
+
res.status(400).json({ error: getErrorMessage(error) });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// POST /unlock - Unlock cold wallet with encrypted password
|
|
285
|
+
// Returns admin token for UI access
|
|
286
|
+
router.post('/', async (req: Request, res: Response) => {
|
|
287
|
+
try {
|
|
288
|
+
const pubkey = typeof req.body?.pubkey === 'string' ? req.body.pubkey : '';
|
|
289
|
+
if (!pubkey.trim()) {
|
|
290
|
+
res.status(400).json({ error: 'pubkey is required' });
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!isValidAgentPubkey(pubkey)) {
|
|
294
|
+
res.status(400).json({ error: 'pubkey must be a valid RSA public key (PEM or base64)' });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const normalizedPubkey = normalizeAgentPubkey(pubkey);
|
|
298
|
+
const password = parseEncryptedPassword(req.body.encrypted);
|
|
299
|
+
|
|
300
|
+
if (!hasColdWallet()) {
|
|
301
|
+
res.status(400).json({ error: 'No cold wallet found. Run /setup first.' });
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Always validate password, even if already unlocked
|
|
306
|
+
// This allows users to get a new token after page refresh
|
|
307
|
+
const success = unlock(password);
|
|
308
|
+
|
|
309
|
+
if (!success) {
|
|
310
|
+
res.status(401).json({ error: 'Invalid password' });
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const address = getColdWalletAddress();
|
|
315
|
+
|
|
316
|
+
// Log the unlock event
|
|
317
|
+
logger.unlocked(address || '');
|
|
318
|
+
events.vaultUnlocked({ address: address || '', vaultId: 'primary' });
|
|
319
|
+
|
|
320
|
+
// After primary unlock, auto-unlock linked vaults (independent vaults stay locked).
|
|
321
|
+
try {
|
|
322
|
+
autoUnlockLinkedVaults();
|
|
323
|
+
} catch (err) {
|
|
324
|
+
log.warn({ err }, 'Failed to auto-unlock linked vaults after unlock');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// If scope=extension, issue a scoped token instead of admin
|
|
328
|
+
const scope = req.body?.scope;
|
|
329
|
+
if (scope === 'extension') {
|
|
330
|
+
const ttl = getDefaultSync<number>('ttl.admin', 604800);
|
|
331
|
+
const token = await createToken('extension', 0, ['extension:*'], ttl, {
|
|
332
|
+
credentialAccess: { read: ['*'] },
|
|
333
|
+
agentPubkey: normalizedPubkey,
|
|
334
|
+
});
|
|
335
|
+
res.json({
|
|
336
|
+
success: true,
|
|
337
|
+
message: 'Wallet unlocked (extension scope)',
|
|
338
|
+
address,
|
|
339
|
+
token,
|
|
340
|
+
scope: 'extension',
|
|
341
|
+
});
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Create admin token on successful unlock
|
|
346
|
+
const token = await createAdminToken(normalizedPubkey);
|
|
347
|
+
|
|
348
|
+
res.json({
|
|
349
|
+
success: true,
|
|
350
|
+
message: 'Wallet unlocked',
|
|
351
|
+
address,
|
|
352
|
+
token
|
|
353
|
+
});
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (error instanceof HttpError) { res.status(error.status).json({ error: error.message }); return; }
|
|
356
|
+
res.status(400).json({ error: getErrorMessage(error) });
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// POST /unlock/:vaultId - Unlock a specific vault
|
|
361
|
+
router.post('/:vaultId', async (req: Request<{ vaultId: string }>, res: Response) => {
|
|
362
|
+
try {
|
|
363
|
+
const { vaultId } = req.params;
|
|
364
|
+
const pubkey = typeof req.body?.pubkey === 'string' ? req.body.pubkey : '';
|
|
365
|
+
if (!pubkey.trim()) {
|
|
366
|
+
res.status(400).json({ error: 'pubkey is required' });
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (!isValidAgentPubkey(pubkey)) {
|
|
370
|
+
res.status(400).json({ error: 'pubkey must be a valid RSA public key (PEM or base64)' });
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const normalizedPubkey = normalizeAgentPubkey(pubkey);
|
|
374
|
+
const password = parseEncryptedPassword(req.body.encrypted);
|
|
375
|
+
|
|
376
|
+
const success = unlockVault(vaultId, password);
|
|
377
|
+
|
|
378
|
+
if (!success) {
|
|
379
|
+
res.status(401).json({ error: 'Invalid password' });
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Create admin token (or return existing if primary is already unlocked)
|
|
384
|
+
const token = await createAdminToken(normalizedPubkey);
|
|
385
|
+
const address = getVaultAddress(vaultId);
|
|
386
|
+
|
|
387
|
+
logger.unlocked(address || vaultId);
|
|
388
|
+
events.vaultUnlocked({ address: address || '', vaultId });
|
|
389
|
+
|
|
390
|
+
res.json({
|
|
391
|
+
success: true,
|
|
392
|
+
message: `Vault ${vaultId} unlocked`,
|
|
393
|
+
vaultId,
|
|
394
|
+
address,
|
|
395
|
+
token
|
|
396
|
+
});
|
|
397
|
+
} catch (error) {
|
|
398
|
+
if (error instanceof HttpError) { res.status(error.status).json({ error: error.message }); return; }
|
|
399
|
+
res.status(400).json({ error: getErrorMessage(error) });
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
export default router;
|