@antasphere/chassis-server 1.0.0
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 +82 -0
- package/dist/accounts/deletion.d.ts +163 -0
- package/dist/accounts/deletion.d.ts.map +1 -0
- package/dist/accounts/deletion.js +360 -0
- package/dist/accounts/deletion.js.map +1 -0
- package/dist/accounts/erasure-log.d.ts +66 -0
- package/dist/accounts/erasure-log.d.ts.map +1 -0
- package/dist/accounts/erasure-log.js +117 -0
- package/dist/accounts/erasure-log.js.map +1 -0
- package/dist/accounts/index.d.ts +6 -0
- package/dist/accounts/index.d.ts.map +1 -0
- package/dist/accounts/index.js +6 -0
- package/dist/accounts/index.js.map +1 -0
- package/dist/accounts/mint-refusal.d.ts +47 -0
- package/dist/accounts/mint-refusal.d.ts.map +1 -0
- package/dist/accounts/mint-refusal.js +59 -0
- package/dist/accounts/mint-refusal.js.map +1 -0
- package/dist/accounts/signup-duplicate.d.ts +14 -0
- package/dist/accounts/signup-duplicate.d.ts.map +1 -0
- package/dist/accounts/signup-duplicate.js +25 -0
- package/dist/accounts/signup-duplicate.js.map +1 -0
- package/dist/accounts/superadmin.d.ts +27 -0
- package/dist/accounts/superadmin.d.ts.map +1 -0
- package/dist/accounts/superadmin.js +46 -0
- package/dist/accounts/superadmin.js.map +1 -0
- package/dist/api/apikeys.d.ts +14 -0
- package/dist/api/apikeys.d.ts.map +1 -0
- package/dist/api/apikeys.js +124 -0
- package/dist/api/apikeys.js.map +1 -0
- package/dist/api/audit.d.ts +4 -0
- package/dist/api/audit.d.ts.map +1 -0
- package/dist/api/audit.js +75 -0
- package/dist/api/audit.js.map +1 -0
- package/dist/api/break-glass.d.ts +48 -0
- package/dist/api/break-glass.d.ts.map +1 -0
- package/dist/api/break-glass.js +217 -0
- package/dist/api/break-glass.js.map +1 -0
- package/dist/api/cli-auth.d.ts +84 -0
- package/dist/api/cli-auth.d.ts.map +1 -0
- package/dist/api/cli-auth.js +177 -0
- package/dist/api/cli-auth.js.map +1 -0
- package/dist/api/create-api.d.ts +123 -0
- package/dist/api/create-api.d.ts.map +1 -0
- package/dist/api/create-api.js +1165 -0
- package/dist/api/create-api.js.map +1 -0
- package/dist/api/demo-passes.d.ts +45 -0
- package/dist/api/demo-passes.d.ts.map +1 -0
- package/dist/api/demo-passes.js +143 -0
- package/dist/api/demo-passes.js.map +1 -0
- package/dist/api/errors.d.ts +5 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +5 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/export.d.ts +63 -0
- package/dist/api/export.d.ts.map +1 -0
- package/dist/api/export.js +393 -0
- package/dist/api/export.js.map +1 -0
- package/dist/api/files.d.ts +40 -0
- package/dist/api/files.d.ts.map +1 -0
- package/dist/api/files.js +150 -0
- package/dist/api/files.js.map +1 -0
- package/dist/api/index.d.ts +17 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +17 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/invitations.d.ts +50 -0
- package/dist/api/invitations.d.ts.map +1 -0
- package/dist/api/invitations.js +313 -0
- package/dist/api/invitations.js.map +1 -0
- package/dist/api/members.d.ts +25 -0
- package/dist/api/members.d.ts.map +1 -0
- package/dist/api/members.js +522 -0
- package/dist/api/members.js.map +1 -0
- package/dist/api/onboarding.d.ts +26 -0
- package/dist/api/onboarding.d.ts.map +1 -0
- package/dist/api/onboarding.js +23 -0
- package/dist/api/onboarding.js.map +1 -0
- package/dist/api/openapi-doc.d.ts +21 -0
- package/dist/api/openapi-doc.d.ts.map +1 -0
- package/dist/api/openapi-doc.js +25 -0
- package/dist/api/openapi-doc.js.map +1 -0
- package/dist/api/projects.d.ts +24 -0
- package/dist/api/projects.d.ts.map +1 -0
- package/dist/api/projects.js +624 -0
- package/dist/api/projects.js.map +1 -0
- package/dist/api/scope-routes.d.ts +12 -0
- package/dist/api/scope-routes.d.ts.map +1 -0
- package/dist/api/scope-routes.js +2 -0
- package/dist/api/scope-routes.js.map +1 -0
- package/dist/api/sso-connect.d.ts +70 -0
- package/dist/api/sso-connect.d.ts.map +1 -0
- package/dist/api/sso-connect.js +164 -0
- package/dist/api/sso-connect.js.map +1 -0
- package/dist/api/sso-logout.d.ts +41 -0
- package/dist/api/sso-logout.d.ts.map +1 -0
- package/dist/api/sso-logout.js +34 -0
- package/dist/api/sso-logout.js.map +1 -0
- package/dist/api/teams.d.ts +43 -0
- package/dist/api/teams.d.ts.map +1 -0
- package/dist/api/teams.js +447 -0
- package/dist/api/teams.js.map +1 -0
- package/dist/api/workspaces.d.ts +114 -0
- package/dist/api/workspaces.d.ts.map +1 -0
- package/dist/api/workspaces.js +462 -0
- package/dist/api/workspaces.js.map +1 -0
- package/dist/apikeys/index.d.ts +3 -0
- package/dist/apikeys/index.d.ts.map +1 -0
- package/dist/apikeys/index.js +3 -0
- package/dist/apikeys/index.js.map +1 -0
- package/dist/apikeys/peppers.d.ts +53 -0
- package/dist/apikeys/peppers.d.ts.map +1 -0
- package/dist/apikeys/peppers.js +97 -0
- package/dist/apikeys/peppers.js.map +1 -0
- package/dist/apikeys/service.d.ts +56 -0
- package/dist/apikeys/service.d.ts.map +1 -0
- package/dist/apikeys/service.js +196 -0
- package/dist/apikeys/service.js.map +1 -0
- package/dist/app.d.ts +65 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +142 -0
- package/dist/app.js.map +1 -0
- package/dist/audit/filters.d.ts +38 -0
- package/dist/audit/filters.d.ts.map +1 -0
- package/dist/audit/filters.js +97 -0
- package/dist/audit/filters.js.map +1 -0
- package/dist/audit/index.d.ts +3 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +3 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/service.d.ts +65 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +103 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/boot.d.ts +8 -0
- package/dist/boot.d.ts.map +1 -0
- package/dist/boot.js +822 -0
- package/dist/boot.js.map +1 -0
- package/dist/constant-time.d.ts +3 -0
- package/dist/constant-time.d.ts.map +1 -0
- package/dist/constant-time.js +10 -0
- package/dist/constant-time.js.map +1 -0
- package/dist/email/driver.d.ts +16 -0
- package/dist/email/driver.d.ts.map +1 -0
- package/dist/email/driver.js +136 -0
- package/dist/email/driver.js.map +1 -0
- package/dist/email/index.d.ts +4 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +4 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/shell.d.ts +72 -0
- package/dist/email/shell.d.ts.map +1 -0
- package/dist/email/shell.js +146 -0
- package/dist/email/shell.js.map +1 -0
- package/dist/email/templates.d.ts +83 -0
- package/dist/email/templates.d.ts.map +1 -0
- package/dist/email/templates.js +96 -0
- package/dist/email/templates.js.map +1 -0
- package/dist/entitlements/check.d.ts +140 -0
- package/dist/entitlements/check.d.ts.map +1 -0
- package/dist/entitlements/check.js +276 -0
- package/dist/entitlements/check.js.map +1 -0
- package/dist/entitlements/clock-skew.d.ts +27 -0
- package/dist/entitlements/clock-skew.d.ts.map +1 -0
- package/dist/entitlements/clock-skew.js +58 -0
- package/dist/entitlements/clock-skew.js.map +1 -0
- package/dist/entitlements/gate.d.ts +113 -0
- package/dist/entitlements/gate.d.ts.map +1 -0
- package/dist/entitlements/gate.js +430 -0
- package/dist/entitlements/gate.js.map +1 -0
- package/dist/entitlements/hub-machine-token.d.ts +83 -0
- package/dist/entitlements/hub-machine-token.d.ts.map +1 -0
- package/dist/entitlements/hub-machine-token.js +175 -0
- package/dist/entitlements/hub-machine-token.js.map +1 -0
- package/dist/entitlements/hub-subject.d.ts +32 -0
- package/dist/entitlements/hub-subject.d.ts.map +1 -0
- package/dist/entitlements/hub-subject.js +68 -0
- package/dist/entitlements/hub-subject.js.map +1 -0
- package/dist/entitlements/index.d.ts +9 -0
- package/dist/entitlements/index.d.ts.map +1 -0
- package/dist/entitlements/index.js +9 -0
- package/dist/entitlements/index.js.map +1 -0
- package/dist/entitlements/poster.d.ts +81 -0
- package/dist/entitlements/poster.d.ts.map +1 -0
- package/dist/entitlements/poster.js +169 -0
- package/dist/entitlements/poster.js.map +1 -0
- package/dist/entitlements/profiles.d.ts +121 -0
- package/dist/entitlements/profiles.d.ts.map +1 -0
- package/dist/entitlements/profiles.js +212 -0
- package/dist/entitlements/profiles.js.map +1 -0
- package/dist/entitlements/slot.d.ts +33 -0
- package/dist/entitlements/slot.d.ts.map +1 -0
- package/dist/entitlements/slot.js +75 -0
- package/dist/entitlements/slot.js.map +1 -0
- package/dist/env.d.ts +293 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +383 -0
- package/dist/env.js.map +1 -0
- package/dist/files/http.d.ts +23 -0
- package/dist/files/http.d.ts.map +1 -0
- package/dist/files/http.js +66 -0
- package/dist/files/http.js.map +1 -0
- package/dist/files/index.d.ts +5 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +5 -0
- package/dist/files/index.js.map +1 -0
- package/dist/files/serve.d.ts +43 -0
- package/dist/files/serve.d.ts.map +1 -0
- package/dist/files/serve.js +71 -0
- package/dist/files/serve.js.map +1 -0
- package/dist/files/service.d.ts +69 -0
- package/dist/files/service.d.ts.map +1 -0
- package/dist/files/service.js +188 -0
- package/dist/files/service.js.map +1 -0
- package/dist/files/spool.d.ts +21 -0
- package/dist/files/spool.d.ts.map +1 -0
- package/dist/files/spool.js +47 -0
- package/dist/files/spool.js.map +1 -0
- package/dist/identity/better-auth.d.ts +3927 -0
- package/dist/identity/better-auth.d.ts.map +1 -0
- package/dist/identity/better-auth.js +896 -0
- package/dist/identity/better-auth.js.map +1 -0
- package/dist/identity/demo-pass-plugin.d.ts +29 -0
- package/dist/identity/demo-pass-plugin.d.ts.map +1 -0
- package/dist/identity/demo-pass-plugin.js +88 -0
- package/dist/identity/demo-pass-plugin.js.map +1 -0
- package/dist/identity/demo-pass-rules.d.ts +84 -0
- package/dist/identity/demo-pass-rules.d.ts.map +1 -0
- package/dist/identity/demo-pass-rules.js +223 -0
- package/dist/identity/demo-pass-rules.js.map +1 -0
- package/dist/identity/demo-pass.d.ts +159 -0
- package/dist/identity/demo-pass.d.ts.map +1 -0
- package/dist/identity/demo-pass.js +319 -0
- package/dist/identity/demo-pass.js.map +1 -0
- package/dist/identity/hub-grant.d.ts +182 -0
- package/dist/identity/hub-grant.d.ts.map +1 -0
- package/dist/identity/hub-grant.js +594 -0
- package/dist/identity/hub-grant.js.map +1 -0
- package/dist/identity/hub-jwt.d.ts +22 -0
- package/dist/identity/hub-jwt.d.ts.map +1 -0
- package/dist/identity/hub-jwt.js +77 -0
- package/dist/identity/hub-jwt.js.map +1 -0
- package/dist/identity/hub-live-gate.d.ts +17 -0
- package/dist/identity/hub-live-gate.d.ts.map +1 -0
- package/dist/identity/hub-live-gate.js +146 -0
- package/dist/identity/hub-live-gate.js.map +1 -0
- package/dist/identity/hub-logout.d.ts +88 -0
- package/dist/identity/hub-logout.d.ts.map +1 -0
- package/dist/identity/hub-logout.js +158 -0
- package/dist/identity/hub-logout.js.map +1 -0
- package/dist/identity/hub-projection.d.ts +103 -0
- package/dist/identity/hub-projection.d.ts.map +1 -0
- package/dist/identity/hub-projection.js +130 -0
- package/dist/identity/hub-projection.js.map +1 -0
- package/dist/identity/hub-reconcile.d.ts +151 -0
- package/dist/identity/hub-reconcile.d.ts.map +1 -0
- package/dist/identity/hub-reconcile.js +400 -0
- package/dist/identity/hub-reconcile.js.map +1 -0
- package/dist/identity/hub-sso.d.ts +363 -0
- package/dist/identity/hub-sso.d.ts.map +1 -0
- package/dist/identity/hub-sso.js +536 -0
- package/dist/identity/hub-sso.js.map +1 -0
- package/dist/identity/hub-user-client.d.ts +222 -0
- package/dist/identity/hub-user-client.d.ts.map +1 -0
- package/dist/identity/hub-user-client.js +406 -0
- package/dist/identity/hub-user-client.js.map +1 -0
- package/dist/identity/index.d.ts +13 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +13 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/oauth-jwt.d.ts +32 -0
- package/dist/identity/oauth-jwt.d.ts.map +1 -0
- package/dist/identity/oauth-jwt.js +122 -0
- package/dist/identity/oauth-jwt.js.map +1 -0
- package/dist/identity/resolve-membership.d.ts +28 -0
- package/dist/identity/resolve-membership.d.ts.map +1 -0
- package/dist/identity/resolve-membership.js +69 -0
- package/dist/identity/resolve-membership.js.map +1 -0
- package/dist/identity/signing-key.d.ts +49 -0
- package/dist/identity/signing-key.d.ts.map +1 -0
- package/dist/identity/signing-key.js +144 -0
- package/dist/identity/signing-key.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/invitations/index.d.ts +2 -0
- package/dist/invitations/index.d.ts.map +1 -0
- package/dist/invitations/index.js +2 -0
- package/dist/invitations/index.js.map +1 -0
- package/dist/invitations/service.d.ts +36 -0
- package/dist/invitations/service.d.ts.map +1 -0
- package/dist/invitations/service.js +122 -0
- package/dist/invitations/service.js.map +1 -0
- package/dist/jobs/index.d.ts +3 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +3 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/pgboss.d.ts +119 -0
- package/dist/jobs/pgboss.d.ts.map +1 -0
- package/dist/jobs/pgboss.js +521 -0
- package/dist/jobs/pgboss.js.map +1 -0
- package/dist/jobs/timers.d.ts +127 -0
- package/dist/jobs/timers.d.ts.map +1 -0
- package/dist/jobs/timers.js +228 -0
- package/dist/jobs/timers.js.map +1 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +64 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp/errors.d.ts +37 -0
- package/dist/mcp/errors.d.ts.map +1 -0
- package/dist/mcp/errors.js +77 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/http.d.ts +35 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +80 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/inputs.d.ts +19 -0
- package/dist/mcp/inputs.d.ts.map +1 -0
- package/dist/mcp/inputs.js +28 -0
- package/dist/mcp/inputs.js.map +1 -0
- package/dist/mcp/projects.d.ts +31 -0
- package/dist/mcp/projects.d.ts.map +1 -0
- package/dist/mcp/projects.js +315 -0
- package/dist/mcp/projects.js.map +1 -0
- package/dist/mcp/server.d.ts +52 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +93 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/teams.d.ts +7 -0
- package/dist/mcp/teams.d.ts.map +1 -0
- package/dist/mcp/teams.js +62 -0
- package/dist/mcp/teams.js.map +1 -0
- package/dist/mcp/tool-kit.d.ts +67 -0
- package/dist/mcp/tool-kit.d.ts.map +1 -0
- package/dist/mcp/tool-kit.js +74 -0
- package/dist/mcp/tool-kit.js.map +1 -0
- package/dist/members/removal.d.ts +95 -0
- package/dist/members/removal.d.ts.map +1 -0
- package/dist/members/removal.js +143 -0
- package/dist/members/removal.js.map +1 -0
- package/dist/middleware/auth-body.d.ts +25 -0
- package/dist/middleware/auth-body.d.ts.map +1 -0
- package/dist/middleware/auth-body.js +55 -0
- package/dist/middleware/auth-body.js.map +1 -0
- package/dist/middleware/auth-context.d.ts +170 -0
- package/dist/middleware/auth-context.d.ts.map +1 -0
- package/dist/middleware/auth-context.js +251 -0
- package/dist/middleware/auth-context.js.map +1 -0
- package/dist/middleware/body-cap.d.ts +17 -0
- package/dist/middleware/body-cap.d.ts.map +1 -0
- package/dist/middleware/body-cap.js +31 -0
- package/dist/middleware/body-cap.js.map +1 -0
- package/dist/middleware/body-refusal.d.ts +20 -0
- package/dist/middleware/body-refusal.d.ts.map +1 -0
- package/dist/middleware/body-refusal.js +5 -0
- package/dist/middleware/body-refusal.js.map +1 -0
- package/dist/middleware/cross-site.d.ts +61 -0
- package/dist/middleware/cross-site.d.ts.map +1 -0
- package/dist/middleware/cross-site.js +59 -0
- package/dist/middleware/cross-site.js.map +1 -0
- package/dist/middleware/hub-managed.d.ts +45 -0
- package/dist/middleware/hub-managed.d.ts.map +1 -0
- package/dist/middleware/hub-managed.js +56 -0
- package/dist/middleware/hub-managed.js.map +1 -0
- package/dist/middleware/idempotency.d.ts +14 -0
- package/dist/middleware/idempotency.d.ts.map +1 -0
- package/dist/middleware/idempotency.js +222 -0
- package/dist/middleware/idempotency.js.map +1 -0
- package/dist/middleware/index.d.ts +12 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +12 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/json-depth.d.ts +32 -0
- package/dist/middleware/json-depth.d.ts.map +1 -0
- package/dist/middleware/json-depth.js +80 -0
- package/dist/middleware/json-depth.js.map +1 -0
- package/dist/middleware/no-store.d.ts +21 -0
- package/dist/middleware/no-store.d.ts.map +1 -0
- package/dist/middleware/no-store.js +32 -0
- package/dist/middleware/no-store.js.map +1 -0
- package/dist/middleware/oauth-public.d.ts +16 -0
- package/dist/middleware/oauth-public.d.ts.map +1 -0
- package/dist/middleware/oauth-public.js +50 -0
- package/dist/middleware/oauth-public.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +182 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +277 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/request-id.d.ts +10 -0
- package/dist/middleware/request-id.d.ts.map +1 -0
- package/dist/middleware/request-id.js +42 -0
- package/dist/middleware/request-id.js.map +1 -0
- package/dist/middleware/scopes.d.ts +33 -0
- package/dist/middleware/scopes.d.ts.map +1 -0
- package/dist/middleware/scopes.js +139 -0
- package/dist/middleware/scopes.js.map +1 -0
- package/dist/middleware/security-headers.d.ts +53 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/security-headers.js +96 -0
- package/dist/middleware/security-headers.js.map +1 -0
- package/dist/observability/index.d.ts +3 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +3 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/metrics.d.ts +17 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +82 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/otel.d.ts +20 -0
- package/dist/observability/otel.d.ts.map +1 -0
- package/dist/observability/otel.js +54 -0
- package/dist/observability/otel.js.map +1 -0
- package/dist/pagination.d.ts +57 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +90 -0
- package/dist/pagination.js.map +1 -0
- package/dist/platform/edition.d.ts +57 -0
- package/dist/platform/edition.d.ts.map +1 -0
- package/dist/platform/edition.js +90 -0
- package/dist/platform/edition.js.map +1 -0
- package/dist/platform/entitlements.d.ts +26 -0
- package/dist/platform/entitlements.d.ts.map +1 -0
- package/dist/platform/entitlements.js +28 -0
- package/dist/platform/entitlements.js.map +1 -0
- package/dist/platform/events.d.ts +87 -0
- package/dist/platform/events.d.ts.map +1 -0
- package/dist/platform/events.js +32 -0
- package/dist/platform/events.js.map +1 -0
- package/dist/platform/index.d.ts +8 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +8 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/local-identity.d.ts +39 -0
- package/dist/platform/local-identity.d.ts.map +1 -0
- package/dist/platform/local-identity.js +87 -0
- package/dist/platform/local-identity.js.map +1 -0
- package/dist/platform/registry.d.ts +23 -0
- package/dist/platform/registry.d.ts.map +1 -0
- package/dist/platform/registry.js +5 -0
- package/dist/platform/registry.js.map +1 -0
- package/dist/platform/usage.d.ts +11 -0
- package/dist/platform/usage.d.ts.map +1 -0
- package/dist/platform/usage.js +12 -0
- package/dist/platform/usage.js.map +1 -0
- package/dist/platform/workspaces.d.ts +25 -0
- package/dist/platform/workspaces.d.ts.map +1 -0
- package/dist/platform/workspaces.js +44 -0
- package/dist/platform/workspaces.js.map +1 -0
- package/dist/projects/access.d.ts +40 -0
- package/dist/projects/access.d.ts.map +1 -0
- package/dist/projects/access.js +133 -0
- package/dist/projects/access.js.map +1 -0
- package/dist/projects/index.d.ts +2 -0
- package/dist/projects/index.d.ts.map +1 -0
- package/dist/projects/index.js +2 -0
- package/dist/projects/index.js.map +1 -0
- package/dist/route-label.d.ts +23 -0
- package/dist/route-label.d.ts.map +1 -0
- package/dist/route-label.js +29 -0
- package/dist/route-label.js.map +1 -0
- package/dist/routes/health.d.ts +30 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +54 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +3 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/wellknown.d.ts +11 -0
- package/dist/routes/wellknown.d.ts.map +1 -0
- package/dist/routes/wellknown.js +45 -0
- package/dist/routes/wellknown.js.map +1 -0
- package/dist/secret.d.ts +30 -0
- package/dist/secret.d.ts.map +1 -0
- package/dist/secret.js +97 -0
- package/dist/secret.js.map +1 -0
- package/dist/setup-transport.d.ts +23 -0
- package/dist/setup-transport.d.ts.map +1 -0
- package/dist/setup-transport.js +52 -0
- package/dist/setup-transport.js.map +1 -0
- package/dist/state.d.ts +21 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +4 -0
- package/dist/state.js.map +1 -0
- package/dist/storage/driver.d.ts +38 -0
- package/dist/storage/driver.d.ts.map +1 -0
- package/dist/storage/driver.js +14 -0
- package/dist/storage/driver.js.map +1 -0
- package/dist/storage/factory.d.ts +4 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +20 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +4 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local.d.ts +21 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +88 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/s3.d.ts +29 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +82 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/testing/fake-hub.d.ts +425 -0
- package/dist/testing/fake-hub.d.ts.map +1 -0
- package/dist/testing/fake-hub.js +1074 -0
- package/dist/testing/fake-hub.js.map +1 -0
- package/dist/testing/helpers.d.ts +124 -0
- package/dist/testing/helpers.d.ts.map +1 -0
- package/dist/testing/helpers.js +174 -0
- package/dist/testing/helpers.js.map +1 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +12 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/setup-origins.d.ts +21 -0
- package/dist/testing/setup-origins.d.ts.map +1 -0
- package/dist/testing/setup-origins.js +57 -0
- package/dist/testing/setup-origins.js.map +1 -0
- package/dist/testing/sso-helpers.d.ts +43 -0
- package/dist/testing/sso-helpers.d.ts.map +1 -0
- package/dist/testing/sso-helpers.js +159 -0
- package/dist/testing/sso-helpers.js.map +1 -0
- package/dist/tool-definition.d.ts +382 -0
- package/dist/tool-definition.d.ts.map +1 -0
- package/dist/tool-definition.js +46 -0
- package/dist/tool-definition.js.map +1 -0
- package/dist/util/index.d.ts +8 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +8 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +161 -0
- package/src/accounts/deletion.ts +389 -0
- package/src/accounts/erasure-log.ts +140 -0
- package/src/accounts/index.ts +5 -0
- package/src/accounts/mint-refusal.ts +67 -0
- package/src/accounts/signup-duplicate.ts +21 -0
- package/src/accounts/superadmin.ts +46 -0
- package/src/api/apikeys.ts +147 -0
- package/src/api/audit.ts +83 -0
- package/src/api/break-glass.ts +294 -0
- package/src/api/cli-auth.ts +308 -0
- package/src/api/create-api.ts +1403 -0
- package/src/api/demo-passes.ts +207 -0
- package/src/api/errors.ts +13 -0
- package/src/api/export.ts +482 -0
- package/src/api/files.ts +200 -0
- package/src/api/index.ts +29 -0
- package/src/api/invitations.ts +396 -0
- package/src/api/members.ts +617 -0
- package/src/api/onboarding.ts +50 -0
- package/src/api/openapi-doc.ts +34 -0
- package/src/api/projects.ts +788 -0
- package/src/api/scope-routes.ts +12 -0
- package/src/api/sso-connect.ts +246 -0
- package/src/api/sso-logout.ts +76 -0
- package/src/api/teams.ts +575 -0
- package/src/api/workspaces.ts +649 -0
- package/src/apikeys/index.ts +2 -0
- package/src/apikeys/peppers.ts +118 -0
- package/src/apikeys/service.ts +223 -0
- package/src/app.ts +228 -0
- package/src/audit/filters.ts +101 -0
- package/src/audit/index.ts +9 -0
- package/src/audit/service.ts +154 -0
- package/src/boot.ts +953 -0
- package/src/constant-time.ts +9 -0
- package/src/email/driver.ts +139 -0
- package/src/email/index.ts +26 -0
- package/src/email/shell.ts +201 -0
- package/src/email/templates.ts +184 -0
- package/src/entitlements/check.ts +435 -0
- package/src/entitlements/clock-skew.ts +58 -0
- package/src/entitlements/gate.ts +636 -0
- package/src/entitlements/hub-machine-token.ts +205 -0
- package/src/entitlements/hub-subject.ts +77 -0
- package/src/entitlements/index.ts +37 -0
- package/src/entitlements/poster.ts +265 -0
- package/src/entitlements/profiles.ts +303 -0
- package/src/entitlements/slot.ts +105 -0
- package/src/env.ts +456 -0
- package/src/files/http.ts +77 -0
- package/src/files/index.ts +4 -0
- package/src/files/serve.ts +115 -0
- package/src/files/service.ts +209 -0
- package/src/files/spool.ts +58 -0
- package/src/identity/better-auth.ts +1103 -0
- package/src/identity/demo-pass-plugin.ts +121 -0
- package/src/identity/demo-pass-rules.ts +229 -0
- package/src/identity/demo-pass.ts +390 -0
- package/src/identity/hub-grant.ts +704 -0
- package/src/identity/hub-jwt.ts +78 -0
- package/src/identity/hub-live-gate.ts +167 -0
- package/src/identity/hub-logout.ts +174 -0
- package/src/identity/hub-projection.ts +251 -0
- package/src/identity/hub-reconcile.ts +572 -0
- package/src/identity/hub-sso.ts +696 -0
- package/src/identity/hub-user-client.ts +554 -0
- package/src/identity/index.ts +56 -0
- package/src/identity/oauth-jwt.ts +128 -0
- package/src/identity/resolve-membership.ts +104 -0
- package/src/identity/signing-key.ts +164 -0
- package/src/index.ts +65 -0
- package/src/invitations/index.ts +1 -0
- package/src/invitations/service.ts +171 -0
- package/src/jobs/index.ts +25 -0
- package/src/jobs/pgboss.ts +594 -0
- package/src/jobs/timers.ts +287 -0
- package/src/logger.ts +67 -0
- package/src/mcp/errors.ts +111 -0
- package/src/mcp/http.ts +129 -0
- package/src/mcp/index.ts +28 -0
- package/src/mcp/inputs.ts +33 -0
- package/src/mcp/projects.ts +480 -0
- package/src/mcp/server.ts +174 -0
- package/src/mcp/teams.ts +97 -0
- package/src/mcp/tool-kit.ts +148 -0
- package/src/members/removal.ts +216 -0
- package/src/middleware/auth-body.ts +58 -0
- package/src/middleware/auth-context.ts +384 -0
- package/src/middleware/body-cap.ts +33 -0
- package/src/middleware/body-refusal.ts +23 -0
- package/src/middleware/cross-site.ts +122 -0
- package/src/middleware/hub-managed.ts +61 -0
- package/src/middleware/idempotency.ts +264 -0
- package/src/middleware/index.ts +33 -0
- package/src/middleware/json-depth.ts +74 -0
- package/src/middleware/no-store.ts +30 -0
- package/src/middleware/oauth-public.ts +54 -0
- package/src/middleware/rate-limit.ts +432 -0
- package/src/middleware/request-id.ts +54 -0
- package/src/middleware/scopes.ts +150 -0
- package/src/middleware/security-headers.ts +129 -0
- package/src/observability/index.ts +2 -0
- package/src/observability/metrics.ts +102 -0
- package/src/observability/otel.ts +79 -0
- package/src/pagination.ts +118 -0
- package/src/platform/edition.ts +140 -0
- package/src/platform/entitlements.ts +47 -0
- package/src/platform/events.ts +71 -0
- package/src/platform/index.ts +7 -0
- package/src/platform/local-identity.ts +90 -0
- package/src/platform/registry.ts +28 -0
- package/src/platform/usage.ts +13 -0
- package/src/platform/workspaces.ts +41 -0
- package/src/projects/access.ts +160 -0
- package/src/projects/index.ts +7 -0
- package/src/route-label.ts +29 -0
- package/src/routes/health.ts +79 -0
- package/src/routes/index.ts +2 -0
- package/src/routes/wellknown.ts +67 -0
- package/src/secret.ts +115 -0
- package/src/setup-transport.ts +46 -0
- package/src/state.ts +23 -0
- package/src/storage/driver.ts +48 -0
- package/src/storage/factory.ts +34 -0
- package/src/storage/index.ts +3 -0
- package/src/storage/local.ts +93 -0
- package/src/storage/s3.ts +109 -0
- package/src/testing/fake-hub.ts +1316 -0
- package/src/testing/helpers.ts +254 -0
- package/src/testing/index.ts +11 -0
- package/src/testing/setup-origins.ts +58 -0
- package/src/testing/sso-helpers.ts +178 -0
- package/src/tool-definition.ts +463 -0
- package/src/util/index.ts +17 -0
- package/test/fixtures/onboarding-backfill.sql +13 -0
- package/test/fixtures/operator-user-id-backfill.sql +18 -0
- package/test/host/dr-lib.sh +253 -0
- package/test/host/drizzle/0000_chassis_baseline.sql +438 -0
- package/test/host/drizzle/0001_last_owner_guard.sql +42 -0
- package/test/host/drizzle/meta/0000_snapshot.json +3566 -0
- package/test/host/drizzle/meta/_journal.json +20 -0
- package/test/host/drizzle.config.ts +17 -0
- package/test/host/identity.ts +41 -0
- package/test/host/minimal-host.ts +35 -0
- package/test/host/minimal-tool.ts +123 -0
- package/test/integration/account-oracle-uniformity.test.ts +284 -0
- package/test/integration/account-recovery.test.ts +236 -0
- package/test/integration/account.test.ts +120 -0
- package/test/integration/api-quota.test.ts +208 -0
- package/test/integration/apikey-expiry.test.ts +178 -0
- package/test/integration/apikey-pepper-rotation.test.ts +190 -0
- package/test/integration/boot-posture.test.ts +169 -0
- package/test/integration/break-glass.test.ts +484 -0
- package/test/integration/cloud-lifecycle.test.ts +233 -0
- package/test/integration/credential-boundaries.test.ts +195 -0
- package/test/integration/default-workspace.test.ts +414 -0
- package/test/integration/demo-pass-rules.test.ts +332 -0
- package/test/integration/demo-pass.test.ts +1222 -0
- package/test/integration/dr-verify-real-dump.test.ts +154 -0
- package/test/integration/edition.test.ts +868 -0
- package/test/integration/email-change.test.ts +394 -0
- package/test/integration/empty-tool.test.ts +267 -0
- package/test/integration/entitlements-null-hook.test.ts +187 -0
- package/test/integration/entitlements.test.ts +693 -0
- package/test/integration/export-entries.test.ts +321 -0
- package/test/integration/files.test.ts +320 -0
- package/test/integration/first-boot-claim.test.ts +131 -0
- package/test/integration/gdpr.test.ts +532 -0
- package/test/integration/helpers.ts +29 -0
- package/test/integration/hub-grant.test.ts +505 -0
- package/test/integration/hub-live-gate.test.ts +612 -0
- package/test/integration/hub-managed-membership.test.ts +559 -0
- package/test/integration/hub-org-selector.test.ts +281 -0
- package/test/integration/hub-reconcile.test.ts +646 -0
- package/test/integration/hub-sso.test.ts +704 -0
- package/test/integration/hub-teams.test.ts +771 -0
- package/test/integration/idempotency.test.ts +261 -0
- package/test/integration/invite-verification.test.ts +210 -0
- package/test/integration/last-owner-race.test.ts +263 -0
- package/test/integration/m9-fixes.test.ts +290 -0
- package/test/integration/mcp-projects.test.ts +463 -0
- package/test/integration/member-removal-race.test.ts +466 -0
- package/test/integration/member-removal.test.ts +794 -0
- package/test/integration/oauth-mcp.test.ts +477 -0
- package/test/integration/oauth-workspace.test.ts +371 -0
- package/test/integration/observability.test.ts +183 -0
- package/test/integration/onboarding.test.ts +268 -0
- package/test/integration/orphan-purge.test.ts +451 -0
- package/test/integration/pagination.test.ts +344 -0
- package/test/integration/platform-core.test.ts +305 -0
- package/test/integration/platform.test.ts +246 -0
- package/test/integration/pool-survives-restart.test.ts +80 -0
- package/test/integration/projects-cloud.test.ts +193 -0
- package/test/integration/projects.test.ts +1310 -0
- package/test/integration/security-fixes.test.ts +190 -0
- package/test/integration/setup-transport.test.ts +118 -0
- package/test/integration/signin-wall.test.ts +103 -0
- package/test/integration/signing-key-rotation.test.ts +349 -0
- package/test/integration/sso-helpers.ts +15 -0
- package/test/integration/sso-logout.test.ts +283 -0
- package/test/integration/teams-cloud.test.ts +194 -0
- package/test/integration/teams.test.ts +551 -0
- package/test/integration/timers.test.ts +377 -0
- package/test/integration/two-factor.test.ts +301 -0
- package/test/integration/usage-window.test.ts +161 -0
- package/test/integration/workspace-create-cloud.test.ts +523 -0
- package/test/integration/workspace-create.test.ts +510 -0
- package/test/integration/workspace-scoping.test.ts +530 -0
- package/test/integration/zz-adv-replay-owner.test.ts +205 -0
- package/test/unit/apikey-peppers.test.ts +110 -0
- package/test/unit/apikey-prefix.test.ts +34 -0
- package/test/unit/audit-filters.test.ts +135 -0
- package/test/unit/auth-origins.test.ts +92 -0
- package/test/unit/body-cap.test.ts +84 -0
- package/test/unit/clock-skew.test.ts +68 -0
- package/test/unit/cross-site.test.ts +166 -0
- package/test/unit/declared-credits.test.ts +13 -0
- package/test/unit/demo-pass-rules.test.ts +192 -0
- package/test/unit/email-driver.test.ts +127 -0
- package/test/unit/end-pool.test.ts +181 -0
- package/test/unit/entitlement-check.test.ts +492 -0
- package/test/unit/entitlement-gate.test.ts +1333 -0
- package/test/unit/entitlement-profiles.test.ts +289 -0
- package/test/unit/erasure-log.test.ts +72 -0
- package/test/unit/error-mapping.test.ts +89 -0
- package/test/unit/export-entries.test.ts +85 -0
- package/test/unit/export-sanitize.test.ts +27 -0
- package/test/unit/files-head-route.test.ts +155 -0
- package/test/unit/files-http.test.ts +97 -0
- package/test/unit/head-rewrite.test.ts +72 -0
- package/test/unit/hub-create-org.test.ts +181 -0
- package/test/unit/hub-jwt.test.ts +93 -0
- package/test/unit/hub-sso-params.test.ts +39 -0
- package/test/unit/hub-subject.test.ts +83 -0
- package/test/unit/idempotency-crypto.test.ts +45 -0
- package/test/unit/json-depth.test.ts +88 -0
- package/test/unit/logger-redaction.test.ts +66 -0
- package/test/unit/mcp-errors.test.ts +110 -0
- package/test/unit/mcp-inputs.test.ts +106 -0
- package/test/unit/mcp-project-hints.test.ts +26 -0
- package/test/unit/no-store.test.ts +49 -0
- package/test/unit/no-tool-migration-filename.test.ts +70 -0
- package/test/unit/observability.test.ts +64 -0
- package/test/unit/openapi-doc.test.ts +93 -0
- package/test/unit/rate-limit-consume-on.test.ts +76 -0
- package/test/unit/rate-limit.test.ts +39 -0
- package/test/unit/readyz-storage.test.ts +157 -0
- package/test/unit/request-quota.test.ts +146 -0
- package/test/unit/scopes.test.ts +66 -0
- package/test/unit/security-headers.test.ts +131 -0
- package/test/unit/serve-content-type.test.ts +74 -0
- package/test/unit/setup-transport.test.ts +29 -0
- package/test/unit/storage-local.test.ts +53 -0
- package/test/unit/superadmin.test.ts +32 -0
- package/test/unit/timers.test.ts +287 -0
- package/test/unit/tool-definition-guards.test.ts +166 -0
- package/test/unit/tool-entitlements-guard.test.ts +168 -0
- package/test/unit/usage-poster.test.ts +542 -0
- package/test/unit/usage-window.test.ts +48 -0
- package/test/unit/workspace-create-audit.test.ts +48 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
2
|
+
/** Discovery-document cache lifetime; the JWKS itself is cached by jose. */
|
|
3
|
+
const DISCOVERY_TTL_MS = 10 * 60 * 1000;
|
|
4
|
+
/**
|
|
5
|
+
* Verifies hub-minted RS256 JWTs against the hub's REMOTE JWKS (discovered
|
|
6
|
+
* via OIDC discovery at the issuer root) — the cloud edition's trust anchor
|
|
7
|
+
* for "Sign in with Antasphere" (internal/federation.md). Same discipline as
|
|
8
|
+
* the local OauthJwtVerifier: hard issuer pinning, per-call audience
|
|
9
|
+
* pinning, RS256 allowlist (implicitly rejects none/HS256), small clock
|
|
10
|
+
* tolerance. jose's remote key set self-refreshes on an unknown `kid`; a
|
|
11
|
+
* signature failure on a KNOWN kid (rotation racing the cache) gets exactly
|
|
12
|
+
* one retry against a freshly created key set.
|
|
13
|
+
*/
|
|
14
|
+
export class HubJwtVerifier {
|
|
15
|
+
issuerUrl;
|
|
16
|
+
discoveryCache = null;
|
|
17
|
+
keySet = null;
|
|
18
|
+
constructor(issuerUrl) {
|
|
19
|
+
this.issuerUrl = issuerUrl;
|
|
20
|
+
}
|
|
21
|
+
async jwksUri() {
|
|
22
|
+
if (this.discoveryCache && Date.now() - this.discoveryCache.fetchedAt < DISCOVERY_TTL_MS) {
|
|
23
|
+
return this.discoveryCache.jwksUri;
|
|
24
|
+
}
|
|
25
|
+
const url = this.issuerUrl.replace(/\/+$/, '') + '/.well-known/openid-configuration';
|
|
26
|
+
const res = await fetch(url);
|
|
27
|
+
if (!res.ok)
|
|
28
|
+
throw new Error(`hub discovery failed: ${res.status} ${url}`);
|
|
29
|
+
const doc = (await res.json());
|
|
30
|
+
// The discovery document must claim the issuer we were configured to
|
|
31
|
+
// trust — a mismatch means HUB_ISSUER_URL points at the wrong deployment
|
|
32
|
+
// (or something impersonating it); fail closed before trusting its keys.
|
|
33
|
+
if (doc.issuer !== this.issuerUrl) {
|
|
34
|
+
throw new Error(`hub discovery issuer mismatch: expected ${this.issuerUrl}, got ${doc.issuer}`);
|
|
35
|
+
}
|
|
36
|
+
if (typeof doc.jwks_uri !== 'string' || !doc.jwks_uri) {
|
|
37
|
+
throw new Error('hub discovery document carries no jwks_uri');
|
|
38
|
+
}
|
|
39
|
+
this.discoveryCache = { jwksUri: doc.jwks_uri, fetchedAt: Date.now() };
|
|
40
|
+
return this.discoveryCache.jwksUri;
|
|
41
|
+
}
|
|
42
|
+
async keys(forceRefresh = false) {
|
|
43
|
+
const jwksUri = await this.jwksUri();
|
|
44
|
+
if (!forceRefresh && this.keySet)
|
|
45
|
+
return this.keySet;
|
|
46
|
+
this.keySet = createRemoteJWKSet(new URL(jwksUri));
|
|
47
|
+
return this.keySet;
|
|
48
|
+
}
|
|
49
|
+
/** Verify a hub JWT with the given audience pin; throws on any failure. */
|
|
50
|
+
async verify(token, audience) {
|
|
51
|
+
const options = {
|
|
52
|
+
issuer: this.issuerUrl,
|
|
53
|
+
// The hub access token's `aud` is an ARRAY when openid is scoped
|
|
54
|
+
// ([<our resource>, <hub userinfo>]); jose accepts any element
|
|
55
|
+
// matching, so pinning our own value stays exact.
|
|
56
|
+
audience,
|
|
57
|
+
algorithms: ['RS256'], // hard allowlist — implicitly rejects none/HS256
|
|
58
|
+
clockTolerance: 5,
|
|
59
|
+
requiredClaims: ['sub']
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
return (await jwtVerify(token, await this.keys(), options)).payload;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
// One retry with fresh keys covers signing-key rotation mid-cache.
|
|
66
|
+
// Two shapes mean "the cached keys may be stale": an unknown kid
|
|
67
|
+
// (JWKSNoMatchingKey — jose's own refetch is cooldown-throttled and
|
|
68
|
+
// may have been skipped) and a signature failure on a known kid.
|
|
69
|
+
if (err instanceof Error &&
|
|
70
|
+
(err.name === 'JWKSNoMatchingKey' || err.name === 'JWSSignatureVerificationFailed')) {
|
|
71
|
+
return (await jwtVerify(token, await this.keys(true), options)).payload;
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=hub-jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-jwt.js","sourceRoot":"","sources":["../../src/identity/hub-jwt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Db } from '@antasphere/chassis-db';
|
|
2
|
+
import type { Logger } from '../logger.js';
|
|
3
|
+
import type { PrincipalGate } from '../middleware/auth-context.js';
|
|
4
|
+
import type { HubOrgReconciler } from './hub-reconcile.js';
|
|
5
|
+
export interface HubLiveGateDeps {
|
|
6
|
+
db: Db;
|
|
7
|
+
reconciler: HubOrgReconciler;
|
|
8
|
+
logger: Logger;
|
|
9
|
+
}
|
|
10
|
+
export declare class HubLiveGate {
|
|
11
|
+
private readonly deps;
|
|
12
|
+
constructor(deps: HubLiveGateDeps);
|
|
13
|
+
readonly assert: PrincipalGate;
|
|
14
|
+
private memberRow;
|
|
15
|
+
private assertOrgActive;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=hub-live-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-live-gate.d.ts","sourceRoot":"","sources":["../../src/identity/hub-live-gate.ts"],"names":[],"mappings":"AACA,OAAO,EAAgC,KAAK,EAAE,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAiB,aAAa,EAAuB,MAAM,+BAA+B,CAAC;AACvG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAuD3D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,EAAE,CAAC;IACP,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,eAAe;IAElD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAoE5B;YAEY,SAAS;YAcT,eAAe;CAc9B"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
2
|
+
import { workspaceMembers, workspaces } from '@antasphere/chassis-db';
|
|
3
|
+
import { resolveMembership } from './resolve-membership.js';
|
|
4
|
+
/**
|
|
5
|
+
* The cloud edition's post-resolution principal gate (user-scoped
|
|
6
|
+
* federation — the replacement for the retired master-key P4 gates): on
|
|
7
|
+
* EVERY authenticated request into a PROJECTED workspace
|
|
8
|
+
* (`principal.accountRef` set), hub truth is re-read AS THE USER via the
|
|
9
|
+
* TTL'd reconciler, and the request is judged on the freshly reconciled
|
|
10
|
+
* LOCAL rows. No service key, no cross-tenant surface — a user's requests
|
|
11
|
+
* can only ever be gated by their OWN grant's view of the hub.
|
|
12
|
+
*
|
|
13
|
+
* Verdicts, in order:
|
|
14
|
+
* - grant dead → 401 `hub_grant_expired` (immediate, no stale window —
|
|
15
|
+
* Romain's decision 3; a browser SSO re-login heals the grant);
|
|
16
|
+
* - reconcile stale beyond the window AND the hub failing → 403
|
|
17
|
+
* `hub_unavailable` (fail closed after ~15 min of outage);
|
|
18
|
+
* - hub-origin membership row now inactive (this very pass may have swept
|
|
19
|
+
* it) → 401 `membership_revoked`;
|
|
20
|
+
* - workspace `hub_status='suspended'` → 403 `account_suspended`, EXCEPT
|
|
21
|
+
* `GET /api/v1/me` — suspended is VISIBLE-BUT-BLOCKED, and /me is how
|
|
22
|
+
* the dashboard shows the suspended badge instead of stranding the user
|
|
23
|
+
* (the reason the PrincipalGate seam carries the request);
|
|
24
|
+
* - a role delta from the reconciled row applies to THIS request (D11);
|
|
25
|
+
* - a request that SELECTED no workspace, on a credential that pins none,
|
|
26
|
+
* runs in the person's default AS THE PASS LEFT IT: the resolver chose
|
|
27
|
+
* the workspace before the pass ran, so a default moved at the hub
|
|
28
|
+
* (`antasphere org use`) would land the very command that read it in the
|
|
29
|
+
* old organization and only the next one in the new (PRDCT-2958, F2).
|
|
30
|
+
* The membership is resolved again here and handed back as `workspace`.
|
|
31
|
+
*
|
|
32
|
+
* Guest and local-origin rows in a projected workspace skip hub enforcement
|
|
33
|
+
* entirely EXCEPT the org-level suspension read — and that reads the
|
|
34
|
+
* LOCALLY MATERIALIZED `workspaces.hub_status` column only, never a hub
|
|
35
|
+
* round-trip on a borrowed grant. ACCEPTED BOUND (documented): a guest's
|
|
36
|
+
* own `/orgs` never includes the resource's org, so that column refreshes only
|
|
37
|
+
* when a MEMBER's reconcile runs — a guest may keep resource access in a
|
|
38
|
+
* hub-suspended org until a member next touches the tool. Every cheap fix
|
|
39
|
+
* leaks (a public status probe = suspension oracle) or borrows credentials
|
|
40
|
+
* (acting on a grant its holder didn't present). Same accepted-bound class
|
|
41
|
+
* as ungated share-link viewing; suspension still cuts all MEMBERS within
|
|
42
|
+
* seconds.
|
|
43
|
+
*/
|
|
44
|
+
const SUSPENDED_MESSAGE = 'This organization is suspended on Antasphere';
|
|
45
|
+
const UNAVAILABLE_MESSAGE = 'The Antasphere hub has been unreachable for too long; requests are refused until it recovers';
|
|
46
|
+
const GRANT_EXPIRED_MESSAGE = 'Your Antasphere grant on this instance has expired — sign in with Antasphere again to renew it';
|
|
47
|
+
/** The one suspension exemption: the dashboard's whoami read. */
|
|
48
|
+
function isMeRead(request) {
|
|
49
|
+
return request.method === 'GET' && request.path === '/api/v1/me';
|
|
50
|
+
}
|
|
51
|
+
export class HubLiveGate {
|
|
52
|
+
deps;
|
|
53
|
+
constructor(deps) {
|
|
54
|
+
this.deps = deps;
|
|
55
|
+
}
|
|
56
|
+
assert = async (principal, request) => {
|
|
57
|
+
// Unprojected workspace: zero hub surface on this request.
|
|
58
|
+
if (!principal.accountRef)
|
|
59
|
+
return { ok: true };
|
|
60
|
+
// Guest/local rows are the tool's own business (never hub truth): no
|
|
61
|
+
// reconcile, no membership re-assertion, no grant requirement. Org-level
|
|
62
|
+
// suspension still applies, read from the local column.
|
|
63
|
+
if (principal.origin !== 'hub') {
|
|
64
|
+
return this.assertOrgActive(principal.workspaceId, request);
|
|
65
|
+
}
|
|
66
|
+
const verdict = await this.deps.reconciler.reconcile(principal.userId);
|
|
67
|
+
if (verdict.kind === 'grant_dead') {
|
|
68
|
+
return { ok: false, status: 401, code: 'hub_grant_expired', message: GRANT_EXPIRED_MESSAGE };
|
|
69
|
+
}
|
|
70
|
+
if (verdict.kind === 'unavailable') {
|
|
71
|
+
return { ok: false, status: 403, code: 'hub_unavailable', message: UNAVAILABLE_MESSAGE };
|
|
72
|
+
}
|
|
73
|
+
// Judge the FRESHLY RECONCILED local rows (the pass above may have
|
|
74
|
+
// swept the membership, synced the role, or flipped hub_status).
|
|
75
|
+
const row = await this.memberRow(principal.userId, principal.workspaceId);
|
|
76
|
+
if (!row || !row.isActive) {
|
|
77
|
+
// Resolution saw an active row moments ago; the reconcile swept it —
|
|
78
|
+
// the hub no longer asserts this membership. Every later request dies
|
|
79
|
+
// at resolution itself (plain 401); THIS one carries the reason. A
|
|
80
|
+
// swept membership is never papered over by a move to another
|
|
81
|
+
// workspace below: the person learns what happened.
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
status: 401,
|
|
85
|
+
code: 'membership_revoked',
|
|
86
|
+
message: 'Your membership of this organization was removed on Antasphere'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// A selector-less request on an unpinned credential runs in the default
|
|
90
|
+
// membership as the pass above left it (the resolver read it before, so
|
|
91
|
+
// a default moved at the hub would land THIS request in the old one).
|
|
92
|
+
// The moved-to workspace is judged exactly as the resolved one is.
|
|
93
|
+
if (request.selector === null && !principal.pinned) {
|
|
94
|
+
const fresh = await resolveMembership(this.deps.db, principal.userId, null);
|
|
95
|
+
if (fresh && fresh.workspaceId !== principal.workspaceId) {
|
|
96
|
+
const target = await this.memberRow(principal.userId, fresh.workspaceId);
|
|
97
|
+
if (target?.isActive) {
|
|
98
|
+
if (target.hubStatus === 'suspended' && !isMeRead(request)) {
|
|
99
|
+
return { ok: false, status: 403, code: 'account_suspended', message: SUSPENDED_MESSAGE };
|
|
100
|
+
}
|
|
101
|
+
const moved = {
|
|
102
|
+
workspaceId: fresh.workspaceId,
|
|
103
|
+
role: target.role,
|
|
104
|
+
origin: fresh.origin,
|
|
105
|
+
accountRef: fresh.accountRef
|
|
106
|
+
};
|
|
107
|
+
return { ok: true, workspace: moved };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (row.hubStatus === 'suspended' && !isMeRead(request)) {
|
|
112
|
+
return { ok: false, status: 403, code: 'account_suspended', message: SUSPENDED_MESSAGE };
|
|
113
|
+
}
|
|
114
|
+
// A role delta from the reconciled row applies to THIS request (D11):
|
|
115
|
+
// a demoted admin loses admin surfaces now, a promotion lands now.
|
|
116
|
+
if (row.role !== principal.role) {
|
|
117
|
+
return { ok: true, role: row.role };
|
|
118
|
+
}
|
|
119
|
+
return { ok: true };
|
|
120
|
+
};
|
|
121
|
+
async memberRow(userId, workspaceId) {
|
|
122
|
+
const [row] = await this.deps.db
|
|
123
|
+
.select({
|
|
124
|
+
role: workspaceMembers.role,
|
|
125
|
+
isActive: workspaceMembers.isActive,
|
|
126
|
+
hubStatus: workspaces.hubStatus
|
|
127
|
+
})
|
|
128
|
+
.from(workspaceMembers)
|
|
129
|
+
.innerJoin(workspaces, eq(workspaceMembers.workspaceId, workspaces.id))
|
|
130
|
+
.where(and(eq(workspaceMembers.userId, userId), eq(workspaceMembers.workspaceId, workspaceId)))
|
|
131
|
+
.limit(1);
|
|
132
|
+
return row;
|
|
133
|
+
}
|
|
134
|
+
async assertOrgActive(workspaceId, request) {
|
|
135
|
+
const [ws] = await this.deps.db
|
|
136
|
+
.select({ hubStatus: workspaces.hubStatus })
|
|
137
|
+
.from(workspaces)
|
|
138
|
+
.where(eq(workspaces.id, workspaceId))
|
|
139
|
+
.limit(1);
|
|
140
|
+
if (ws?.hubStatus === 'suspended' && !isMeRead(request)) {
|
|
141
|
+
return { ok: false, status: 403, code: 'account_suspended', message: SUSPENDED_MESSAGE };
|
|
142
|
+
}
|
|
143
|
+
return { ok: true };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=hub-live-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-live-gate.js","sourceRoot":"","sources":["../../src/identity/hub-live-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { symmetricDecrypt } from 'better-auth/crypto';
|
|
2
|
+
import { type Db } from '@antasphere/chassis-db';
|
|
3
|
+
import type { Logger } from '../logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* RP-initiated logout against the hub (SL-2, internal/federation.md): "one
|
|
6
|
+
* concept of being logged in" needs logout-anywhere to end the HUB session,
|
|
7
|
+
* not just the local one. The hub's oauth-provider plugin exposes
|
|
8
|
+
* `GET /oauth2/end-session` (RP-initiated logout); this module builds the
|
|
9
|
+
* URL the browser must VISIT to end the anchor session — the local revoke
|
|
10
|
+
* itself happens in api/sso-logout.ts regardless of whether a URL could be
|
|
11
|
+
* built.
|
|
12
|
+
*
|
|
13
|
+
* Everything here FAILS SOFT to `null` ("no hub leg — local signout only"):
|
|
14
|
+
* a logout must never be blocked by a missing id_token, a hub outage, or a
|
|
15
|
+
* malformed discovery document. Pinned 1.6.15 plugin facts this leans on
|
|
16
|
+
* (re-verify on ANY Better Auth / oauth-provider bump):
|
|
17
|
+
*
|
|
18
|
+
* - the id_token is stored PLAINTEXT on the `account` row —
|
|
19
|
+
* `encryptOAuthTokens` wraps only access/refresh tokens (setTokenUtil,
|
|
20
|
+
* dist/oauth2/utils.mjs; idToken is written raw in
|
|
21
|
+
* generic-oauth/routes.mjs + oauth2/link-account.mjs). The read below is
|
|
22
|
+
* still encrypted-TOLERANT (the hub-grant isLikelyEncrypted mirror) so a
|
|
23
|
+
* future bump that starts encrypting it cannot break logout;
|
|
24
|
+
* - the end-session endpoint HARD-FAILS on an id_token_hint without a
|
|
25
|
+
* `sid` claim ("id token missing session") — pre-flip id_tokens (minted
|
|
26
|
+
* before the hub client's enableEndSession flip) carry none, so a
|
|
27
|
+
* sid-less token degrades HERE to null instead of bouncing the user
|
|
28
|
+
* through a hub error;
|
|
29
|
+
* - verification there is signature-only (jose compactVerify against the
|
|
30
|
+
* hub JWKS) — an EXPIRED id_token is still a usable hint, so no expiry
|
|
31
|
+
* check happens here either;
|
|
32
|
+
* - `post_logout_redirect_uri` must EXACT-match the client's registered
|
|
33
|
+
* postLogoutRedirectUris — the cross-repo contract pins
|
|
34
|
+
* `<PUBLIC_BASE_URL>/login?signed_out=1`, seeded in the hub registry.
|
|
35
|
+
*/
|
|
36
|
+
/** The pinned post-logout landing (cross-repo contract — match EXACTLY). */
|
|
37
|
+
export declare function postLogoutRedirectUri(publicBaseUrl: string): string;
|
|
38
|
+
export interface HubLogoutOptions {
|
|
39
|
+
db: Db;
|
|
40
|
+
/** Hub OIDC issuer (HUB_ISSUER_URL) — discovery derives from it. */
|
|
41
|
+
issuerUrl: string;
|
|
42
|
+
/** This instance's public origin — the post-logout landing derives from it. */
|
|
43
|
+
publicBaseUrl: string;
|
|
44
|
+
/**
|
|
45
|
+
* Better Auth's token-encryption key material (`(await
|
|
46
|
+
* auth.$context).secretConfig`) — only ever consulted when a stored
|
|
47
|
+
* id_token LOOKS encrypted (future-bump insurance; today's are plaintext).
|
|
48
|
+
*/
|
|
49
|
+
key: () => Promise<Parameters<typeof symmetricDecrypt>[0]['key']>;
|
|
50
|
+
logger: Logger;
|
|
51
|
+
/** Discovery fetch timeout — a slow hub must never hang a logout. */
|
|
52
|
+
discoveryTimeoutMs?: number;
|
|
53
|
+
/** Test seam. */
|
|
54
|
+
fetchImpl?: typeof fetch;
|
|
55
|
+
}
|
|
56
|
+
export declare class HubLogoutService {
|
|
57
|
+
private readonly opts;
|
|
58
|
+
private readonly fetchImpl;
|
|
59
|
+
private readonly timeoutMs;
|
|
60
|
+
/** Discovery-cached end-session endpoint; failures are retried per call. */
|
|
61
|
+
private endpointCache;
|
|
62
|
+
constructor(opts: HubLogoutOptions);
|
|
63
|
+
/**
|
|
64
|
+
* The hub end-session URL for this user's CURRENT hub session, or null
|
|
65
|
+
* when no usable hub leg exists (no `antasphere` account row, no stored
|
|
66
|
+
* id_token, a sid-less pre-flip id_token, or an unreachable/endpoint-less
|
|
67
|
+
* discovery document). The caller revokes the local session and clears
|
|
68
|
+
* the hint EITHER WAY — null only means the browser skips the hub bounce.
|
|
69
|
+
*/
|
|
70
|
+
endSessionUrl(userId: string): Promise<string | null>;
|
|
71
|
+
/** The user's stored hub id_token, plaintext/encrypted-tolerant. */
|
|
72
|
+
private readIdToken;
|
|
73
|
+
/** Discovery read, cached on success; a failed fetch retries next call. */
|
|
74
|
+
private endSessionEndpoint;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The Set-Cookie header value that CLEARS the shared SSO hint cookie —
|
|
78
|
+
* mirroring the hub-set attributes exactly (cross-repo contract: Domain on
|
|
79
|
+
* the shared parent, Path=/, SameSite=Lax, Secure on https, NOT HttpOnly)
|
|
80
|
+
* so the browser matches and expires the hub's cookie rather than minting a
|
|
81
|
+
* host-only shadow. Max-Age=0 is the deletion.
|
|
82
|
+
*/
|
|
83
|
+
export declare function hintCookieClearHeader(opts: {
|
|
84
|
+
name: string;
|
|
85
|
+
domain: string;
|
|
86
|
+
secure: boolean;
|
|
87
|
+
}): string;
|
|
88
|
+
//# sourceMappingURL=hub-logout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-logout.d.ts","sourceRoot":"","sources":["../../src/identity/hub-logout.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAW,KAAK,EAAE,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAI3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,EAAE,CAAC;IACP,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,GAAG,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,qBAAa,gBAAgB;IAMf,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAAuB;gBAEf,IAAI,EAAE,gBAAgB;IAKnD;;;;;;OAMG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA+B3D,oEAAoE;YACtD,WAAW;IAoBzB,2EAA2E;YAC7D,kBAAkB;CA0BjC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAErG"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
2
|
+
import { decodeJwt } from 'jose';
|
|
3
|
+
import { symmetricDecrypt } from 'better-auth/crypto';
|
|
4
|
+
import { account } from '@antasphere/chassis-db';
|
|
5
|
+
import { isLikelyEncrypted } from './hub-grant.js';
|
|
6
|
+
import { HUB_SSO_PROVIDER_ID } from './hub-sso.js';
|
|
7
|
+
/**
|
|
8
|
+
* RP-initiated logout against the hub (SL-2, internal/federation.md): "one
|
|
9
|
+
* concept of being logged in" needs logout-anywhere to end the HUB session,
|
|
10
|
+
* not just the local one. The hub's oauth-provider plugin exposes
|
|
11
|
+
* `GET /oauth2/end-session` (RP-initiated logout); this module builds the
|
|
12
|
+
* URL the browser must VISIT to end the anchor session — the local revoke
|
|
13
|
+
* itself happens in api/sso-logout.ts regardless of whether a URL could be
|
|
14
|
+
* built.
|
|
15
|
+
*
|
|
16
|
+
* Everything here FAILS SOFT to `null` ("no hub leg — local signout only"):
|
|
17
|
+
* a logout must never be blocked by a missing id_token, a hub outage, or a
|
|
18
|
+
* malformed discovery document. Pinned 1.6.15 plugin facts this leans on
|
|
19
|
+
* (re-verify on ANY Better Auth / oauth-provider bump):
|
|
20
|
+
*
|
|
21
|
+
* - the id_token is stored PLAINTEXT on the `account` row —
|
|
22
|
+
* `encryptOAuthTokens` wraps only access/refresh tokens (setTokenUtil,
|
|
23
|
+
* dist/oauth2/utils.mjs; idToken is written raw in
|
|
24
|
+
* generic-oauth/routes.mjs + oauth2/link-account.mjs). The read below is
|
|
25
|
+
* still encrypted-TOLERANT (the hub-grant isLikelyEncrypted mirror) so a
|
|
26
|
+
* future bump that starts encrypting it cannot break logout;
|
|
27
|
+
* - the end-session endpoint HARD-FAILS on an id_token_hint without a
|
|
28
|
+
* `sid` claim ("id token missing session") — pre-flip id_tokens (minted
|
|
29
|
+
* before the hub client's enableEndSession flip) carry none, so a
|
|
30
|
+
* sid-less token degrades HERE to null instead of bouncing the user
|
|
31
|
+
* through a hub error;
|
|
32
|
+
* - verification there is signature-only (jose compactVerify against the
|
|
33
|
+
* hub JWKS) — an EXPIRED id_token is still a usable hint, so no expiry
|
|
34
|
+
* check happens here either;
|
|
35
|
+
* - `post_logout_redirect_uri` must EXACT-match the client's registered
|
|
36
|
+
* postLogoutRedirectUris — the cross-repo contract pins
|
|
37
|
+
* `<PUBLIC_BASE_URL>/login?signed_out=1`, seeded in the hub registry.
|
|
38
|
+
*/
|
|
39
|
+
/** The pinned post-logout landing (cross-repo contract — match EXACTLY). */
|
|
40
|
+
export function postLogoutRedirectUri(publicBaseUrl) {
|
|
41
|
+
return publicBaseUrl.replace(/\/+$/, '') + '/login?signed_out=1';
|
|
42
|
+
}
|
|
43
|
+
export class HubLogoutService {
|
|
44
|
+
opts;
|
|
45
|
+
fetchImpl;
|
|
46
|
+
timeoutMs;
|
|
47
|
+
/** Discovery-cached end-session endpoint; failures are retried per call. */
|
|
48
|
+
endpointCache = null;
|
|
49
|
+
constructor(opts) {
|
|
50
|
+
this.opts = opts;
|
|
51
|
+
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
52
|
+
this.timeoutMs = opts.discoveryTimeoutMs ?? 3_000;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The hub end-session URL for this user's CURRENT hub session, or null
|
|
56
|
+
* when no usable hub leg exists (no `antasphere` account row, no stored
|
|
57
|
+
* id_token, a sid-less pre-flip id_token, or an unreachable/endpoint-less
|
|
58
|
+
* discovery document). The caller revokes the local session and clears
|
|
59
|
+
* the hint EITHER WAY — null only means the browser skips the hub bounce.
|
|
60
|
+
*/
|
|
61
|
+
async endSessionUrl(userId) {
|
|
62
|
+
try {
|
|
63
|
+
const idToken = await this.readIdToken(userId);
|
|
64
|
+
if (!idToken)
|
|
65
|
+
return null;
|
|
66
|
+
// decode (NOT verify) — we only need the sid presence check; the hub
|
|
67
|
+
// verifies the hint's signature itself on presentation.
|
|
68
|
+
let sid;
|
|
69
|
+
try {
|
|
70
|
+
sid = decodeJwt(idToken).sid;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null; // not a JWT — nothing presentable
|
|
74
|
+
}
|
|
75
|
+
if (typeof sid !== 'string' || !sid) {
|
|
76
|
+
// Pre-flip id_token (rollout caveat): the hub would hard-fail the
|
|
77
|
+
// hint, so degrade to a local-only signout until the next login
|
|
78
|
+
// stores a sid-carrying token.
|
|
79
|
+
this.opts.logger.info({ userId }, 'hub logout: stored id_token carries no sid — local signout only');
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const endpoint = await this.endSessionEndpoint();
|
|
83
|
+
if (!endpoint)
|
|
84
|
+
return null;
|
|
85
|
+
const url = new URL(endpoint);
|
|
86
|
+
url.searchParams.set('id_token_hint', idToken);
|
|
87
|
+
url.searchParams.set('post_logout_redirect_uri', postLogoutRedirectUri(this.opts.publicBaseUrl));
|
|
88
|
+
return url.toString();
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
this.opts.logger.warn({ err, userId }, 'hub logout: end-session URL build failed — local signout only');
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** The user's stored hub id_token, plaintext/encrypted-tolerant. */
|
|
96
|
+
async readIdToken(userId) {
|
|
97
|
+
const [row] = await this.opts.db
|
|
98
|
+
.select({ idToken: account.idToken })
|
|
99
|
+
.from(account)
|
|
100
|
+
.where(and(eq(account.userId, userId), eq(account.providerId, HUB_SSO_PROVIDER_ID)))
|
|
101
|
+
.limit(1);
|
|
102
|
+
const stored = row?.idToken;
|
|
103
|
+
if (!stored)
|
|
104
|
+
return null;
|
|
105
|
+
// A JWT (dots, base64url) never matches isLikelyEncrypted, so today's
|
|
106
|
+
// plaintext tokens pass straight through; a future encrypted value gets
|
|
107
|
+
// one decrypt attempt and fails soft.
|
|
108
|
+
if (!isLikelyEncrypted(stored))
|
|
109
|
+
return stored;
|
|
110
|
+
try {
|
|
111
|
+
return await symmetricDecrypt({ key: await this.opts.key(), data: stored });
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
this.opts.logger.warn({ err, userId }, 'hub logout: stored id_token failed to decrypt');
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Discovery read, cached on success; a failed fetch retries next call. */
|
|
119
|
+
async endSessionEndpoint() {
|
|
120
|
+
if (this.endpointCache)
|
|
121
|
+
return this.endpointCache;
|
|
122
|
+
const discoveryUrl = this.opts.issuerUrl.replace(/\/+$/, '') + '/.well-known/openid-configuration';
|
|
123
|
+
try {
|
|
124
|
+
const res = await this.fetchImpl(discoveryUrl, {
|
|
125
|
+
headers: { accept: 'application/json' },
|
|
126
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
127
|
+
});
|
|
128
|
+
if (!res.ok) {
|
|
129
|
+
this.opts.logger.warn({ status: res.status }, 'hub logout: discovery answered non-2xx');
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const doc = (await res.json());
|
|
133
|
+
const endpoint = doc.end_session_endpoint;
|
|
134
|
+
if (typeof endpoint !== 'string' || !endpoint) {
|
|
135
|
+
// A hub predating the end-session flip advertises none — degrade.
|
|
136
|
+
this.opts.logger.info('hub logout: discovery carries no end_session_endpoint — local signout only');
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
this.endpointCache = endpoint;
|
|
140
|
+
return endpoint;
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
this.opts.logger.warn({ err }, 'hub logout: discovery unreachable — local signout only');
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The Set-Cookie header value that CLEARS the shared SSO hint cookie —
|
|
150
|
+
* mirroring the hub-set attributes exactly (cross-repo contract: Domain on
|
|
151
|
+
* the shared parent, Path=/, SameSite=Lax, Secure on https, NOT HttpOnly)
|
|
152
|
+
* so the browser matches and expires the hub's cookie rather than minting a
|
|
153
|
+
* host-only shadow. Max-Age=0 is the deletion.
|
|
154
|
+
*/
|
|
155
|
+
export function hintCookieClearHeader(opts) {
|
|
156
|
+
return `${opts.name}=; Max-Age=0; Domain=${opts.domain}; Path=/; SameSite=Lax${opts.secure ? '; Secure' : ''}`;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=hub-logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-logout.js","sourceRoot":"","sources":["../../src/identity/hub-logout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type Db, type DbConn, type WorkspaceRole } from '@antasphere/chassis-db';
|
|
2
|
+
/**
|
|
3
|
+
* The ONE hub-org projection primitive (internal/federation.md, ADR 015/018):
|
|
4
|
+
* extracted verbatim from `HubSsoService.project()` so the SSO login path
|
|
5
|
+
* and the H4 reconciler (`hub-reconcile.ts`) write byte-identical rows.
|
|
6
|
+
*
|
|
7
|
+
* Lazy projection: the hub org becomes a local workspace on first use
|
|
8
|
+
* (`centralAccountId` = the hub org id), then a membership upsert
|
|
9
|
+
* re-asserts (role, origin='hub', active) on EVERY call — reactivating a
|
|
10
|
+
* deactivated row and following hub role changes (D11). Deliberately NOT
|
|
11
|
+
* WorkspaceService.create: the projection needs ON CONFLICT semantics on
|
|
12
|
+
* the 0021 unique partial index, the hub's role (not owner), and
|
|
13
|
+
* origin='hub'.
|
|
14
|
+
*
|
|
15
|
+
* Throws raw on any database failure — each caller owns its error posture
|
|
16
|
+
* (the login path maps to the fail-closed `sso_projection_failed`; the
|
|
17
|
+
* reconciler logs and continues with the next entry).
|
|
18
|
+
*/
|
|
19
|
+
export interface OrgMembershipProjection {
|
|
20
|
+
localUserId: string;
|
|
21
|
+
/** The hub org id (uuid, validated upstream) — never a local workspace id. */
|
|
22
|
+
hubWorkspaceId: string;
|
|
23
|
+
/** Display name (H1); null keeps the current name (or mints the placeholder). */
|
|
24
|
+
hubWorkspaceName: string | null;
|
|
25
|
+
/** The hub org role, verbatim (D11) — validated upstream, never invented here. */
|
|
26
|
+
role: WorkspaceRole;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The name a hub org goes by when the hub sends none (an older hub, a blank
|
|
30
|
+
* name): recognizable, and self-healing wherever the next hub read carries
|
|
31
|
+
* the real one. The projection's H1 fallback and the refusal page's hint.
|
|
32
|
+
*/
|
|
33
|
+
export declare function placeholderWorkspaceName(hubOrgId: string): string;
|
|
34
|
+
export declare function projectOrgMembership(db: Db, projection: OrgMembershipProjection): Promise<{
|
|
35
|
+
workspaceId: string;
|
|
36
|
+
memberId: string;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* The person's team seats in ONE projected workspace, as the hub asserted
|
|
40
|
+
* them in this pass (`GET /orgs`' per-org `teams`: the caller's own, never
|
|
41
|
+
* the team's roster). Upserts each team by its hub id (slug and name follow
|
|
42
|
+
* the hub; `updated_at` moves only when one of them changed), inserts the
|
|
43
|
+
* missing seats, and deletes this membership's seats whose team the hub no
|
|
44
|
+
* longer lists. Both the login pass and the periodic pass come through here,
|
|
45
|
+
* so they write byte-identical rows.
|
|
46
|
+
*
|
|
47
|
+
* Run inside one transaction per org by the caller. Throws raw, like
|
|
48
|
+
* `projectOrgMembership`. A team id already projected into ANOTHER workspace
|
|
49
|
+
* (a hub id cannot move between organizations; only a corrupt answer could
|
|
50
|
+
* say so) is left where it is and not seated here.
|
|
51
|
+
*/
|
|
52
|
+
export interface TeamSeatsProjection {
|
|
53
|
+
/** The projected workspace (from `projectOrgMembership`). */
|
|
54
|
+
workspaceId: string;
|
|
55
|
+
/** The person's membership row in it — the seat rides on it. */
|
|
56
|
+
memberId: string;
|
|
57
|
+
teams: ReadonlyArray<{
|
|
58
|
+
id: string;
|
|
59
|
+
slug: string;
|
|
60
|
+
name: string;
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
export declare function projectTeamSeats(db: DbConn, projection: TeamSeatsProjection): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* The organization's teams in ONE projected workspace, as the hub's `GET
|
|
66
|
+
* /teams` lists them (PRDCT-2813). This is the organization's DEFINITIVE
|
|
67
|
+
* list as one member reads it: every team of the org, whether anyone who
|
|
68
|
+
* signed in here sits in it or not, so a team can be added to a project
|
|
69
|
+
* before any of its members arrived. The rosters stay the seats of the
|
|
70
|
+
* people who signed in (`projectTeamSeats`); this touches no seat itself.
|
|
71
|
+
*
|
|
72
|
+
* Upserts every listed team by its hub id (slug and name follow the hub,
|
|
73
|
+
* `updated_at` moves on drift only, never across organizations: the same
|
|
74
|
+
* rule as `projectTeamSeats`), then, when `complete`, DELETES this
|
|
75
|
+
* workspace's projected teams (`hub_team_id IS NOT NULL`) the list does not
|
|
76
|
+
* name: a team deleted at the hub goes, and the foreign keys' cascade takes
|
|
77
|
+
* its seats and its project entries with it. A local team (`hub_team_id IS
|
|
78
|
+
* NULL`) is never touched. An incomplete list (the drain's page cap) only
|
|
79
|
+
* upserts.
|
|
80
|
+
*
|
|
81
|
+
* Run inside one transaction by the caller. Throws raw.
|
|
82
|
+
*/
|
|
83
|
+
export interface OrgTeamsProjection {
|
|
84
|
+
/** The projected workspace (from `projectOrgMembership`). */
|
|
85
|
+
workspaceId: string;
|
|
86
|
+
teams: ReadonlyArray<{
|
|
87
|
+
id: string;
|
|
88
|
+
slug: string;
|
|
89
|
+
name: string;
|
|
90
|
+
}>;
|
|
91
|
+
/** False when the list is a prefix: nothing is deleted then. Default true. */
|
|
92
|
+
complete?: boolean | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* When the read of the list BEGAN. A projected team created here after that
|
|
95
|
+
* moment (another replica's pass, another person's seat) is not in this
|
|
96
|
+
* list because it is newer than the read, not because the hub dropped it:
|
|
97
|
+
* the delete leaves it alone (verifier round 1, F5). Default: now, which
|
|
98
|
+
* deletes nothing newer than the call.
|
|
99
|
+
*/
|
|
100
|
+
readAt?: Date | undefined;
|
|
101
|
+
}
|
|
102
|
+
export declare function projectOrgTeams(db: DbConn, projection: OrgTeamsProjection): Promise<void>;
|
|
103
|
+
//# sourceMappingURL=hub-projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-projection.d.ts","sourceRoot":"","sources":["../../src/identity/hub-projection.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,EAAE,EACP,KAAK,MAAM,EACX,KAAK,aAAa,EACnB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kFAAkF;IAClF,IAAI,EAAE,aAAa,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,EAAE,EACN,UAAU,EAAE,uBAAuB,GAClC,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmDpD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDjG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CAC3B;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC/F"}
|