@adonis-agora/authkit-server 1.0.0-rc.1 → 1.0.0-rc.11
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/CHANGELOG.md +107 -0
- package/README.md +4 -5
- package/build/commands/agora_doctor.d.ts +22 -0
- package/build/commands/agora_doctor.js +109 -0
- package/build/commands/clients_create.d.ts +4 -0
- package/build/commands/clients_create.js +97 -0
- package/build/commands/commands.json +78 -0
- package/build/commands/doctor.js +8 -74
- package/build/host/views/account/apps.edge +1 -5
- package/build/host/views/account/extension.edge +77 -0
- package/build/host/views/account/mfa.edge +2 -0
- package/build/host/views/account/partials/extension_actions.edge +14 -0
- package/build/host/views/account/partials/extension_block.edge +197 -0
- package/build/host/views/account/partials/extension_value.edge +8 -0
- package/build/host/views/account/partials/nav.edge +14 -0
- package/build/host/views/account/security.edge +1 -5
- package/build/host/views/account/tokens.edge +2 -2
- package/build/host/views/agents/consent.edge +11 -2
- package/build/host/views/agents/done.edge +1 -1
- package/build/host/views/partials/styles.edge +1 -1
- package/build/index.d.ts +12 -12
- package/build/index.js +7 -9
- package/build/providers/authkit_server_provider.js +29 -8
- package/build/src/account/extension.d.ts +10 -0
- package/build/src/account/extension.js +9 -0
- package/build/src/account/http.d.ts +77 -0
- package/build/src/account/http.js +381 -0
- package/build/src/account/mount.d.ts +40 -0
- package/build/src/account/mount.js +171 -0
- package/build/src/account/registry.d.ts +41 -0
- package/build/src/account/registry.js +81 -0
- package/build/src/account/remote.d.ts +72 -0
- package/build/src/account/remote.js +406 -0
- package/build/src/account/runtime.d.ts +289 -0
- package/build/src/account/runtime.js +641 -0
- package/build/src/accounts/account_store.d.ts +13 -7
- package/build/src/accounts/lucid_account_store.d.ts +3 -3
- package/build/src/accounts/lucid_account_store.js +42 -17
- package/build/src/accounts/lucid_store/mfa.js +3 -3
- package/build/src/accounts/lucid_store/organization_models.d.ts +14 -3
- package/build/src/accounts/lucid_store/organization_models.js +31 -3
- package/build/src/accounts/lucid_store/organizations.d.ts +2 -2
- package/build/src/accounts/lucid_store/organizations.js +40 -31
- package/build/src/accounts/lucid_store/shared.d.ts +11 -3
- package/build/src/accounts/lucid_store/shared.js +7 -5
- package/build/src/accounts/lucid_store/webauthn.js +1 -1
- package/build/src/adapters/factory.d.ts +13 -2
- package/build/src/adapters/factory.js +2 -2
- package/build/src/agents/config.d.ts +9 -13
- package/build/src/agents/config.js +17 -11
- package/build/src/agents/delegation_service.js +3 -3
- package/build/src/agents/delegation_store.d.ts +3 -2
- package/build/src/agents/delegation_store.js +6 -5
- package/build/src/agents/extension.d.ts +261 -0
- package/build/src/agents/extension.js +40 -0
- package/build/src/agents/middleware.js +1 -1
- package/build/src/agents/protocol.d.ts +4 -4
- package/build/src/agents/registry_store.d.ts +3 -2
- package/build/src/agents/registry_store.js +1 -1
- package/build/src/agents/runtime.d.ts +26 -7
- package/build/src/agents/runtime.js +110 -75
- package/build/src/audit/audit_sink.d.ts +1 -1
- package/build/src/audit/audit_sink.js +10 -1
- package/build/src/commands/expire_scan_command.js +1 -1
- package/build/src/commands/settings_commands.js +2 -2
- package/build/src/console_switcher.d.ts +132 -0
- package/build/src/console_switcher.js +206 -0
- package/build/src/define_config.d.ts +38 -13
- package/build/src/define_config.js +32 -10
- package/build/src/dev_tools/contract.d.ts +72 -0
- package/build/src/dev_tools/contract.js +10 -0
- package/build/src/dev_tools/gate.d.ts +17 -0
- package/build/src/dev_tools/gate.js +69 -0
- package/build/src/dev_tools/register.d.ts +11 -0
- package/build/src/dev_tools/register.js +25 -0
- package/build/src/dev_tools/tools.d.ts +24 -0
- package/build/src/dev_tools/tools.js +384 -0
- package/build/src/doctor/agora_checks.d.ts +15 -0
- package/build/src/doctor/agora_checks.js +150 -0
- package/build/src/doctor/agora_doctor.d.ts +115 -0
- package/build/src/doctor/agora_doctor.js +692 -0
- package/build/src/doctor/checks.js +2 -2
- package/build/src/doctor/doctor_input.d.ts +27 -0
- package/build/src/doctor/doctor_input.js +86 -0
- package/build/src/doctor/index.d.ts +5 -0
- package/build/src/doctor/index.js +4 -0
- package/build/src/host/access_token_verifier.js +7 -0
- package/build/src/host/account_api/account_api_controller.js +8 -6
- package/build/src/host/account_session_sign_in.d.ts +12 -0
- package/build/src/host/account_session_sign_in.js +19 -0
- package/build/src/host/admin_api/admin_users_service.d.ts +5 -2
- package/build/src/host/admin_api/api_clients_controller.d.ts +37 -0
- package/build/src/host/admin_api/api_clients_controller.js +31 -1
- package/build/src/host/admin_api/dto.d.ts +3 -1
- package/build/src/host/admin_api/dto.js +2 -0
- package/build/src/host/admin_authorize.d.ts +8 -0
- package/build/src/host/admin_authorize.js +25 -0
- package/build/src/host/admin_clients_service.d.ts +20 -0
- package/build/src/host/admin_clients_service.js +55 -0
- package/build/src/host/admin_console/admin_console_entry.d.ts +14 -0
- package/build/src/host/admin_console/admin_console_entry.js +80 -0
- package/build/src/host/admin_console/admin_shell_controller.js +8 -0
- package/build/src/host/admin_console/console_clients_controller.d.ts +41 -0
- package/build/src/host/admin_console/console_clients_controller.js +34 -1
- package/build/src/host/admin_console/console_personal_agents_controller.d.ts +1 -6
- package/build/src/host/admin_console/console_personal_agents_controller.js +50 -30
- package/build/src/host/admin_console/console_switcher_controller.d.ts +11 -0
- package/build/src/host/admin_console/console_switcher_controller.js +23 -0
- package/build/src/host/admin_sessions_service.js +8 -5
- package/build/src/host/admin_validators.d.ts +29 -0
- package/build/src/host/admin_validators.js +30 -0
- package/build/src/host/auth_host_config.d.ts +9 -2
- package/build/src/host/controllers/account_apps_controller.js +15 -11
- package/build/src/host/csrf.d.ts +3 -3
- package/build/src/host/csrf.js +3 -13
- package/build/src/host/default_mailer.d.ts +11 -6
- package/build/src/host/default_mailer.js +16 -12
- package/build/src/host/i18n.d.ts +41 -14
- package/build/src/host/i18n.js +55 -13
- package/build/src/host/rate_limit.d.ts +8 -0
- package/build/src/host/rate_limit.js +9 -0
- package/build/src/host/register_auth_host.d.ts +14 -0
- package/build/src/host/register_auth_host.js +167 -106
- package/build/src/host/renderers/edge_renderer.js +5 -0
- package/build/src/host/renderers/inertia_renderer.js +7 -1
- package/build/src/host/runtime_settings.d.ts +6 -6
- package/build/src/host/runtime_settings.js +4 -3
- package/build/src/host/screen_props.d.ts +39 -8
- package/build/src/host/ui-dist/assets/index-Dq3dJVBv.js +67 -0
- package/build/src/host/ui-dist/assets/{index-DTSmD4RU.css → index-WNIyCEPA.css} +1 -1
- package/build/src/host/ui-dist/index.html +2 -2
- package/build/src/provider/build_provider.js +37 -6
- package/build/src/provider/oidc_service.d.ts +1 -1
- package/build/src/provider/oidc_service.js +1 -1
- package/build/src/schema/ensure.d.ts +24 -1
- package/build/src/schema/ensure.js +44 -154
- package/build/src/schema/tables_location.d.ts +27 -0
- package/build/src/schema/tables_location.js +22 -0
- package/build/src/service_tenants/client_metadata.d.ts +39 -0
- package/build/src/service_tenants/client_metadata.js +80 -0
- package/build/src/service_tenants/config.d.ts +44 -0
- package/build/src/service_tenants/config.js +44 -0
- package/build/src/service_tenants/provider_hooks.d.ts +52 -0
- package/build/src/service_tenants/provider_hooks.js +124 -0
- package/build/src/service_tenants/tenant_verifier.d.ts +73 -0
- package/build/src/service_tenants/tenant_verifier.js +104 -0
- package/build/src/tenancy/tenant_labels.d.ts +30 -0
- package/build/src/tenancy/tenant_labels.js +64 -0
- package/build/types.d.ts +3 -1
- package/package.json +9 -6
- package/build/src/agents/poppy/client_registry.d.ts +0 -50
- package/build/src/agents/poppy/client_registry.js +0 -260
- package/build/src/agents/poppy/config.d.ts +0 -396
- package/build/src/agents/poppy/config.js +0 -228
- package/build/src/agents/poppy/dpop.d.ts +0 -61
- package/build/src/agents/poppy/dpop.js +0 -158
- package/build/src/agents/poppy/email_code_sign_in.d.ts +0 -27
- package/build/src/agents/poppy/email_code_sign_in.js +0 -140
- package/build/src/agents/poppy/errors.d.ts +0 -25
- package/build/src/agents/poppy/errors.js +0 -25
- package/build/src/agents/poppy/replay.d.ts +0 -25
- package/build/src/agents/poppy/replay.js +0 -106
- package/build/src/agents/poppy/resource.d.ts +0 -100
- package/build/src/agents/poppy/resource.js +0 -129
- package/build/src/agents/poppy/safe_fetch.d.ts +0 -48
- package/build/src/agents/poppy/safe_fetch.js +0 -262
- package/build/src/agents/poppy/service.d.ts +0 -293
- package/build/src/agents/poppy/service.js +0 -1458
- package/build/src/agents/poppy/session_jwt.d.ts +0 -30
- package/build/src/agents/poppy/session_jwt.js +0 -133
- package/build/src/agents/poppy/store.d.ts +0 -106
- package/build/src/agents/poppy/store.js +0 -261
- package/build/src/agents/poppy/web_session.d.ts +0 -39
- package/build/src/agents/poppy/web_session.js +0 -122
- package/build/src/host/controllers/poppy_consent_controller.d.ts +0 -19
- package/build/src/host/controllers/poppy_consent_controller.js +0 -199
- package/build/src/host/controllers/poppy_controller.d.ts +0 -33
- package/build/src/host/controllers/poppy_controller.js +0 -219
- package/build/src/host/poppy_session_key.d.ts +0 -9
- package/build/src/host/poppy_session_key.js +0 -9
- package/build/src/host/ui-dist/assets/index-D0EIeyKK.js +0 -67
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--bg0:#07070d;--bg1:#0c0c16;--bg2:#12121f;--bg3:#181828;--line:#9797c41a;--line-strong:#9797c42e;--text:#e9e9f4;--muted:#9292ac;--faint:#5b5b74;--accent:#625fff;--accent-soft:#625fff24;--accent-hover:#625fff38;--green:#3ecf8e;--green-soft:#3ecf8e1f;--red:#ff5470;--red-soft:#ff54701f;--amber:#ffb454;--amber-soft:#ffb4541f;--blue:#4cc2ff;--blue-soft:#4cc2ff1f;--sans:"Sora", system-ui, -apple-system, sans-serif;--mono:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--sidebar-w:240px;--radius:10px;--topbar-h:54px}html.light{--bg0:#f7f7fb;--bg1:#fff;--bg2:#f0f0f6;--bg3:#e7e7f0;--line:#2828461a;--line-strong:#2828462e;--text:#1c1c28;--muted:#55556c;--faint:#8a8aa0;--accent-soft:#625fff1f;--accent-hover:#625fff2e;--amber-soft:#c4821e24;--green-soft:#1ea06424;--red-soft:#dc32501f;--blue-soft:#2896d21f}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%}body{font-family:var(--sans);background:var(--bg0);color:var(--text);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:13px;overflow:hidden}body:before{content:"";pointer-events:none;z-index:0;background:radial-gradient(1100px 500px at 15% -10%,#625fff1a,#0000 60%),radial-gradient(900px 420px at 90% 0,#3ecf8e0a,#0000 55%);position:fixed;inset:0}html.light body:before{display:none}::selection{background:#625fff4d}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-thumb{border:2px solid var(--bg0);background:#26263a;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#33334d}::-webkit-scrollbar-track{background:0 0}html.light ::-webkit-scrollbar-thumb{background:#cfcfe0}html.light ::-webkit-scrollbar-thumb:hover{background:#b8b8cc}#root{z-index:1;position:relative}.app-shell{height:100vh;display:flex;overflow:hidden}.sidebar{width:var(--sidebar-w);border-right:1px solid var(--line);-webkit-backdrop-filter:blur(10px);background:#0a0a12bf;flex-direction:column;flex-shrink:0;display:flex;overflow:hidden}html.light .sidebar{background:#ffffffd9}.main{flex-direction:column;flex:1;min-width:0;display:flex;overflow:hidden}.topbar{height:var(--topbar-h);border-bottom:1px solid var(--line);-webkit-backdrop-filter:blur(8px);background:#09091099;flex-shrink:0;align-items:center;gap:12px;padding:0 20px;display:flex}html.light .topbar{background:#ffffffb3}.content{flex:1;padding:24px 24px 60px;overflow-y:auto}.brand{border-bottom:1px solid var(--line);flex-shrink:0;align-items:center;gap:10px;padding:18px 18px 14px;display:flex}.brand-logo{background:var(--accent-soft);border:1px solid #625fff4d;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;width:30px;height:30px;display:flex;position:relative}.brand-logo svg{width:16px;height:16px;color:var(--accent)}.brand-logo .ring{border:1px solid #625fff59;border-radius:12px;animation:3s ease-out infinite ring-pulse;position:absolute;inset:-3px}@keyframes ring-pulse{0%{opacity:.9;transform:scale(.85)}70%{opacity:0;transform:scale(1.15)}to{opacity:0;transform:scale(1.15)}}.brand-text{overflow:hidden}.brand-name{letter-spacing:.03em;font-size:14px;font-weight:700}.brand-name span{color:var(--accent)}.brand-sub{color:var(--faint);letter-spacing:.08em;text-transform:uppercase;margin-top:1px;font-size:10px;font-weight:500}.nav{flex:1;padding:10px 10px 8px;overflow-y:auto}.nav-section{letter-spacing:.14em;text-transform:uppercase;color:var(--faint);padding:12px 8px 5px;font-size:9.5px;font-weight:600}.nav-item{color:var(--muted);cursor:pointer;-webkit-user-select:none;user-select:none;border:1px solid #0000;border-radius:8px;align-items:center;gap:9px;padding:7px 9px;font-size:12.5px;font-weight:500;text-decoration:none;transition:background .12s,color .12s;display:flex}.nav-item:hover{color:var(--text);background:#9797c40f}.nav-item.active{background:var(--accent-soft);color:var(--text);border-color:#625fff38}.nav-item.active .nav-ico{color:var(--accent)}.nav-ico{width:15px;height:15px;color:var(--faint);flex-shrink:0;transition:color .12s}.nav-item:hover .nav-ico{color:var(--muted)}.sidebar-foot{border-top:1px solid var(--line);flex-direction:column;flex-shrink:0;gap:10px;padding:12px 14px;display:flex}.user-row{cursor:default;border-radius:8px;align-items:center;gap:9px;padding:6px 8px;display:flex}.avatar{background:var(--accent-soft);width:28px;height:28px;color:var(--accent);border:1px solid #625fff4d;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:10.5px;font-weight:600;display:flex}.user-info{flex:1;min-width:0}.user-email{color:var(--text);text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:500;overflow:hidden}.user-role{color:var(--faint);font-size:9.5px;font-family:var(--mono)}.foot-actions{gap:6px;display:flex}.topbar-title{color:var(--text);font-size:14px;font-weight:600}.topbar-spacer{flex:1}.theme-btn{border:1px solid var(--line);background:var(--bg2);width:32px;height:32px;color:var(--muted);cursor:pointer;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;transition:all .13s;display:flex}.theme-btn:hover{background:var(--bg3);color:var(--text);border-color:var(--line-strong)}.theme-btn svg{width:14px;height:14px}.btn{background:var(--bg2);border:1px solid var(--line);height:32px;color:var(--muted);font-family:var(--sans);cursor:pointer;white-space:nowrap;border-radius:8px;align-items:center;gap:7px;padding:0 13px;font-size:12px;font-weight:500;text-decoration:none;transition:all .13s;display:inline-flex}.btn:hover{color:var(--text);border-color:var(--line-strong);background:var(--bg3)}.btn svg{flex-shrink:0;width:13px;height:13px}.btn.btn-primary{background:var(--accent);color:#fff;border-color:#0000}.btn.btn-primary:hover{background:#7472ff}.btn.btn-danger:hover{color:var(--red);background:var(--red-soft);border-color:#ff547066}.btn.btn-sm{border-radius:6px;height:26px;padding:0 10px;font-size:11px}.btn.btn-ghost{background:0 0;border-color:#0000}.btn.btn-ghost:hover{background:#9797c40f}.btn:disabled,.btn[disabled]{opacity:.45;pointer-events:none}.cards{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:20px;display:grid}.card{border:1px solid var(--line);border-radius:var(--radius);background:linear-gradient(#9797c40a,#9797c403);padding:16px 18px 14px;animation:.45s cubic-bezier(.2,.7,.3,1) both rise}.card:nth-child(2){animation-delay:40ms}.card:nth-child(3){animation-delay:80ms}.card:nth-child(4){animation-delay:.12s}.card:nth-child(5){animation-delay:.16s}@keyframes rise{0%{opacity:0;transform:translateY(8px)}}.card-label{letter-spacing:.12em;text-transform:uppercase;color:var(--faint);margin-bottom:10px;font-size:10px;font-weight:600}.card-value{font-family:var(--mono);letter-spacing:-.02em;color:var(--text);font-size:26px;font-weight:600;line-height:1}.card-hint{color:var(--faint);font-size:10.5px;font-family:var(--mono);margin-top:8px}.card-hint b{color:var(--muted);font-weight:500}.card.c-accent .card-value{color:var(--accent)}.card.c-green .card-value{color:var(--green)}.card.c-amber .card-value{color:var(--amber)}.card.c-red .card-value{color:var(--red)}.card.c-blue .card-value{color:var(--blue)}.panel{border:1px solid var(--line);border-radius:var(--radius);background:linear-gradient(#9797c409,#9797c403);margin-bottom:16px;animation:.5s cubic-bezier(.2,.7,.3,1) both rise;overflow:hidden}.panel-head{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:10px;padding:12px 16px;display:flex}.panel-head h3{letter-spacing:.02em;font-size:12.5px;font-weight:600}.panel-head .meta{font-family:var(--mono);color:var(--faint);font-size:10.5px}.panel-body{padding:12px 16px 16px}.grid-2{grid-template-columns:1fr 1fr;gap:16px;display:grid}@media (width<=1100px){.grid-2{grid-template-columns:1fr}}.page-header{margin-bottom:20px}.page-title{letter-spacing:.01em;margin-bottom:4px;font-size:18px;font-weight:700}.page-sub{color:var(--faint);font-size:12px}.page-actions{align-items:center;gap:8px;display:flex}.page-header-row{justify-content:space-between;align-items:center;gap:16px;margin-bottom:16px;display:flex}.tbl-wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:12.5px}thead th{text-align:left;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);border-bottom:1px solid var(--line);white-space:nowrap;padding:8px 12px;font-size:10px;font-weight:600}tbody tr{border-bottom:1px solid var(--line);cursor:pointer;transition:background .1s}tbody tr:last-child{border-bottom:0}tbody tr:hover{background:#9797c40a}tbody td{color:var(--muted);vertical-align:middle;padding:10px 12px}tbody td b{color:var(--text);font-weight:500}tbody td.no-click{cursor:default}.badge{height:18px;font-family:var(--mono);white-space:nowrap;border-radius:99px;align-items:center;padding:0 8px;font-size:10px;font-weight:500;display:inline-flex}.badge-accent{background:var(--accent-soft);color:var(--accent);border:1px solid #625fff38}.badge-green{background:var(--green-soft);color:var(--green);border:1px solid #3ecf8e38}.badge-red{background:var(--red-soft);color:var(--red);border:1px solid #ff547038}.badge-amber{background:var(--amber-soft);color:var(--amber);border:1px solid #ffb45438}.badge-muted{color:var(--muted);border:1px solid var(--line);background:#9797c412}.badge-blue{background:var(--blue-soft);color:var(--blue);border:1px solid #4cc2ff38}.input{background:var(--bg2);border:1px solid var(--line);width:100%;height:34px;color:var(--text);font-family:var(--sans);border-radius:8px;outline:none;padding:0 12px;font-size:13px;transition:border-color .15s,box-shadow .15s}.input:focus{border-color:#625fff80;box-shadow:0 0 0 3px #625fff1c}.input::placeholder{color:var(--faint)}.input-mono{font-family:var(--mono);font-size:12px}textarea.input{resize:vertical;height:auto;padding:10px 12px}.search-input{position:relative}.search-input input{padding-left:32px}.search-ico{width:14px;height:14px;color:var(--faint);pointer-events:none;position:absolute;top:50%;left:10px;transform:translateY(-50%)}.field{margin-bottom:14px}.field label{color:var(--muted);letter-spacing:.02em;margin-bottom:5px;font-size:11px;font-weight:500;display:block}.field .hint{color:var(--faint);margin-top:4px;font-size:10.5px}.checkbox-row{cursor:pointer;align-items:center;gap:9px;padding:7px 0;display:flex}.checkbox-row input[type=checkbox]{width:14px;height:14px;accent-color:var(--accent);cursor:pointer}.checkbox-row .chk-label{color:var(--muted);font-size:12.5px}.checkbox-row:hover .chk-label{color:var(--text)}.toggle-row{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;padding:10px 0;display:flex}.toggle-row:last-child{border-bottom:0}.toggle-info{flex:1;min-width:0}.toggle-label{color:var(--text);font-size:12.5px;font-weight:500}.toggle-desc{color:var(--faint);margin-top:2px;font-size:11px}.toggle{flex-shrink:0;width:38px;height:20px;position:relative}.toggle input{opacity:0;width:0;height:0;position:absolute}.toggle-track{background:var(--bg3);border:1px solid var(--line-strong);cursor:pointer;border-radius:99px;transition:background .2s;position:absolute;inset:0}.toggle input:checked+.toggle-track{background:var(--accent);border-color:var(--accent)}.toggle-thumb{background:var(--faint);pointer-events:none;border-radius:50%;width:14px;height:14px;transition:transform .2s,background .2s;position:absolute;top:2px;left:2px}.toggle input:checked~.toggle-thumb{background:#fff;transform:translate(18px)}.modal-overlay{z-index:100;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0009;justify-content:center;align-items:center;padding:20px;animation:.15s fade-in;display:flex;position:fixed;inset:0}@keyframes fade-in{0%{opacity:0}}.modal{background:var(--bg1);border:1px solid var(--line-strong);border-radius:14px;flex-direction:column;width:100%;max-width:520px;max-height:90vh;animation:.2s cubic-bezier(.2,.8,.3,1) modal-in;display:flex;overflow:hidden}@keyframes modal-in{0%{opacity:0;transform:scale(.96)translateY(8px)}}.modal.modal-lg{max-width:760px}.modal-head{border-bottom:1px solid var(--line);flex-shrink:0;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.modal-head h2{font-size:14px;font-weight:600}.modal-close{width:28px;height:28px;color:var(--faint);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;transition:all .12s;display:flex}.modal-close:hover{color:var(--text);background:#9797c414}.modal-close svg{width:14px;height:14px}.modal-body{flex:1;padding:20px;overflow-y:auto}.modal-foot{border-top:1px solid var(--line);flex-shrink:0;justify-content:flex-end;gap:8px;padding:14px 20px;display:flex}.drawer-overlay{z-index:100;background:#00000080;animation:.15s fade-in;position:fixed;inset:0}.drawer{z-index:101;background:var(--bg1);border-left:1px solid var(--line-strong);flex-direction:column;width:480px;max-width:95vw;animation:.22s cubic-bezier(.2,.8,.3,1) drawer-in;display:flex;position:fixed;top:0;bottom:0;right:0;overflow:hidden}@keyframes drawer-in{0%{transform:translate(100%)}}.drawer-head{border-bottom:1px solid var(--line);flex-shrink:0;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.drawer-head h2{font-size:14px;font-weight:600}.drawer-body{flex:1;padding:20px;overflow-y:auto}.pagination{justify-content:flex-end;align-items:center;gap:6px;padding-top:16px;display:flex}.pag-info{font-family:var(--mono);color:var(--faint);margin-right:4px;font-size:11px}.empty-state{text-align:center;flex-direction:column;justify-content:center;align-items:center;gap:10px;padding:48px 24px;display:flex}.empty-state svg{width:36px;height:36px;color:var(--faint);margin-bottom:4px}.empty-state h4{color:var(--muted);font-size:14px;font-weight:600}.empty-state p{color:var(--faint);max-width:300px;font-size:12px}.spinner{border:2px solid var(--bg3);border-top-color:var(--accent);border-radius:50%;flex-shrink:0;width:20px;height:20px;animation:.7s linear infinite spin}.spinner.sm{width:14px;height:14px}.spinner.lg{border-width:3px;width:28px;height:28px}@keyframes spin{to{transform:rotate(360deg)}}.loading-row{color:var(--faint);justify-content:center;align-items:center;gap:12px;padding:40px;font-size:12px;display:flex}.error-box{background:var(--red-soft);border-radius:var(--radius);color:var(--red);border:1px solid #ff547040;padding:12px 16px;font-size:12px}.settings-section{margin-bottom:24px}.settings-section-head{border-bottom:1px solid var(--line);align-items:center;gap:10px;margin-bottom:4px;padding-bottom:10px;display:flex}.settings-section-head h3{font-size:13px;font-weight:600}.settings-section-head p{color:var(--faint);margin-top:1px;font-size:11px}.settings-row{border-bottom:1px solid var(--line);justify-content:space-between;align-items:flex-start;gap:20px;padding:12px 0;display:flex}.settings-row:last-child{border-bottom:0}.settings-info{flex:1;min-width:0}.settings-key{font-family:var(--mono);color:var(--text);font-size:12px;font-weight:500}.settings-desc{color:var(--faint);margin-top:2px;font-size:11px}.settings-badge{font-family:var(--mono);color:var(--faint);border:1px solid var(--line);background:#9797c412;border-radius:4px;margin-left:6px;padding:1px 6px;font-size:9.5px}.settings-control{flex-shrink:0;align-items:center;gap:8px;display:flex}.code{font-family:var(--mono);background:var(--bg2);border:1px solid var(--line);color:var(--muted);word-break:break-all;border-radius:6px;padding:2px 8px;font-size:11.5px}.sep{background:var(--line);height:1px;margin:16px 0}.flex{display:flex}.flex-center{align-items:center;display:flex}.gap-2{gap:8px}.gap-3{gap:12px}.ml-auto{margin-left:auto}.text-accent{color:var(--accent)}.text-muted{color:var(--muted)}.text-faint{color:var(--faint)}.text-green{color:var(--green)}.text-red{color:var(--red)}.text-amber{color:var(--amber)}.mono{font-family:var(--mono)}.text-sm{font-size:11px}.text-xs{font-size:10px}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.secret-box{background:var(--bg2);font-family:var(--mono);color:var(--accent);word-break:break-all;border:1px solid #625fff40;border-radius:8px;margin:8px 0;padding:12px 16px;font-size:12px}.secret-box small{color:var(--faint);font-size:10.5px;font-family:var(--sans);letter-spacing:.06em;text-transform:uppercase;margin-bottom:6px;display:block}.chart-svg{width:100%;height:90px;display:block;overflow:visible}.toast-container{z-index:200;flex-direction:column-reverse;gap:8px;display:flex;position:fixed;bottom:20px;right:20px}.toast{background:var(--bg2);border:1px solid var(--line-strong);color:var(--text);border-radius:10px;align-items:center;gap:10px;min-width:260px;max-width:380px;padding:10px 14px;font-size:12.5px;animation:.22s cubic-bezier(.2,.8,.3,1) toast-in;display:flex;box-shadow:0 4px 24px #0006}.toast svg{flex-shrink:0;width:15px;height:15px}.toast.toast-success svg{color:var(--green)}.toast.toast-error svg{color:var(--red)}.toast.toast-info svg{color:var(--accent)}@keyframes toast-in{0%{opacity:0;transform:translate(16px)}}.chart-tip{background:var(--panel,var(--bg));border:1px solid var(--line);color:var(--text);border-radius:8px;padding:7px 10px;font-size:11.5px;box-shadow:0 6px 24px #00000040}.chart-tip-label{color:var(--faint);margin-bottom:4px;font-size:10px}.chart-tip-row{align-items:center;gap:6px;display:flex}.chart-tip-row b{font-weight:600}.chart-tip-dot{border-radius:2px;flex:none;width:7px;height:7px}
|
|
1
|
+
:root{--bg0:#07070d;--bg1:#0c0c16;--bg2:#12121f;--bg3:#181828;--line:#9797c41a;--line-strong:#9797c42e;--text:#e9e9f4;--muted:#9292ac;--faint:#5b5b74;--accent:#625fff;--accent-soft:#625fff24;--accent-hover:#625fff38;--green:#3ecf8e;--green-soft:#3ecf8e1f;--red:#ff5470;--red-soft:#ff54701f;--amber:#ffb454;--amber-soft:#ffb4541f;--blue:#4cc2ff;--blue-soft:#4cc2ff1f;--sans:"Sora", system-ui, -apple-system, sans-serif;--mono:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--sidebar-w:240px;--radius:10px;--topbar-h:54px}html.light{--bg0:#f7f7fb;--bg1:#fff;--bg2:#f0f0f6;--bg3:#e7e7f0;--line:#2828461a;--line-strong:#2828462e;--text:#1c1c28;--muted:#55556c;--faint:#8a8aa0;--accent-soft:#625fff1f;--accent-hover:#625fff2e;--amber-soft:#c4821e24;--green-soft:#1ea06424;--red-soft:#dc32501f;--blue-soft:#2896d21f}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%}body{font-family:var(--sans);background:var(--bg0);color:var(--text);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:13px;overflow:hidden}body:before{content:"";pointer-events:none;z-index:0;background:radial-gradient(1100px 500px at 15% -10%,#625fff1a,#0000 60%),radial-gradient(900px 420px at 90% 0,#3ecf8e0a,#0000 55%);position:fixed;inset:0}html.light body:before{display:none}::selection{background:#625fff4d}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-thumb{border:2px solid var(--bg0);background:#26263a;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#33334d}::-webkit-scrollbar-track{background:0 0}html.light ::-webkit-scrollbar-thumb{background:#cfcfe0}html.light ::-webkit-scrollbar-thumb:hover{background:#b8b8cc}#root{z-index:1;position:relative}.app-shell{height:100vh;display:flex;overflow:hidden}.sidebar{width:var(--sidebar-w);border-right:1px solid var(--line);-webkit-backdrop-filter:blur(10px);background:#0a0a12bf;flex-direction:column;flex-shrink:0;display:flex;overflow:hidden}html.light .sidebar{background:#ffffffd9}.main{flex-direction:column;flex:1;min-width:0;display:flex;overflow:hidden}.topbar{height:var(--topbar-h);border-bottom:1px solid var(--line);-webkit-backdrop-filter:blur(8px);background:#09091099;flex-shrink:0;align-items:center;gap:12px;padding:0 20px;display:flex}html.light .topbar{background:#ffffffb3}.content{flex:1;padding:24px 24px 60px;overflow-y:auto}.brand{border-bottom:1px solid var(--line);flex-shrink:0;align-items:center;gap:10px;padding:18px 18px 14px;display:flex}.brand-logo{background:var(--accent-soft);border:1px solid #625fff4d;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;width:30px;height:30px;display:flex;position:relative}.brand-logo svg{width:16px;height:16px;color:var(--accent)}.brand-logo .ring{border:1px solid #625fff59;border-radius:12px;animation:3s ease-out infinite ring-pulse;position:absolute;inset:-3px}@keyframes ring-pulse{0%{opacity:.9;transform:scale(.85)}70%{opacity:0;transform:scale(1.15)}to{opacity:0;transform:scale(1.15)}}.brand-text{overflow:hidden}.brand-name{letter-spacing:.03em;font-size:14px;font-weight:700}.brand-name span{color:var(--accent)}.brand-sub{color:var(--faint);letter-spacing:.08em;text-transform:uppercase;margin-top:1px;font-size:10px;font-weight:500}.nav{flex:1;padding:10px 10px 8px;overflow-y:auto}.nav-section{letter-spacing:.14em;text-transform:uppercase;color:var(--faint);padding:12px 8px 5px;font-size:9.5px;font-weight:600}.nav-item{color:var(--muted);cursor:pointer;-webkit-user-select:none;user-select:none;border:1px solid #0000;border-radius:8px;align-items:center;gap:9px;padding:7px 9px;font-size:12.5px;font-weight:500;text-decoration:none;transition:background .12s,color .12s;display:flex}.nav-item:hover{color:var(--text);background:#9797c40f}.nav-item.active{background:var(--accent-soft);color:var(--text);border-color:#625fff38}.nav-item.active .nav-ico{color:var(--accent)}.nav-ico{width:15px;height:15px;color:var(--faint);flex-shrink:0;transition:color .12s}.nav-item:hover .nav-ico{color:var(--muted)}.sidebar-foot{border-top:1px solid var(--line);flex-direction:column;flex-shrink:0;gap:10px;padding:12px 14px;display:flex}.user-row{cursor:default;border-radius:8px;align-items:center;gap:9px;padding:6px 8px;display:flex}.avatar{background:var(--accent-soft);width:28px;height:28px;color:var(--accent);border:1px solid #625fff4d;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:10.5px;font-weight:600;display:flex}.user-info{flex:1;min-width:0}.user-email{color:var(--text);text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:500;overflow:hidden}.user-role{color:var(--faint);font-size:9.5px;font-family:var(--mono)}.foot-actions{gap:6px;display:flex}.topbar-title{color:var(--text);font-size:14px;font-weight:600}.topbar-spacer{flex:1}.theme-btn{border:1px solid var(--line);background:var(--bg2);width:32px;height:32px;color:var(--muted);cursor:pointer;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;transition:all .13s;display:flex}.theme-btn:hover{background:var(--bg3);color:var(--text);border-color:var(--line-strong)}.theme-btn svg{width:14px;height:14px}.btn{background:var(--bg2);border:1px solid var(--line);height:32px;color:var(--muted);font-family:var(--sans);cursor:pointer;white-space:nowrap;border-radius:8px;align-items:center;gap:7px;padding:0 13px;font-size:12px;font-weight:500;text-decoration:none;transition:all .13s;display:inline-flex}.btn:hover{color:var(--text);border-color:var(--line-strong);background:var(--bg3)}.btn svg{flex-shrink:0;width:13px;height:13px}.btn.btn-primary{background:var(--accent);color:#fff;border-color:#0000}.btn.btn-primary:hover{background:#7472ff}.btn.btn-danger:hover{color:var(--red);background:var(--red-soft);border-color:#ff547066}.btn.btn-sm{border-radius:6px;height:26px;padding:0 10px;font-size:11px}.btn.btn-ghost{background:0 0;border-color:#0000}.btn.btn-ghost:hover{background:#9797c40f}.btn:disabled,.btn[disabled]{opacity:.45;pointer-events:none}.cards{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:20px;display:grid}.card{border:1px solid var(--line);border-radius:var(--radius);background:linear-gradient(#9797c40a,#9797c403);padding:16px 18px 14px;animation:.45s cubic-bezier(.2,.7,.3,1) both rise}.card:nth-child(2){animation-delay:40ms}.card:nth-child(3){animation-delay:80ms}.card:nth-child(4){animation-delay:.12s}.card:nth-child(5){animation-delay:.16s}@keyframes rise{0%{opacity:0;transform:translateY(8px)}}.card-label{letter-spacing:.12em;text-transform:uppercase;color:var(--faint);margin-bottom:10px;font-size:10px;font-weight:600}.card-value{font-family:var(--mono);letter-spacing:-.02em;color:var(--text);font-size:26px;font-weight:600;line-height:1}.card-hint{color:var(--faint);font-size:10.5px;font-family:var(--mono);margin-top:8px}.card-hint b{color:var(--muted);font-weight:500}.card.c-accent .card-value{color:var(--accent)}.card.c-green .card-value{color:var(--green)}.card.c-amber .card-value{color:var(--amber)}.card.c-red .card-value{color:var(--red)}.card.c-blue .card-value{color:var(--blue)}.panel{border:1px solid var(--line);border-radius:var(--radius);background:linear-gradient(#9797c409,#9797c403);margin-bottom:16px;animation:.5s cubic-bezier(.2,.7,.3,1) both rise;overflow:hidden}.panel-head{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:10px;padding:12px 16px;display:flex}.panel-head h3{letter-spacing:.02em;font-size:12.5px;font-weight:600}.panel-head .meta{font-family:var(--mono);color:var(--faint);font-size:10.5px}.panel-body{padding:12px 16px 16px}.grid-2{grid-template-columns:1fr 1fr;gap:16px;display:grid}@media (width<=1100px){.grid-2{grid-template-columns:1fr}}.page-header{margin-bottom:20px}.page-title{letter-spacing:.01em;margin-bottom:4px;font-size:18px;font-weight:700}.page-sub{color:var(--faint);font-size:12px}.page-actions{align-items:center;gap:8px;display:flex}.page-header-row{justify-content:space-between;align-items:center;gap:16px;margin-bottom:16px;display:flex}.tbl-wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:12.5px}thead th{text-align:left;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);border-bottom:1px solid var(--line);white-space:nowrap;padding:8px 12px;font-size:10px;font-weight:600}tbody tr{border-bottom:1px solid var(--line);cursor:pointer;transition:background .1s}tbody tr:last-child{border-bottom:0}tbody tr:hover{background:#9797c40a}tbody td{color:var(--muted);vertical-align:middle;padding:10px 12px}tbody td b{color:var(--text);font-weight:500}tbody td.no-click{cursor:default}.badge{height:18px;font-family:var(--mono);white-space:nowrap;border-radius:99px;align-items:center;padding:0 8px;font-size:10px;font-weight:500;display:inline-flex}.badge-accent{background:var(--accent-soft);color:var(--accent);border:1px solid #625fff38}.badge-green{background:var(--green-soft);color:var(--green);border:1px solid #3ecf8e38}.badge-red{background:var(--red-soft);color:var(--red);border:1px solid #ff547038}.badge-amber{background:var(--amber-soft);color:var(--amber);border:1px solid #ffb45438}.badge-muted{color:var(--muted);border:1px solid var(--line);background:#9797c412}.badge-blue{background:var(--blue-soft);color:var(--blue);border:1px solid #4cc2ff38}.input{background:var(--bg2);border:1px solid var(--line);width:100%;height:34px;color:var(--text);font-family:var(--sans);border-radius:8px;outline:none;padding:0 12px;font-size:13px;transition:border-color .15s,box-shadow .15s}.input:focus{border-color:#625fff80;box-shadow:0 0 0 3px #625fff1c}.input::placeholder{color:var(--faint)}.input-mono{font-family:var(--mono);font-size:12px}textarea.input{resize:vertical;height:auto;padding:10px 12px}.search-input{position:relative}.search-input input{padding-left:32px}.search-ico{width:14px;height:14px;color:var(--faint);pointer-events:none;position:absolute;top:50%;left:10px;transform:translateY(-50%)}.field{margin-bottom:14px}.field label{color:var(--muted);letter-spacing:.02em;margin-bottom:5px;font-size:11px;font-weight:500;display:block}.field .hint{color:var(--faint);margin-top:4px;font-size:10.5px}.checkbox-row{cursor:pointer;align-items:center;gap:9px;padding:7px 0;display:flex}.checkbox-row input[type=checkbox]{width:14px;height:14px;accent-color:var(--accent);cursor:pointer}.checkbox-row .chk-label{color:var(--muted);font-size:12.5px}.checkbox-row:hover .chk-label{color:var(--text)}.toggle-row{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;padding:10px 0;display:flex}.toggle-row:last-child{border-bottom:0}.toggle-info{flex:1;min-width:0}.toggle-label{color:var(--text);font-size:12.5px;font-weight:500}.toggle-desc{color:var(--faint);margin-top:2px;font-size:11px}.toggle{flex-shrink:0;width:38px;height:20px;position:relative}.toggle input{opacity:0;width:0;height:0;position:absolute}.toggle-track{background:var(--bg3);border:1px solid var(--line-strong);cursor:pointer;border-radius:99px;transition:background .2s;position:absolute;inset:0}.toggle input:checked+.toggle-track{background:var(--accent);border-color:var(--accent)}.toggle-thumb{background:var(--faint);pointer-events:none;border-radius:50%;width:14px;height:14px;transition:transform .2s,background .2s;position:absolute;top:2px;left:2px}.toggle input:checked~.toggle-thumb{background:#fff;transform:translate(18px)}.modal-overlay{z-index:100;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0009;justify-content:center;align-items:center;padding:20px;animation:.15s fade-in;display:flex;position:fixed;inset:0}@keyframes fade-in{0%{opacity:0}}.modal{background:var(--bg1);border:1px solid var(--line-strong);border-radius:14px;flex-direction:column;width:100%;max-width:520px;max-height:90vh;animation:.2s cubic-bezier(.2,.8,.3,1) modal-in;display:flex;overflow:hidden}@keyframes modal-in{0%{opacity:0;transform:scale(.96)translateY(8px)}}.modal.modal-lg{max-width:760px}.modal-head{border-bottom:1px solid var(--line);flex-shrink:0;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.modal-head h2{font-size:14px;font-weight:600}.modal-close{width:28px;height:28px;color:var(--faint);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;transition:all .12s;display:flex}.modal-close:hover{color:var(--text);background:#9797c414}.modal-close svg{width:14px;height:14px}.modal-body{flex:1;padding:20px;overflow-y:auto}.modal-foot{border-top:1px solid var(--line);flex-shrink:0;justify-content:flex-end;gap:8px;padding:14px 20px;display:flex}.drawer-overlay{z-index:100;background:#00000080;animation:.15s fade-in;position:fixed;inset:0}.drawer{z-index:101;background:var(--bg1);border-left:1px solid var(--line-strong);flex-direction:column;width:480px;max-width:95vw;animation:.22s cubic-bezier(.2,.8,.3,1) drawer-in;display:flex;position:fixed;top:0;bottom:0;right:0;overflow:hidden}@keyframes drawer-in{0%{transform:translate(100%)}}.drawer-head{border-bottom:1px solid var(--line);flex-shrink:0;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.drawer-head h2{font-size:14px;font-weight:600}.drawer-body{flex:1;padding:20px;overflow-y:auto}.pagination{justify-content:flex-end;align-items:center;gap:6px;padding-top:16px;display:flex}.pag-info{font-family:var(--mono);color:var(--faint);margin-right:4px;font-size:11px}.empty-state{text-align:center;flex-direction:column;justify-content:center;align-items:center;gap:10px;padding:48px 24px;display:flex}.empty-state svg{width:36px;height:36px;color:var(--faint);margin-bottom:4px}.empty-state h4{color:var(--muted);font-size:14px;font-weight:600}.empty-state p{color:var(--faint);max-width:300px;font-size:12px}.spinner{border:2px solid var(--bg3);border-top-color:var(--accent);border-radius:50%;flex-shrink:0;width:20px;height:20px;animation:.7s linear infinite spin}.spinner.sm{width:14px;height:14px}.spinner.lg{border-width:3px;width:28px;height:28px}@keyframes spin{to{transform:rotate(360deg)}}.loading-row{color:var(--faint);justify-content:center;align-items:center;gap:12px;padding:40px;font-size:12px;display:flex}.error-box{background:var(--red-soft);border-radius:var(--radius);color:var(--red);border:1px solid #ff547040;padding:12px 16px;font-size:12px}.settings-section{margin-bottom:24px}.settings-section-head{border-bottom:1px solid var(--line);align-items:center;gap:10px;margin-bottom:4px;padding-bottom:10px;display:flex}.settings-section-head h3{font-size:13px;font-weight:600}.settings-section-head p{color:var(--faint);margin-top:1px;font-size:11px}.settings-row{border-bottom:1px solid var(--line);justify-content:space-between;align-items:flex-start;gap:20px;padding:12px 0;display:flex}.settings-row:last-child{border-bottom:0}.settings-info{flex:1;min-width:0}.settings-key{font-family:var(--mono);color:var(--text);font-size:12px;font-weight:500}.settings-desc{color:var(--faint);margin-top:2px;font-size:11px}.settings-badge{font-family:var(--mono);color:var(--faint);border:1px solid var(--line);background:#9797c412;border-radius:4px;margin-left:6px;padding:1px 6px;font-size:9.5px}.settings-control{flex-shrink:0;align-items:center;gap:8px;display:flex}.code{font-family:var(--mono);background:var(--bg2);border:1px solid var(--line);color:var(--muted);word-break:break-all;border-radius:6px;padding:2px 8px;font-size:11.5px}.sep{background:var(--line);height:1px;margin:16px 0}.flex{display:flex}.flex-center{align-items:center;display:flex}.gap-2{gap:8px}.gap-3{gap:12px}.ml-auto{margin-left:auto}.text-accent{color:var(--accent)}.text-muted{color:var(--muted)}.text-faint{color:var(--faint)}.text-green{color:var(--green)}.text-red{color:var(--red)}.text-amber{color:var(--amber)}.mono{font-family:var(--mono)}.text-sm{font-size:11px}.text-xs{font-size:10px}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.secret-box{background:var(--bg2);font-family:var(--mono);color:var(--accent);word-break:break-all;border:1px solid #625fff40;border-radius:8px;margin:8px 0;padding:12px 16px;font-size:12px}.secret-box small{color:var(--faint);font-size:10.5px;font-family:var(--sans);letter-spacing:.06em;text-transform:uppercase;margin-bottom:6px;display:block}.chart-svg{width:100%;height:90px;display:block;overflow:visible}.toast-container{z-index:200;flex-direction:column-reverse;gap:8px;display:flex;position:fixed;bottom:20px;right:20px}.toast{background:var(--bg2);border:1px solid var(--line-strong);color:var(--text);border-radius:10px;align-items:center;gap:10px;min-width:260px;max-width:380px;padding:10px 14px;font-size:12.5px;animation:.22s cubic-bezier(.2,.8,.3,1) toast-in;display:flex;box-shadow:0 4px 24px #0006}.toast svg{flex-shrink:0;width:15px;height:15px}.toast.toast-success svg{color:var(--green)}.toast.toast-error svg{color:var(--red)}.toast.toast-info svg{color:var(--accent)}@keyframes toast-in{0%{opacity:0;transform:translate(16px)}}.chart-tip{background:var(--panel,var(--bg));border:1px solid var(--line);color:var(--text);border-radius:8px;padding:7px 10px;font-size:11.5px;box-shadow:0 6px 24px #00000040}.chart-tip-label{color:var(--faint);margin-bottom:4px;font-size:10px}.chart-tip-row{align-items:center;gap:6px;display:flex}.chart-tip-row b{font-weight:600}.chart-tip-dot{border-radius:2px;flex:none;width:7px;height:7px}.brand.switcher{padding:10px 10px 8px;position:relative}.switcher-button{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px;display:flex}.switcher-button:hover,.switcher-button[aria-expanded=true]{background:var(--bg2);border-color:var(--line)}.switcher-button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.switcher-chevron{color:var(--faint);flex-shrink:0;margin-left:auto}.switcher-menu{z-index:50;background:var(--bg1);border:1px solid var(--line-strong);border-radius:8px;padding:4px;position:absolute;top:calc(100% - 2px);left:10px;right:10px}.switcher-item{color:var(--muted);border-radius:6px;grid-template-columns:16px 1fr auto;align-items:center;column-gap:10px;padding:7px 8px;font-size:12.5px;text-decoration:none;display:grid}.switcher-item .switcher-package{font-family:var(--mono);color:var(--faint);grid-column:2/4;font-size:10px}.switcher-item.current{color:var(--text)}.switcher-check{color:var(--accent)}a.switcher-item:hover,a.switcher-item:focus-visible{background:var(--accent-soft);color:var(--text);outline:none}.switcher-title{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.search-trigger{align-items:center;gap:6px;display:inline-flex}.search-trigger kbd{font-family:var(--mono);color:var(--faint);border:1px solid var(--line);border-radius:4px;padding:0 4px;font-size:10px}.search-overlay{align-items:flex-start;padding-top:12vh}.search-dialog{background:var(--bg1);border:1px solid var(--line-strong);border-radius:var(--radius);width:min(560px,100%);overflow:hidden}.search-input{border:0;border-bottom:1px solid var(--line);width:100%;color:var(--text);font:inherit;background:0 0;outline:none;padding:14px 16px;font-size:14px}.search-results{max-height:52vh;padding:6px;overflow-y:auto}.search-empty{color:var(--faint);padding:14px 10px;font-size:12.5px}.search-group+.search-group{margin-top:6px}.search-group-title{letter-spacing:.1em;text-transform:uppercase;color:var(--faint);align-items:center;gap:6px;padding:6px 10px 4px;font-size:10px;font-weight:600;display:flex}.search-hit{color:var(--text);border-radius:6px;flex-direction:column;gap:1px;padding:7px 10px;text-decoration:none;display:flex}.search-hit.active{background:var(--accent-soft)}.search-hit-title{font-size:13px}.search-hit-sub{color:var(--muted);font-size:11px}.consoles-panel{margin-top:16px}.console-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px;display:grid}.console-card{border:1px solid var(--line);color:var(--text);border-radius:8px;align-items:center;gap:12px;padding:12px;text-decoration:none;display:flex}.console-card:hover,.console-card:focus-visible{border-color:var(--line-strong);background:var(--bg2);outline:none}.console-card-icon{background:var(--accent-soft);width:36px;height:36px;color:var(--accent);border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;display:flex}.console-card-text{flex-direction:column;min-width:0;display:flex}.console-card-title{font-size:13px;font-weight:600}.console-card-path{font-family:var(--mono);color:var(--faint);font-size:10.5px}@media (width<=640px){.switcher-menu{border-radius:0;position:fixed;top:64px;left:0;right:0}}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
10
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700&family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
|
|
11
|
-
<script type="module" crossorigin src="/__AUTHKIT_BASE__/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/__AUTHKIT_BASE__/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/__AUTHKIT_BASE__/assets/index-Dq3dJVBv.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/__AUTHKIT_BASE__/assets/index-WNIyCEPA.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
|
@@ -3,6 +3,8 @@ import { pickModelAdapterClass } from '../adapters/factory.js';
|
|
|
3
3
|
import { normalizeActiveOrg, readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
|
|
4
4
|
import { consoleSsoClientMetadata } from '../host/console_sso.js';
|
|
5
5
|
import { findMcpResource, mcpAuthorizeMiddleware } from '../mcp/mcp_oauth.js';
|
|
6
|
+
import { hideDisabledClients } from '../service_tenants/client_metadata.js';
|
|
7
|
+
import { createServiceTenantHooks } from '../service_tenants/provider_hooks.js';
|
|
6
8
|
import { createDeviceSources } from './device_sources.js';
|
|
7
9
|
import { createLogoutSources } from './logout_sources.js';
|
|
8
10
|
import { registrationPolicyMiddleware } from './registration_policy.js';
|
|
@@ -79,21 +81,35 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
|
|
|
79
81
|
return key ? { key, rc: at.resources[key] } : null;
|
|
80
82
|
};
|
|
81
83
|
const mcp = config.mcp;
|
|
82
|
-
|
|
84
|
+
// Service tenants (`serviceTenants`): `client_credentials` clients with a partition,
|
|
85
|
+
// whose tokens are JWTs for a service-tenant resource. See `service_tenants/`.
|
|
86
|
+
const serviceTenants = createServiceTenantHooks({
|
|
87
|
+
serviceTenants: config.serviceTenants ?? { enabled: false, resources: {} },
|
|
88
|
+
AdapterClass: config.AdapterClass,
|
|
89
|
+
audit: config.audit,
|
|
90
|
+
});
|
|
91
|
+
const signAlg = config.jwks.keys[0]?.alg ?? 'RS256';
|
|
92
|
+
const resourceIndicatorFeatures = at.anyJwt || declaredResources.length > 0 || mcp.enabled || serviceTenants.enabled
|
|
83
93
|
? {
|
|
84
94
|
resourceIndicators: {
|
|
85
95
|
enabled: true,
|
|
86
|
-
defaultResource: async (
|
|
96
|
+
defaultResource: async (ctx, client, oneOf) => {
|
|
87
97
|
// Nas trocas (code/refresh/device), o provider passa `oneOf` com as
|
|
88
98
|
// resources já concedidas — devolvemos para não falhar a request.
|
|
89
99
|
if (oneOf)
|
|
90
100
|
return oneOf;
|
|
101
|
+
const serviceResource = await serviceTenants.defaultResource(ctx, client);
|
|
102
|
+
if (serviceResource)
|
|
103
|
+
return serviceResource;
|
|
91
104
|
// Authorize/sem resource explícito: no modo JWT, liga ao resource
|
|
92
105
|
// default (modo simples). Só opaco: sem resource (AT de sempre).
|
|
93
106
|
return at.anyJwt ? at.audience : undefined;
|
|
94
107
|
},
|
|
95
108
|
useGrantedResource: async () => true,
|
|
96
|
-
getResourceServerInfo: (
|
|
109
|
+
getResourceServerInfo: async (ctx, resourceIndicator, client) => {
|
|
110
|
+
const serviceInfo = await serviceTenants.resourceServerInfo(ctx, resourceIndicator, client, signAlg);
|
|
111
|
+
if (serviceInfo)
|
|
112
|
+
return serviceInfo;
|
|
97
113
|
const found = findResource(resourceIndicator);
|
|
98
114
|
const isDefault = at.anyJwt && resourceIndicator === at.audience;
|
|
99
115
|
// Servidores MCP (`mcp`): os declarados e os registrados em runtime.
|
|
@@ -122,7 +138,7 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
|
|
|
122
138
|
info.accessTokenTTL = ttl;
|
|
123
139
|
if (format === 'jwt') {
|
|
124
140
|
// Assina com a chave de assinatura corrente do keystore (mesma do JWKS).
|
|
125
|
-
info.jwt = { sign: { alg:
|
|
141
|
+
info.jwt = { sign: { alg: signAlg } };
|
|
126
142
|
}
|
|
127
143
|
return info;
|
|
128
144
|
},
|
|
@@ -138,13 +154,23 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
|
|
|
138
154
|
// O ator (`act`, RFC 8693) dos access tokens de impersonation — ver
|
|
139
155
|
// `impersonationExtraClaims` em `token_exchange.ts`. Sem isto o token trocado
|
|
140
156
|
// não se distingue de um token do próprio alvo no resource server.
|
|
141
|
-
|
|
157
|
+
// Service tenants add `partition`/`capabilities` (see `service_tenants/`).
|
|
158
|
+
extraTokenClaims: async (ctx, token) => {
|
|
159
|
+
const actor = impersonationExtraClaims(token);
|
|
160
|
+
const tenant = await serviceTenants.extraClaims(ctx, token);
|
|
161
|
+
return actor || tenant ? { ...tenant, ...actor } : undefined;
|
|
162
|
+
},
|
|
142
163
|
// Dispatcher por modelo (suportado pelo oidc-provider: `Adapter` aceita
|
|
143
164
|
// função `(name) => adapter` além de classe). Session-scoped vai pro
|
|
144
165
|
// `SessionAdapterClass`, o resto pro `AdapterClass` — mesma regra de
|
|
145
166
|
// `pickModelAdapterClass`, usada também pelos serviços que instanciam
|
|
146
167
|
// adapter na mão. Sem `session.adapter`, as duas classes são a mesma.
|
|
147
|
-
|
|
168
|
+
// A disabled client (`authkit_disabled`) is "not found" for the provider: every
|
|
169
|
+
// endpoint answers `invalid_client` on the next request.
|
|
170
|
+
adapter: ((name) => {
|
|
171
|
+
const instance = new (pickModelAdapterClass(name, config.AdapterClass, config.SessionAdapterClass))(name);
|
|
172
|
+
return name === 'Client' ? hideDisabledClients(instance) : instance;
|
|
173
|
+
}),
|
|
148
174
|
findAccount: options.findAccount,
|
|
149
175
|
// Grant REAPROVEITADO (consent já lembrado): o org capturado no primeiro
|
|
150
176
|
// consent ficaria VELHO se o usuário trocasse de org no console e voltasse a
|
|
@@ -244,6 +270,7 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
|
|
|
244
270
|
backchannelLogout: { enabled: true },
|
|
245
271
|
revocation: { enabled: true },
|
|
246
272
|
introspection: { enabled: true },
|
|
273
|
+
...(serviceTenants.enabled ? { clientCredentials: { enabled: true } } : {}),
|
|
247
274
|
...registrationFeatures,
|
|
248
275
|
...deviceFlowFeatures,
|
|
249
276
|
...dpopFeatures,
|
|
@@ -279,6 +306,10 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
|
|
|
279
306
|
? sessionTtlHolder.transientSec
|
|
280
307
|
: sessionTtlHolder.rememberSec
|
|
281
308
|
: config.ttl.session,
|
|
309
|
+
// `client_credentials` tokens: the resource's lifetime (a service-tenant resource
|
|
310
|
+
// always sets one), else the access-token TTL.
|
|
311
|
+
ClientCredentials: (_ctx, token) => token?.resourceServer?.accessTokenTTL ??
|
|
312
|
+
(tokenTtlHolder ? tokenTtlHolder.accessTokenSec : config.ttl.accessToken),
|
|
282
313
|
Interaction: 3600,
|
|
283
314
|
Grant: tokenTtlHolder
|
|
284
315
|
? () => tokenTtlHolder.refreshTokenSec
|
|
@@ -28,7 +28,7 @@ export declare class OidcService {
|
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
30
30
|
* @internal Segredo derivado da app key para um uso (`label`) — HMAC dos nonces
|
|
31
|
-
* DPoP
|
|
31
|
+
* DPoP de uma extension de personal agents, por exemplo. Igual em todas as instâncias, sem expor a chave.
|
|
32
32
|
*/
|
|
33
33
|
deriveSecret(label: string): Buffer;
|
|
34
34
|
/** Pathname do issuer sem barra final (ex.: `/oidc`). Vazio quando montado na raiz. */
|
|
@@ -44,7 +44,7 @@ export class OidcService {
|
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* @internal Segredo derivado da app key para um uso (`label`) — HMAC dos nonces
|
|
47
|
-
* DPoP
|
|
47
|
+
* DPoP de uma extension de personal agents, por exemplo. Igual em todas as instâncias, sem expor a chave.
|
|
48
48
|
*/
|
|
49
49
|
deriveSecret(label) {
|
|
50
50
|
return createHmac('sha256', this.#appKey).update(`authkit:${label}`).digest();
|
|
@@ -23,9 +23,24 @@
|
|
|
23
23
|
* }
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
+
import type { AuthkitTable } from '../agents/extension.js';
|
|
26
27
|
export interface EnsureSchemaOptions {
|
|
27
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Conexão Lucid das tabelas do authkit (`schema.connection`). Default: conexão
|
|
30
|
+
* primária. Ignorada quando `db` já é um client (o `this.db` de uma migration).
|
|
31
|
+
*/
|
|
28
32
|
connection?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Conexão Lucid do model da conta, onde vive a tabela da conta (a coluna
|
|
35
|
+
* `login_methods`). Default: a conexão primária — NÃO a das tabelas do authkit.
|
|
36
|
+
* O provider passa a do `accountStore` no boot. Ignorada quando `db` já é um client.
|
|
37
|
+
*/
|
|
38
|
+
accountConnection?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Tabelas de `personalAgents.extensions` (`tables` de cada uma), garantidas junto. O provider
|
|
41
|
+
* passa as do config no boot.
|
|
42
|
+
*/
|
|
43
|
+
tables?: readonly AuthkitTable[];
|
|
29
44
|
/**
|
|
30
45
|
* Tabela da conta, onde a coluna opcional `login_methods` pertence. Vem do
|
|
31
46
|
* metadado `accountStore.accountTable` (o provider repassa no boot). Undefined
|
|
@@ -51,6 +66,14 @@ export interface EnsureSchemaReport {
|
|
|
51
66
|
ensured: boolean;
|
|
52
67
|
};
|
|
53
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Garante tabelas lib-owned no formato {@link AuthkitTable} — o que `ensureAuthkitSchema` faz com as
|
|
71
|
+
* do authkit: cria as que faltam, adiciona colunas e índices novos, nunca dropa nem altera. Para
|
|
72
|
+
* uma extension expor o próprio `ensure*Schema()` para migrations do host. Estável no 1.x.
|
|
73
|
+
*/
|
|
74
|
+
export declare function ensureAuthkitTables(db: any, tables: readonly AuthkitTable[], options?: {
|
|
75
|
+
connection?: string;
|
|
76
|
+
}): Promise<Pick<EnsureSchemaReport, 'created' | 'altered' | 'indexed'>>;
|
|
54
77
|
/**
|
|
55
78
|
* Garante que as tabelas do authkit existem e têm todas as colunas que esta
|
|
56
79
|
* versão da lib espera. Aditivo e idempotente — seguro de rodar em todo boot
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* }
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
+
import { authkitTablesClient } from './tables_location.js';
|
|
26
27
|
/** Busca por conta (`account_id`) e limpeza dos expirados (`expires_at`). */
|
|
27
28
|
const OIDC_PAYLOAD_INDEXES = [
|
|
28
29
|
{ name: 'authkit_oidc_payloads_account_idx', columns: ['model_name', 'account_id'] },
|
|
@@ -309,7 +310,7 @@ const TABLES = [
|
|
|
309
310
|
name: 'auth_agent_registry',
|
|
310
311
|
/**
|
|
311
312
|
* Agentes liberados (`allowed`) ou bloqueados (`blocked`) pelo console admin,
|
|
312
|
-
* somados ao `defineConfig` — PACT pelo `iss
|
|
313
|
+
* somados ao `defineConfig` — PACT pelo `iss`; uma extension pelo identificador dela.
|
|
313
314
|
*/
|
|
314
315
|
create: (t) => {
|
|
315
316
|
t.string('id').primary();
|
|
@@ -333,135 +334,6 @@ const TABLES = [
|
|
|
333
334
|
updated_at: (t) => t.dateTime('updated_at', { useTz: true, precision: 3 }).nullable(),
|
|
334
335
|
},
|
|
335
336
|
},
|
|
336
|
-
/*
|
|
337
|
-
* Personal Agent Protocol (Poppy). Os refresh tokens são grants de
|
|
338
|
-
* `auth_agent_grants` (prefixo `pgrant_`) com o hash em
|
|
339
|
-
* `auth_agent_refresh_tokens`; aqui ficam Sessions, access tokens, pedidos de
|
|
340
|
-
* sign-in e os `jti` vistos. Mesmas regras de data das tabelas acima.
|
|
341
|
-
*/
|
|
342
|
-
{
|
|
343
|
-
name: 'auth_poppy_sessions',
|
|
344
|
-
/**
|
|
345
|
-
* Uma Session: um usuário do agente (`user_id`, opaco) num agente (`client_id`), com o
|
|
346
|
-
* `agent_id` opcional. `account_id` é a conta que já usou. `grant_id` e `scope` ficaram da
|
|
347
|
-
* Draft 0.1, sem uso (o schema só é aditivo).
|
|
348
|
-
*/
|
|
349
|
-
create: (t) => {
|
|
350
|
-
t.string('id').primary();
|
|
351
|
-
t.string('client_id', 2048).notNullable();
|
|
352
|
-
t.string('user_id').notNullable();
|
|
353
|
-
t.string('agent_id').nullable();
|
|
354
|
-
t.string('account_id').nullable().index();
|
|
355
|
-
t.string('grant_id').nullable().index();
|
|
356
|
-
t.text('scope').notNullable();
|
|
357
|
-
t.dateTime('expires_at', { useTz: true, precision: 3 }).notNullable();
|
|
358
|
-
t.dateTime('ended_at', { useTz: true, precision: 3 }).nullable();
|
|
359
|
-
t.dateTime('created_at', { useTz: true, precision: 3 }).notNullable();
|
|
360
|
-
t.dateTime('updated_at', { useTz: true, precision: 3 }).notNullable();
|
|
361
|
-
},
|
|
362
|
-
columns: {
|
|
363
|
-
client_id: (t) => t.string('client_id', 2048),
|
|
364
|
-
user_id: (t) => t.string('user_id'),
|
|
365
|
-
agent_id: (t) => t.string('agent_id').nullable(),
|
|
366
|
-
account_id: (t) => t.string('account_id').nullable().index(),
|
|
367
|
-
grant_id: (t) => t.string('grant_id').nullable().index(),
|
|
368
|
-
scope: (t) => t.text('scope'),
|
|
369
|
-
expires_at: (t) => t.dateTime('expires_at', { useTz: true, precision: 3 }).nullable(),
|
|
370
|
-
ended_at: (t) => t.dateTime('ended_at', { useTz: true, precision: 3 }).nullable(),
|
|
371
|
-
created_at: (t) => t.dateTime('created_at', { useTz: true, precision: 3 }).nullable(),
|
|
372
|
-
updated_at: (t) => t.dateTime('updated_at', { useTz: true, precision: 3 }).nullable(),
|
|
373
|
-
},
|
|
374
|
-
},
|
|
375
|
-
{
|
|
376
|
-
name: 'auth_poppy_tokens',
|
|
377
|
-
/**
|
|
378
|
-
* Access tokens — só o hash. `jkt` = chave DPoP; `null` = Bearer. `session_id` e `user_id`
|
|
379
|
-
* ficaram da Draft 0.1 e saem vazios: o sign-in não conhece a Session.
|
|
380
|
-
*/
|
|
381
|
-
create: (t) => {
|
|
382
|
-
t.string('token_hash', 64).primary();
|
|
383
|
-
t.string('session_id').notNullable().index();
|
|
384
|
-
t.string('client_id', 2048).notNullable();
|
|
385
|
-
t.string('user_id').notNullable();
|
|
386
|
-
t.string('account_id').nullable();
|
|
387
|
-
t.string('grant_id').nullable().index();
|
|
388
|
-
t.text('scope').notNullable();
|
|
389
|
-
t.string('resource', 2048).nullable();
|
|
390
|
-
t.string('jkt', 64).nullable();
|
|
391
|
-
t.dateTime('expires_at', { useTz: true, precision: 3 }).notNullable().index();
|
|
392
|
-
t.dateTime('created_at', { useTz: true, precision: 3 }).notNullable();
|
|
393
|
-
},
|
|
394
|
-
columns: {
|
|
395
|
-
session_id: (t) => t.string('session_id').index(),
|
|
396
|
-
client_id: (t) => t.string('client_id', 2048),
|
|
397
|
-
user_id: (t) => t.string('user_id'),
|
|
398
|
-
account_id: (t) => t.string('account_id').nullable(),
|
|
399
|
-
grant_id: (t) => t.string('grant_id').nullable().index(),
|
|
400
|
-
scope: (t) => t.text('scope'),
|
|
401
|
-
resource: (t) => t.string('resource', 2048).nullable(),
|
|
402
|
-
jkt: (t) => t.string('jkt', 64).nullable(),
|
|
403
|
-
expires_at: (t) => t.dateTime('expires_at', { useTz: true, precision: 3 }).nullable(),
|
|
404
|
-
created_at: (t) => t.dateTime('created_at', { useTz: true, precision: 3 }).nullable(),
|
|
405
|
-
},
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
name: 'auth_poppy_requests',
|
|
409
|
-
/**
|
|
410
|
-
* Pedidos: `authorize` (Direct — vira código após o consentimento), `device` (RFC 8628),
|
|
411
|
-
* `mediated` (aguardando o código de uso único) e `browser` (entrada de um navegador, §6).
|
|
412
|
-
* Códigos só como hash. Linhas expiradas são apagadas no próximo pedido.
|
|
413
|
-
*/
|
|
414
|
-
create: (t) => {
|
|
415
|
-
t.string('id').primary();
|
|
416
|
-
t.string('kind', 16).notNullable();
|
|
417
|
-
t.string('code_hash', 64).nullable().unique();
|
|
418
|
-
t.string('user_code', 16).nullable().unique();
|
|
419
|
-
t.string('client_id', 2048).notNullable();
|
|
420
|
-
t.string('session_id').nullable();
|
|
421
|
-
t.string('account_id').nullable();
|
|
422
|
-
t.text('requested_scope').notNullable();
|
|
423
|
-
t.text('granted_scope').nullable();
|
|
424
|
-
t.string('redirect_uri', 2048).nullable();
|
|
425
|
-
t.string('code_challenge', 128).nullable();
|
|
426
|
-
t.string('status', 16).notNullable();
|
|
427
|
-
t.integer('attempts').notNullable().defaultTo(0);
|
|
428
|
-
t.integer('interval_seconds').nullable();
|
|
429
|
-
t.dateTime('last_polled_at', { useTz: true, precision: 3 }).nullable();
|
|
430
|
-
t.text('data').nullable();
|
|
431
|
-
t.dateTime('expires_at', { useTz: true, precision: 3 }).notNullable().index();
|
|
432
|
-
t.dateTime('created_at', { useTz: true, precision: 3 }).notNullable();
|
|
433
|
-
},
|
|
434
|
-
columns: {
|
|
435
|
-
kind: (t) => t.string('kind', 16),
|
|
436
|
-
code_hash: (t) => t.string('code_hash', 64).nullable(),
|
|
437
|
-
user_code: (t) => t.string('user_code', 16).nullable(),
|
|
438
|
-
client_id: (t) => t.string('client_id', 2048),
|
|
439
|
-
session_id: (t) => t.string('session_id').nullable(),
|
|
440
|
-
account_id: (t) => t.string('account_id').nullable(),
|
|
441
|
-
requested_scope: (t) => t.text('requested_scope'),
|
|
442
|
-
granted_scope: (t) => t.text('granted_scope').nullable(),
|
|
443
|
-
redirect_uri: (t) => t.string('redirect_uri', 2048).nullable(),
|
|
444
|
-
code_challenge: (t) => t.string('code_challenge', 128).nullable(),
|
|
445
|
-
status: (t) => t.string('status', 16),
|
|
446
|
-
attempts: (t) => t.integer('attempts').defaultTo(0),
|
|
447
|
-
interval_seconds: (t) => t.integer('interval_seconds').nullable(),
|
|
448
|
-
last_polled_at: (t) => t.dateTime('last_polled_at', { useTz: true, precision: 3 }).nullable(),
|
|
449
|
-
data: (t) => t.text('data').nullable(),
|
|
450
|
-
expires_at: (t) => t.dateTime('expires_at', { useTz: true, precision: 3 }).nullable(),
|
|
451
|
-
created_at: (t) => t.dateTime('created_at', { useTz: true, precision: 3 }).nullable(),
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
{
|
|
455
|
-
name: 'auth_poppy_jtis',
|
|
456
|
-
/** `jti` já aceitos (client assertions, Session JWTs, provas DPoP), até expirarem — anti-replay entre instâncias. */
|
|
457
|
-
create: (t) => {
|
|
458
|
-
t.string('key', 100).primary();
|
|
459
|
-
t.dateTime('expires_at', { useTz: true, precision: 3 }).notNullable().index();
|
|
460
|
-
},
|
|
461
|
-
columns: {
|
|
462
|
-
expires_at: (t) => t.dateTime('expires_at', { useTz: true, precision: 3 }).nullable(),
|
|
463
|
-
},
|
|
464
|
-
},
|
|
465
337
|
];
|
|
466
338
|
/** Linhas de um `rawQuery`, no formato de cada driver (pg, mysql2, sqlite). */
|
|
467
339
|
function catalogRows(result) {
|
|
@@ -586,26 +458,9 @@ async function ensureIndex(conn, table, index) {
|
|
|
586
458
|
throw error;
|
|
587
459
|
}
|
|
588
460
|
}
|
|
589
|
-
/**
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
* e dentro de migrations.
|
|
593
|
-
*
|
|
594
|
-
* `db` é o manager (`Database`, no boot) ou o client da própria migration
|
|
595
|
-
* (`this.db`, um `QueryClient`/`TransactionClient`, usado como está).
|
|
596
|
-
*/
|
|
597
|
-
export async function ensureAuthkitSchema(db, options = {}) {
|
|
598
|
-
const conn = typeof db.connection !== 'function'
|
|
599
|
-
? db
|
|
600
|
-
: options.connection
|
|
601
|
-
? db.connection(options.connection)
|
|
602
|
-
: db.connection();
|
|
603
|
-
const report = {
|
|
604
|
-
created: [],
|
|
605
|
-
altered: {},
|
|
606
|
-
loginMethods: { table: options.accountTable ?? 'users', ensured: false },
|
|
607
|
-
};
|
|
608
|
-
for (const def of TABLES) {
|
|
461
|
+
/** Cria/completa as tabelas (o miolo de {@link ensureAuthkitSchema}). */
|
|
462
|
+
async function ensureTables(conn, tables, report) {
|
|
463
|
+
for (const def of tables) {
|
|
609
464
|
if (!(await tableExists(conn, def.name))) {
|
|
610
465
|
try {
|
|
611
466
|
await conn.schema.createTable(def.name, def.create);
|
|
@@ -639,6 +494,37 @@ export async function ensureAuthkitSchema(db, options = {}) {
|
|
|
639
494
|
report.indexed[def.name].push(index.name);
|
|
640
495
|
}
|
|
641
496
|
}
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Garante tabelas lib-owned no formato {@link AuthkitTable} — o que `ensureAuthkitSchema` faz com as
|
|
500
|
+
* do authkit: cria as que faltam, adiciona colunas e índices novos, nunca dropa nem altera. Para
|
|
501
|
+
* uma extension expor o próprio `ensure*Schema()` para migrations do host. Estável no 1.x.
|
|
502
|
+
*/
|
|
503
|
+
export async function ensureAuthkitTables(db, tables, options = {}) {
|
|
504
|
+
const conn = authkitTablesClient(db, { connection: options.connection });
|
|
505
|
+
const report = {
|
|
506
|
+
created: [],
|
|
507
|
+
altered: {},
|
|
508
|
+
};
|
|
509
|
+
await ensureTables(conn, tables, report);
|
|
510
|
+
return report;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Garante que as tabelas do authkit existem e têm todas as colunas que esta
|
|
514
|
+
* versão da lib espera. Aditivo e idempotente — seguro de rodar em todo boot
|
|
515
|
+
* e dentro de migrations.
|
|
516
|
+
*
|
|
517
|
+
* `db` é o manager (`Database`, no boot) ou o client da própria migration
|
|
518
|
+
* (`this.db`, um `QueryClient`/`TransactionClient`, usado como está).
|
|
519
|
+
*/
|
|
520
|
+
export async function ensureAuthkitSchema(db, options = {}) {
|
|
521
|
+
const conn = authkitTablesClient(db, { connection: options.connection });
|
|
522
|
+
const report = {
|
|
523
|
+
created: [],
|
|
524
|
+
altered: {},
|
|
525
|
+
loginMethods: { table: options.accountTable ?? 'users', ensured: false },
|
|
526
|
+
};
|
|
527
|
+
await ensureTables(conn, [...TABLES, ...(options.tables ?? [])], report);
|
|
642
528
|
// Coluna `login_methods` na tabela da CONTA — a tabela é HOST-owned (não está
|
|
643
529
|
// em TABLES porque o nome/shape são decisão do app), mas a LIB consome a
|
|
644
530
|
// coluna (LoginMethodsPreferenceCapability). Alguns deploys de host só migram
|
|
@@ -654,11 +540,15 @@ export async function ensureAuthkitSchema(db, options = {}) {
|
|
|
654
540
|
// `users` fixo: com a conta em `auth_users` (o nome que o próprio scaffold da
|
|
655
541
|
// lib usa), o `users` fixo acertava uma tabela qualquer de mesmo nome e a
|
|
656
542
|
// coluna ia parar no lugar errado sem erro nenhum.
|
|
543
|
+
//
|
|
544
|
+
// A tabela da conta fica na conexão do MODEL da conta, não na das tabelas do
|
|
545
|
+
// authkit (`schema.connection`).
|
|
657
546
|
const accountTable = options.accountTable ?? 'users';
|
|
658
|
-
|
|
547
|
+
const accountConn = authkitTablesClient(db, { connection: options.accountConnection });
|
|
548
|
+
if (await tableExists(accountConn, accountTable)) {
|
|
659
549
|
try {
|
|
660
|
-
if (!(await columnExists(
|
|
661
|
-
await
|
|
550
|
+
if (!(await columnExists(accountConn, accountTable, 'login_methods'))) {
|
|
551
|
+
await accountConn.schema.alterTable(accountTable, (t) => {
|
|
662
552
|
t.jsonb('login_methods').nullable();
|
|
663
553
|
});
|
|
664
554
|
report.altered[accountTable] = ['login_methods'];
|
|
@@ -675,7 +565,7 @@ export async function ensureAuthkitSchema(db, options = {}) {
|
|
|
675
565
|
* o boot não avisava nada — o silêncio que este bloco existe para acabar.
|
|
676
566
|
* (Mesma semântica do probe de `createTable` logo acima.)
|
|
677
567
|
*/
|
|
678
|
-
if (await columnExists(
|
|
568
|
+
if (await columnExists(accountConn, accountTable, 'login_methods')) {
|
|
679
569
|
report.loginMethods.ensured = true;
|
|
680
570
|
}
|
|
681
571
|
else {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where AuthKit's OWN tables live (`auth_settings`, `auth_mfa`, `auth_password_history`,
|
|
3
|
+
* the organization tables, `authkit_oidc_payloads`, the personal-agent tables, the
|
|
4
|
+
* `authkit_keystore` of the Lucid keystore, and the tables of agent extensions).
|
|
5
|
+
*
|
|
6
|
+
* The rule: every lib-owned table goes through `schema.connection` of `defineConfig`
|
|
7
|
+
* (default: the primary Lucid connection). The app's account table is not lib-owned: it
|
|
8
|
+
* stays on the account model's connection (`static connection` of the model).
|
|
9
|
+
*/
|
|
10
|
+
export interface AuthkitTablesLocation {
|
|
11
|
+
/** Lucid connection name. `undefined` = the primary connection. */
|
|
12
|
+
connection?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The query client for AuthKit's tables. `db` is the Lucid manager (`Database`) or a
|
|
16
|
+
* client the caller already holds (a migration's `this.db`, a transaction), which is
|
|
17
|
+
* used as is.
|
|
18
|
+
*/
|
|
19
|
+
export declare function authkitTablesClient(db: any, location?: AuthkitTablesLocation): any;
|
|
20
|
+
/**
|
|
21
|
+
* Hidden hook a store built by `lucidAccountStore` exposes so `defineConfig` can tell it
|
|
22
|
+
* where AuthKit's tables live (the store is built before `defineConfig` sees the `schema`
|
|
23
|
+
* option). Not part of the `AccountStore` contract.
|
|
24
|
+
*/
|
|
25
|
+
export declare const BIND_TABLES_LOCATION: unique symbol;
|
|
26
|
+
/** Hands `location` to a store that accepts it (see {@link BIND_TABLES_LOCATION}). */
|
|
27
|
+
export declare function bindTablesLocation(store: unknown, location: AuthkitTablesLocation): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The query client for AuthKit's tables. `db` is the Lucid manager (`Database`) or a
|
|
3
|
+
* client the caller already holds (a migration's `this.db`, a transaction), which is
|
|
4
|
+
* used as is.
|
|
5
|
+
*/
|
|
6
|
+
export function authkitTablesClient(db, location = {}) {
|
|
7
|
+
if (typeof db?.connection !== 'function')
|
|
8
|
+
return db;
|
|
9
|
+
return location.connection ? db.connection(location.connection) : db.connection();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Hidden hook a store built by `lucidAccountStore` exposes so `defineConfig` can tell it
|
|
13
|
+
* where AuthKit's tables live (the store is built before `defineConfig` sees the `schema`
|
|
14
|
+
* option). Not part of the `AccountStore` contract.
|
|
15
|
+
*/
|
|
16
|
+
export const BIND_TABLES_LOCATION = Symbol.for('authkit.bindTablesLocation');
|
|
17
|
+
/** Hands `location` to a store that accepts it (see {@link BIND_TABLES_LOCATION}). */
|
|
18
|
+
export function bindTablesLocation(store, location) {
|
|
19
|
+
const bind = store?.[BIND_TABLES_LOCATION];
|
|
20
|
+
if (typeof bind === 'function')
|
|
21
|
+
bind(location);
|
|
22
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { OidcAdapter } from '../adapters/adapter_contract.js';
|
|
2
|
+
/**
|
|
3
|
+
* authkit-owned keys in a persisted `Client` payload. oidc-provider strips unknown
|
|
4
|
+
* metadata when it builds a `Client` (and when dynamic registration persists one), so
|
|
5
|
+
* these keys can only be written by authkit itself (console, Admin API, CLI) and are
|
|
6
|
+
* read straight from the adapter.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SERVICE_TENANT_KEY = "authkit_service_tenant";
|
|
9
|
+
export declare const CLIENT_DISABLED_KEY = "authkit_disabled";
|
|
10
|
+
/** The service-tenant part of a client. */
|
|
11
|
+
export interface ServiceTenantInfo {
|
|
12
|
+
/** Partition (tenant) the client acts as. Emitted as the `partition` claim. */
|
|
13
|
+
partition: string;
|
|
14
|
+
/** Granted capabilities. Emitted as the `capabilities` claim when non-empty. */
|
|
15
|
+
capabilities: string[];
|
|
16
|
+
}
|
|
17
|
+
/** Letters, digits, `.`, `_`, `:` and `-`; starts with a letter or digit; up to 128 chars. */
|
|
18
|
+
export declare const PARTITION_PATTERN: RegExp;
|
|
19
|
+
/** Like {@link PARTITION_PATTERN}, plus `*` and `/`. */
|
|
20
|
+
export declare const CAPABILITY_PATTERN: RegExp;
|
|
21
|
+
export declare const MAX_CAPABILITIES = 64;
|
|
22
|
+
/** Reads the service-tenant metadata from a raw client payload, or `null`. */
|
|
23
|
+
export declare function serviceTenantOf(payload: Record<string, unknown> | undefined | null): ServiceTenantInfo | null;
|
|
24
|
+
/** `true` when the raw client payload is disabled. */
|
|
25
|
+
export declare function isClientDisabled(payload: Record<string, unknown> | undefined | null): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Validates and normalizes service-tenant input. Returns an error message or the
|
|
28
|
+
* normalized value (trimmed, de-duplicated capabilities).
|
|
29
|
+
*/
|
|
30
|
+
export declare function normalizeServiceTenant(input: {
|
|
31
|
+
partition: string;
|
|
32
|
+
capabilities?: string[];
|
|
33
|
+
}): ServiceTenantInfo | string;
|
|
34
|
+
/**
|
|
35
|
+
* Wraps the provider's `Client` adapter so a disabled client is "not found": every
|
|
36
|
+
* endpoint answers `invalid_client` from the next request on, on every instance that
|
|
37
|
+
* shares the adapter. Writes are untouched.
|
|
38
|
+
*/
|
|
39
|
+
export declare function hideDisabledClients<T extends OidcAdapter>(adapter: T): T;
|