@runeditrun/rer 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +154 -2
- package/bin/rer.mjs +3 -0
- package/cli/README.md +243 -0
- package/cli/adapter.mjs +10 -0
- package/cli/ai-local.mjs +65 -0
- package/cli/archive-path.mjs +52 -0
- package/cli/archive-vite.mjs +55 -0
- package/cli/archive.mjs +8 -0
- package/cli/auth-copy.mjs +80 -0
- package/cli/bounded.mjs +70 -0
- package/cli/cache-doctor.mjs +63 -0
- package/cli/container-transfer.mjs +35 -0
- package/cli/container.mjs +62 -0
- package/cli/contract.mjs +25 -0
- package/cli/dependencies.mjs +45 -0
- package/cli/dependency-approval.mjs +33 -0
- package/cli/dependency-doctor.mjs +34 -0
- package/cli/dependency-input.mjs +43 -0
- package/cli/deploy-config.mjs +118 -0
- package/cli/deploy.mjs +37 -0
- package/cli/dev-identity.mjs +80 -0
- package/cli/errors.mjs +14 -0
- package/cli/export.mjs +42 -0
- package/cli/font-cache.mjs +29 -0
- package/cli/hosted-output.mjs +110 -0
- package/cli/independent.mjs +29 -0
- package/cli/inspect.mjs +174 -0
- package/cli/jsonc.mjs +111 -0
- package/cli/lifecycle.mjs +148 -0
- package/cli/local.mjs +100 -0
- package/cli/main.mjs +57 -0
- package/cli/managed-server.mjs +143 -0
- package/cli/node-runtime-cache.mjs +68 -0
- package/cli/node-runtime-discovery.mjs +56 -0
- package/cli/node-runtime-download.mjs +59 -0
- package/cli/node-runtime.mjs +50 -0
- package/cli/output.mjs +22 -0
- package/cli/parse.mjs +77 -0
- package/cli/port.mjs +52 -0
- package/cli/portability-worker.mjs +56 -0
- package/cli/portability.mjs +66 -0
- package/cli/preview-cache.mjs +99 -0
- package/cli/project.mjs +48 -0
- package/cli/release.mjs +24 -0
- package/cli/runtime-front-door.mjs +46 -0
- package/cli/scheduled.mjs +81 -0
- package/cli/server-activity.mjs +12 -0
- package/cli/server-proxy.mjs +93 -0
- package/cli/server-registry.mjs +177 -0
- package/cli/server-resources.mjs +22 -0
- package/cli/settings.mjs +15 -0
- package/cli/setup-answers.mjs +29 -0
- package/cli/setup-input.mjs +113 -0
- package/cli/shared.mjs +7 -0
- package/cli/transfers.mjs +33 -0
- package/cli/version.mjs +36 -0
- package/composition/git-source.mjs +111 -0
- package/edition-authoring/files.mjs +81 -0
- package/edition-authoring/manifest.mjs +65 -0
- package/global/README.md +155 -0
- package/global/checkout.mjs +72 -0
- package/global/cli/command.mjs +13 -0
- package/global/cli/install.mjs +75 -0
- package/global/cli/layout.mjs +45 -0
- package/global/cli/status.mjs +54 -0
- package/global/cli/uninstall.mjs +36 -0
- package/global/delegate.mjs +13 -0
- package/global/launch/capacity.mjs +72 -0
- package/global/launch/dev.mjs +129 -0
- package/global/launch/facts.mjs +93 -0
- package/global/launch/failure.mjs +5 -0
- package/global/launch/local-defaults.mjs +4 -0
- package/global/launch/prepare.mjs +61 -0
- package/global/launch/render.mjs +44 -0
- package/global/launch/state.mjs +97 -0
- package/global/launch/steps.mjs +81 -0
- package/global/launch/worker.mjs +43 -0
- package/global/list.mjs +59 -0
- package/global/main.mjs +53 -0
- package/global/package-info.mjs +38 -0
- package/global/registry.mjs +24 -0
- package/global/rer.mjs +29 -0
- package/global/update/archive.mjs +89 -0
- package/global/update/command.mjs +90 -0
- package/global/update/latest.mjs +6 -0
- package/global/update/notice.mjs +58 -0
- package/global/update/registry.mjs +47 -0
- package/global/version.mjs +29 -0
- package/install.sh +144 -0
- package/modules/ai/LICENSE +21 -0
- package/modules/ai/README.md +90 -0
- package/modules/ai/client.mjs +71 -0
- package/modules/ai/config.mjs +39 -0
- package/modules/ai/dev/index.d.mts +6 -0
- package/modules/ai/dev/index.mjs +64 -0
- package/modules/ai/dev/outbox.mjs +33 -0
- package/modules/ai/errors.mjs +28 -0
- package/modules/ai/fake/index.d.mts +3 -0
- package/modules/ai/fake/index.mjs +82 -0
- package/modules/ai/index.d.mts +10 -0
- package/modules/ai/index.mjs +3 -0
- package/modules/ai/package.json +6 -0
- package/modules/container/README.md +156 -0
- package/modules/container/adapters/d1.mjs +34 -0
- package/modules/container/adapters/egress.mjs +40 -0
- package/modules/container/adapters/jobs.mjs +47 -0
- package/modules/container/adapters/objects.mjs +84 -0
- package/modules/container/adapters/retained.mjs +14 -0
- package/modules/container/adapters/sqlite-error.mjs +7 -0
- package/modules/container/adapters/sqlite-worker.mjs +26 -0
- package/modules/container/assets.mjs +14 -0
- package/modules/container/bindings.mjs +1 -0
- package/modules/container/boundary.mjs +38 -0
- package/modules/container/build.mjs +65 -0
- package/modules/container/deploy-input.mjs +17 -0
- package/modules/container/entrypoint.sh +5 -0
- package/modules/container/host.mjs +43 -0
- package/modules/container/ingress.mjs +23 -0
- package/modules/container/lock.mjs +11 -0
- package/modules/container/migrations.mjs +16 -0
- package/modules/container/node-engine.mjs +39 -0
- package/modules/container/portability-schema.mjs +14 -0
- package/modules/container/remote/deploy.py +76 -0
- package/modules/container/remote/files.py +275 -0
- package/modules/container/remote/resources.py +157 -0
- package/modules/container/runner.mjs +10 -0
- package/modules/container/ssh-plan.mjs +44 -0
- package/modules/container/ssh.mjs +63 -0
- package/modules/container/transfer.mjs +27 -0
- package/modules/container/worker-engine.mjs +32 -0
- package/modules/dependencies/README.md +211 -0
- package/modules/dependencies/approval.mjs +51 -0
- package/modules/dependencies/auth.mjs +40 -0
- package/modules/dependencies/authority.mjs +14 -0
- package/modules/dependencies/crypto.mjs +13 -0
- package/modules/dependencies/index.d.mts +23 -0
- package/modules/dependencies/index.mjs +113 -0
- package/modules/dependencies/logout.mjs +42 -0
- package/modules/dependencies/native.mjs +9 -0
- package/modules/dependencies/protocol.mjs +23 -0
- package/modules/dependencies/schema/approval-attempt.sql +3 -0
- package/modules/dependencies/schema/dependencies.sql +15 -0
- package/modules/dependencies/store.mjs +19 -0
- package/modules/deploy-kit/README.md +308 -0
- package/modules/deploy-kit/artifact.mjs +47 -0
- package/modules/deploy-kit/build-modules.mjs +21 -0
- package/modules/deploy-kit/check.mjs +13 -0
- package/modules/deploy-kit/cli.mjs +50 -0
- package/modules/deploy-kit/container-config.mjs +8 -0
- package/modules/deploy-kit/default-stage.mjs +14 -0
- package/modules/deploy-kit/deploy.mjs +80 -0
- package/modules/deploy-kit/health-request.mjs +86 -0
- package/modules/deploy-kit/health.mjs +99 -0
- package/modules/deploy-kit/index.mjs +12 -0
- package/modules/deploy-kit/local-only.mjs +10 -0
- package/modules/deploy-kit/migrations.mjs +48 -0
- package/modules/deploy-kit/owner-cli.mjs +63 -0
- package/modules/deploy-kit/owner.mjs +297 -0
- package/modules/deploy-kit/prepare.mjs +62 -0
- package/modules/deploy-kit/provider.mjs +99 -0
- package/modules/deploy-kit/queue-consumer.mjs +10 -0
- package/modules/deploy-kit/receipt.mjs +16 -0
- package/modules/deploy-kit/render.mjs +47 -0
- package/modules/deploy-kit/resources.mjs +106 -0
- package/modules/deploy-kit/service-grants.mjs +25 -0
- package/modules/deploy-kit/sql-safety.mjs +94 -0
- package/modules/deploy-kit/sql-tokens.mjs +49 -0
- package/modules/deploy-kit/teardown.mjs +100 -0
- package/modules/deploy-kit/templates.mjs +31 -0
- package/modules/deploy-kit/validation.mjs +58 -0
- package/modules/deploy-kit/worker.mjs +105 -0
- package/modules/erasure/CONTRACT.md +90 -0
- package/modules/erasure/README.md +221 -0
- package/modules/erasure/audit.mjs +23 -0
- package/modules/erasure/blobs.mjs +45 -0
- package/modules/erasure/cascade.mjs +91 -0
- package/modules/erasure/completion.mjs +62 -0
- package/modules/erasure/declaration.mjs +51 -0
- package/modules/erasure/fence.mjs +19 -0
- package/modules/erasure/index.d.mts +62 -0
- package/modules/erasure/index.mjs +85 -0
- package/modules/erasure/order.mjs +56 -0
- package/modules/erasure/plan.mjs +121 -0
- package/modules/erasure/rate.mjs +57 -0
- package/modules/erasure/receipt.mjs +32 -0
- package/modules/erasure/root.mjs +21 -0
- package/modules/erasure/schema/erasure-rates.sql +5 -0
- package/modules/erasure/schema/erasure.sql +27 -0
- package/modules/erasure/scope.mjs +29 -0
- package/modules/erasure/sql.mjs +23 -0
- package/modules/erasure/verification.mjs +69 -0
- package/modules/integrations/core/CONTRACT.md +50 -0
- package/modules/integrations/core/README.md +27 -0
- package/modules/integrations/core/crypto.ts +19 -0
- package/modules/integrations/core/database.ts +21 -0
- package/modules/integrations/core/delivery.ts +97 -0
- package/modules/integrations/core/destination.ts +13 -0
- package/modules/integrations/core/index.ts +7 -0
- package/modules/integrations/core/node-transport.ts +72 -0
- package/modules/integrations/core/public-address.ts +57 -0
- package/modules/integrations/core/repository.ts +74 -0
- package/modules/integrations/core/schema.sql +36 -0
- package/modules/integrations/core/sqlite.ts +20 -0
- package/modules/integrations/core/timestamped-body-signature.ts +38 -0
- package/modules/integrations/core/tokens.ts +71 -0
- package/modules/integrations/core/types.ts +52 -0
- package/modules/integrations/core/upgrade-token-usage-context.sql +5 -0
- package/modules/integrations/core/webhook.ts +73 -0
- package/modules/job-queue/README.md +173 -0
- package/modules/job-queue/cli.mjs +64 -0
- package/modules/job-queue/command.mjs +64 -0
- package/modules/job-queue/config.mjs +48 -0
- package/modules/job-queue/identity.mjs +48 -0
- package/modules/job-queue/index.mjs +135 -0
- package/modules/job-queue/membership.mjs +30 -0
- package/modules/job-queue/module.json +10 -0
- package/modules/job-queue/policy.mjs +29 -0
- package/modules/job-queue/processes.mjs +80 -0
- package/modules/job-queue/resources.mjs +37 -0
- package/modules/job-queue/storage.mjs +64 -0
- package/modules/job-queue/supervisor.mjs +30 -0
- package/modules/job-queue/waiting.mjs +16 -0
- package/modules/mail/LICENSE +21 -0
- package/modules/mail/README.md +336 -0
- package/modules/mail/doctor.mjs +81 -0
- package/modules/mail/index.mjs +8 -0
- package/modules/mail/live-proof/README.md +113 -0
- package/modules/mail/live-proof/run.mjs +55 -0
- package/modules/mail/package.json +19 -0
- package/modules/mail/pnpm-lock.yaml +537 -0
- package/modules/mail/src/adapters/cloudflare/README.md +84 -0
- package/modules/mail/src/adapters/cloudflare/headers.mjs +63 -0
- package/modules/mail/src/adapters/cloudflare/index.mjs +47 -0
- package/modules/mail/src/adapters/cloudflare/response.mjs +99 -0
- package/modules/mail/src/adapters/messaging/README.md +92 -0
- package/modules/mail/src/adapters/messaging/contract.mjs +66 -0
- package/modules/mail/src/adapters/messaging/event-schema.mjs +28 -0
- package/modules/mail/src/adapters/messaging/index.mjs +61 -0
- package/modules/mail/src/adapters/messaging/jws.mjs +68 -0
- package/modules/mail/src/adapters/messaging/request.mjs +52 -0
- package/modules/mail/src/adapters/messaging/serialize.mjs +21 -0
- package/modules/mail/src/adapters/resend/README.md +120 -0
- package/modules/mail/src/adapters/resend/index.mjs +22 -0
- package/modules/mail/src/adapters/resend/response.mjs +54 -0
- package/modules/mail/src/adapters/resend/serialize.mjs +46 -0
- package/modules/mail/src/adapters/resend/size.mjs +99 -0
- package/modules/mail/src/adapters/sendgrid/README.md +75 -0
- package/modules/mail/src/adapters/sendgrid/index.mjs +35 -0
- package/modules/mail/src/adapters/sendgrid/serialize.mjs +104 -0
- package/modules/mail/src/core/errors.mjs +9 -0
- package/modules/mail/src/core/transport.mjs +33 -0
- package/modules/mail/src/core/validation.mjs +172 -0
- package/modules/mail/src/development/index.d.mts +37 -0
- package/modules/mail/src/development/index.mjs +35 -0
- package/modules/mail/src/development/outbox.mjs +78 -0
- package/modules/mail/src/development/schema.sql +9 -0
- package/modules/mail/src/development/viewer-html.mjs +42 -0
- package/modules/mail/src/development/viewer.mjs +65 -0
- package/modules/mail/src/doctor/README.md +107 -0
- package/modules/mail/src/doctor/common.mjs +64 -0
- package/modules/mail/src/doctor/dns.mjs +102 -0
- package/modules/mail/src/doctor/index.mjs +28 -0
- package/modules/mail/src/doctor/probe.mjs +66 -0
- package/modules/mail/src/doctor/receive.mjs +81 -0
- package/modules/mail/src/doctor/records.mjs +45 -0
- package/modules/mail/src/events/README.md +268 -0
- package/modules/mail/src/events/d1-schema.sql +25 -0
- package/modules/mail/src/events/d1-storage.mjs +76 -0
- package/modules/mail/src/events/index.mjs +3 -0
- package/modules/mail/src/events/intake.mjs +201 -0
- package/modules/mail/src/events/storage.mjs +52 -0
- package/modules/mail/src/events/verify.mjs +92 -0
- package/modules/mail/src/inbound/README.md +137 -0
- package/modules/mail/src/inbound/bytes.mjs +91 -0
- package/modules/mail/src/inbound/headers.mjs +143 -0
- package/modules/mail/src/inbound/index.mjs +29 -0
- package/modules/mail/src/inbound/mime.mjs +36 -0
- package/modules/mail/src/inbound/multipart.mjs +27 -0
- package/modules/mail/src/inbound/resend-attachment.mjs +43 -0
- package/modules/mail/src/inbound/resend.mjs +54 -0
- package/modules/mail/src/inbound/sendgrid.mjs +60 -0
- package/modules/mail/src/sender.mjs +33 -0
- package/modules/mail/src/sendgrid-email/index.d.mts +33 -0
- package/modules/mail/src/sendgrid-email/index.mjs +86 -0
- package/modules/mail/src/unsubscribe/index.mjs +142 -0
- package/modules/operator-auth/README.md +599 -0
- package/modules/operator-auth/accounts/adapter.mjs +41 -0
- package/modules/operator-auth/accounts/admin.mjs +159 -0
- package/modules/operator-auth/accounts/credentials.mjs +27 -0
- package/modules/operator-auth/accounts/editor-owner.mjs +13 -0
- package/modules/operator-auth/accounts/index.d.mts +9 -0
- package/modules/operator-auth/accounts/index.mjs +9 -0
- package/modules/operator-auth/accounts/native.d.mts +38 -0
- package/modules/operator-auth/accounts/native.mjs +171 -0
- package/modules/operator-auth/accounts/oa.mjs +90 -0
- package/modules/operator-auth/accounts/provider-control.mjs +50 -0
- package/modules/operator-auth/accounts/provider-pin.mjs +14 -0
- package/modules/operator-auth/accounts/roles.mjs +26 -0
- package/modules/operator-auth/accounts/schema/dependency-setup.sql +17 -0
- package/modules/operator-auth/accounts/schema/dev-sessions.sql +22 -0
- package/modules/operator-auth/accounts/schema/domain-rules-step-up.sql +20 -0
- package/modules/operator-auth/accounts/schema/invitation-cancellation.sql +17 -0
- package/modules/operator-auth/accounts/schema/operator-auth.sql +142 -0
- package/modules/operator-auth/accounts/schema/provider-control.sql +30 -0
- package/modules/operator-auth/accounts/schema/provider-pins.sql +6 -0
- package/modules/operator-auth/accounts/settings-page.mjs +4 -0
- package/modules/operator-auth/accounts/store.mjs +246 -0
- package/modules/operator-auth/branding.mjs +66 -0
- package/modules/operator-auth/config.mjs +35 -0
- package/modules/operator-auth/conformance/README.md +81 -0
- package/modules/operator-auth/conformance/index.d.mts +43 -0
- package/modules/operator-auth/conformance/index.mjs +300 -0
- package/modules/operator-auth/conformance/presentation.mjs +57 -0
- package/modules/operator-auth/crypto.mjs +17 -0
- package/modules/operator-auth/dev/badge.mjs +62 -0
- package/modules/operator-auth/dev/index.d.mts +14 -0
- package/modules/operator-auth/dev/index.mjs +137 -0
- package/modules/operator-auth/dev/personas.mjs +17 -0
- package/modules/operator-auth/dev/sign-in-as.d.mts +3 -0
- package/modules/operator-auth/dev/sign-in-as.mjs +25 -0
- package/modules/operator-auth/dev-admission.d.mts +4 -0
- package/modules/operator-auth/dev-admission.mjs +10 -0
- package/modules/operator-auth/dev-gate.mjs +86 -0
- package/modules/operator-auth/docs/README.md +78 -0
- package/modules/operator-auth/docs/native-accounts.md +313 -0
- package/modules/operator-auth/docs/tanstack-start.md +218 -0
- package/modules/operator-auth/docs/vite-spa.md +144 -0
- package/modules/operator-auth/errors.mjs +9 -0
- package/modules/operator-auth/http.mjs +42 -0
- package/modules/operator-auth/identity/admission.mjs +105 -0
- package/modules/operator-auth/identity/after-confirm.mjs +18 -0
- package/modules/operator-auth/identity/email-link/confirmation.mjs +14 -0
- package/modules/operator-auth/identity/email-link/index.d.mts +48 -0
- package/modules/operator-auth/identity/email-link/index.mjs +128 -0
- package/modules/operator-auth/identity/email-link/message.mjs +16 -0
- package/modules/operator-auth/identity/index.d.mts +7 -0
- package/modules/operator-auth/identity/index.mjs +5 -0
- package/modules/operator-auth/identity/oidc/README.md +171 -0
- package/modules/operator-auth/identity/oidc/client-auth.mjs +16 -0
- package/modules/operator-auth/identity/oidc/config.mjs +102 -0
- package/modules/operator-auth/identity/oidc/crypto.mjs +29 -0
- package/modules/operator-auth/identity/oidc/errors.mjs +5 -0
- package/modules/operator-auth/identity/oidc/index.d.mts +33 -0
- package/modules/operator-auth/identity/oidc/index.mjs +287 -0
- package/modules/operator-auth/identity/oidc/jwt.mjs +102 -0
- package/modules/operator-auth/identity/oidc/network.mjs +93 -0
- package/modules/operator-auth/identity/oidc/presets.mjs +26 -0
- package/modules/operator-auth/identity/oidc/schema/oidc.sql +50 -0
- package/modules/operator-auth/identity/oidc/store.mjs +68 -0
- package/modules/operator-auth/identity/principal.mjs +28 -0
- package/modules/operator-auth/index.d.mts +223 -0
- package/modules/operator-auth/index.mjs +11 -0
- package/modules/operator-auth/input.mjs +55 -0
- package/modules/operator-auth/local-cli.mjs +16 -0
- package/modules/operator-auth/page.mjs +34 -0
- package/modules/operator-auth/policy.mjs +20 -0
- package/modules/operator-auth/routes.mjs +91 -0
- package/modules/operator-auth/service.mjs +152 -0
- package/modules/operator-auth/signin/index.mjs +67 -0
- package/modules/owner-claim/README.md +137 -0
- package/modules/owner-claim/idp.mjs +40 -0
- package/modules/owner-claim/index.d.mts +27 -0
- package/modules/owner-claim/index.mjs +39 -0
- package/modules/owner-claim/page.mjs +11 -0
- package/modules/owner-claim/schema/idp.sql +4 -0
- package/modules/owner-claim/schema/owner-claim.sql +18 -0
- package/modules/owner-claim/transaction.mjs +87 -0
- package/modules/owner-claim/verify.mjs +80 -0
- package/modules/portability/README.md +444 -0
- package/modules/portability/bridge.mjs +102 -0
- package/modules/portability/catalogue.mjs +118 -0
- package/modules/portability/cli-options.mjs +38 -0
- package/modules/portability/cli.mjs +56 -0
- package/modules/portability/copies.mjs +68 -0
- package/modules/portability/dev-data.mjs +32 -0
- package/modules/portability/diff.mjs +33 -0
- package/modules/portability/export.mjs +129 -0
- package/modules/portability/files.mjs +65 -0
- package/modules/portability/format.mjs +122 -0
- package/modules/portability/generated.mjs +60 -0
- package/modules/portability/hash.mjs +79 -0
- package/modules/portability/http.mjs +42 -0
- package/modules/portability/import.mjs +185 -0
- package/modules/portability/index.d.mts +58 -0
- package/modules/portability/index.mjs +22 -0
- package/modules/portability/journal.mjs +49 -0
- package/modules/portability/legacy-files.mjs +43 -0
- package/modules/portability/legacy-safety.mjs +27 -0
- package/modules/portability/lines.mjs +36 -0
- package/modules/portability/migration-history.mjs +18 -0
- package/modules/portability/migration-statements.mjs +23 -0
- package/modules/portability/payloads.mjs +52 -0
- package/modules/portability/provider-data.mjs +25 -0
- package/modules/portability/restore-safety.mjs +30 -0
- package/modules/portability/schema/portability.sql +25 -0
- package/modules/portability/storage.mjs +49 -0
- package/modules/release/README.md +212 -0
- package/modules/release/catalogue.mjs +79 -0
- package/modules/release/export.mjs +51 -0
- package/modules/release/produce.mjs +136 -0
- package/modules/release/public-child-transport-state.cjs +29 -0
- package/modules/release/public-child.mjs +71 -0
- package/modules/release/public-git.mjs +21 -0
- package/modules/release/publication-scan.mjs +22 -0
- package/modules/release/publish-handoff.mjs +37 -0
- package/modules/release/source.mjs +71 -0
- package/modules/release/test-summary.mjs +50 -0
- package/onboarding/README.md +163 -0
- package/onboarding/activity.mjs +31 -0
- package/onboarding/adapter-auth.mjs +88 -0
- package/onboarding/adapter-hosting.mjs +35 -0
- package/onboarding/adapter-labels.mjs +13 -0
- package/onboarding/adapter-settings.mjs +129 -0
- package/onboarding/adapter.mjs +238 -0
- package/onboarding/agent-handoff.mjs +9 -0
- package/onboarding/app-processes.mjs +21 -0
- package/onboarding/archive-output.d.mts +3 -0
- package/onboarding/archive-output.mjs +54 -0
- package/onboarding/command-custody.mjs +24 -0
- package/onboarding/command-recovery.mjs +8 -0
- package/onboarding/commands.mjs +166 -0
- package/onboarding/dependency-approval.mjs +36 -0
- package/onboarding/dev-identity.mjs +23 -0
- package/onboarding/email.mjs +24 -0
- package/onboarding/failures.mjs +42 -0
- package/onboarding/input-limits.mjs +7 -0
- package/onboarding/installer-inputs.mjs +24 -0
- package/onboarding/launcher.mjs +34 -0
- package/onboarding/local/build-files.mjs +64 -0
- package/onboarding/local/build.mjs +49 -0
- package/onboarding/local/environment.mjs +30 -0
- package/onboarding/local/files.mjs +35 -0
- package/onboarding/local/operation.mjs +166 -0
- package/onboarding/local/owner.mjs +34 -0
- package/onboarding/local/process-table.mjs +114 -0
- package/onboarding/local/recovery.mjs +79 -0
- package/onboarding/local/state.mjs +31 -0
- package/onboarding/metadata.mjs +7 -0
- package/onboarding/operator-install.mjs +45 -0
- package/onboarding/operator-installer.mjs +39 -0
- package/onboarding/operator-prerequisites.mjs +27 -0
- package/onboarding/package.json +6 -0
- package/onboarding/prefill.mjs +16 -0
- package/onboarding/private-files.mjs +97 -0
- package/onboarding/private.mjs +35 -0
- package/onboarding/process-cleanup.mjs +134 -0
- package/onboarding/process-diagnostics.mjs +91 -0
- package/onboarding/process-scope.mjs +118 -0
- package/onboarding/process-table.mjs +118 -0
- package/onboarding/process-tree.mjs +36 -0
- package/onboarding/production-modules.mjs +22 -0
- package/onboarding/production-options.mjs +40 -0
- package/onboarding/production-plan.mjs +8 -0
- package/onboarding/production-runtime.mjs +30 -0
- package/onboarding/project-vite.mjs +14 -0
- package/onboarding/providers.mjs +289 -0
- package/onboarding/queue.mjs +53 -0
- package/onboarding/queued-entry.mjs +34 -0
- package/onboarding/readiness.mjs +59 -0
- package/onboarding/redaction-jwt.mjs +32 -0
- package/onboarding/redaction.mjs +298 -0
- package/onboarding/schema/SETUP.schema.json +514 -0
- package/onboarding/schema/dependencies.mjs +35 -0
- package/onboarding/schema/json-schema.mjs +202 -0
- package/onboarding/schema/setup-validation.mjs +325 -0
- package/onboarding/secrets.mjs +21 -0
- package/onboarding/server.mjs +188 -0
- package/onboarding/setup-manifest.mjs +116 -0
- package/onboarding/state.mjs +46 -0
- package/onboarding/theme-files.mjs +37 -0
- package/onboarding/theme.mjs +118 -0
- package/onboarding/ui/activity.js +98 -0
- package/onboarding/ui/address-client.mjs +33 -0
- package/onboarding/ui/app.js +377 -0
- package/onboarding/ui/index.html +1 -0
- package/onboarding/ui/model-core.mjs +127 -0
- package/onboarding/ui/model.mjs +5 -0
- package/onboarding/ui/style.css +63 -0
- package/onboarding/ui/theme.css +37 -0
- package/onboarding/ui/workspace-art.js +30 -0
- package/onboarding/wrangler-auth.mjs +43 -0
- package/operations/README.md +246 -0
- package/operations/operations.mjs +168 -0
- package/operations/project-operations.mjs +157 -0
- package/package.json +11 -4
- package/scripts/publish-check/README.md +72 -0
- package/scripts/publish-check/checksum_maps.py +52 -0
- package/scripts/publish-check/cli.py +34 -0
- package/scripts/publish-check/command_arguments.py +51 -0
- package/scripts/publish-check/command_shell_quotes.py +41 -0
- package/scripts/publish-check/command_source_quotes.py +120 -0
- package/scripts/publish-check/content_identities.py +250 -0
- package/scripts/publish-check/copy_identities.py +41 -0
- package/scripts/publish-check/der.py +61 -0
- package/scripts/publish-check/destination.py +117 -0
- package/scripts/publish-check/env_values.py +74 -0
- package/scripts/publish-check/font_licence.py +23 -0
- package/scripts/publish-check/git_history.py +115 -0
- package/scripts/publish-check/github_refs.py +217 -0
- package/scripts/publish-check/gitleaks.py +72 -0
- package/scripts/publish-check/gitleaks.toml +40 -0
- package/scripts/publish-check/history_scan.py +176 -0
- package/scripts/publish-check/hit_location.py +18 -0
- package/scripts/publish-check/hygiene_config.py +42 -0
- package/scripts/publish-check/hygiene_text.py +11 -0
- package/scripts/publish-check/identity-recipe.mjs +54 -0
- package/scripts/publish-check/json_identity.py +79 -0
- package/scripts/publish-check/license-exceptions.json +11 -0
- package/scripts/publish-check/licenses.py +49 -0
- package/scripts/publish-check/policy.py +18 -0
- package/scripts/publish-check/project_identity.py +89 -0
- package/scripts/publish-check/provenance.py +91 -0
- package/scripts/publish-check/provenance_pins.py +33 -0
- package/scripts/publish-check/public_base.py +63 -0
- package/scripts/publish-check/public_hygiene.py +80 -0
- package/scripts/publish-check/publication-config.json +12 -0
- package/scripts/publish-check/publication_config.py +34 -0
- package/scripts/publish-check/report.py +52 -0
- package/scripts/publish-check/repository_commands.py +75 -0
- package/scripts/publish-check/repository_intent.py +52 -0
- package/scripts/publish-check/reviewed.py +41 -0
- package/scripts/publish-check/source_hashes.mjs +6 -0
- package/scripts/publish-check/tree_scan.py +88 -0
- package/scripts/publish-check/trust_bundle.py +87 -0
- package/scripts/publish-check.mjs +58 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Identity providers (OpenID Connect)
|
|
2
|
+
|
|
3
|
+
Server-only portable ESM using Fetch and WebCrypto. An app offers several
|
|
4
|
+
providers at once; each is pinned when linked and has its own routes. A
|
|
5
|
+
provider's output is a verified principal, which the admission (an accounts
|
|
6
|
+
adapter, `../admission.mjs`) turns into a session. Apply `schema/oidc.sql` as a
|
|
7
|
+
new numbered migration on the primary D1 database; every statement is
|
|
8
|
+
`IF NOT EXISTS`, so a store that had the earlier single-issuer adapter applies it
|
|
9
|
+
again as its next migration.
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { createOperatorAuth, createAuthHandler, createSignInHandler } from './modules/operator-auth/index.mjs';
|
|
13
|
+
const auth = createOperatorAuth({
|
|
14
|
+
db: env.DB, clientIp, mailSender,
|
|
15
|
+
// The runtime's fetch; on workerd use redirect 'manual' and refuse every 3xx.
|
|
16
|
+
fetch: providerFetch,
|
|
17
|
+
waitUntil: promise => execution.waitUntil(promise),
|
|
18
|
+
config: {
|
|
19
|
+
origin: 'https://app.example.com', OWNER_EMAIL: env.OWNER_EMAIL, SESSION_SECRET: env.SESSION_SECRET,
|
|
20
|
+
// Independent of SESSION_SECRET; at least 32 characters. Both are Worker secrets.
|
|
21
|
+
OIDC_TRANSACTION_SECRET: env.OIDC_TRANSACTION_SECRET,
|
|
22
|
+
providers: [
|
|
23
|
+
// The app's private key (a Worker secret) authenticates it: no shared secret exists.
|
|
24
|
+
{ id: 'auth', preset: 'auth', issuer: env.IDENTITY_PROVIDER, clientId: env.IDENTITY_CLIENT_ID, privateKey: JSON.parse(env.IDENTITY_PRIVATE_KEY) },
|
|
25
|
+
{ id: 'google', preset: 'google', clientId: env.GOOGLE_CLIENT_ID, tokenAuthMethod: 'client_secret_basic', clientSecret: env.GOOGLE_CLIENT_SECRET },
|
|
26
|
+
{ id: 'corp', preset: 'okta', issuer: 'https://corp.okta.com', clientId: env.OKTA_CLIENT_ID, privateKey: JSON.parse(env.OKTA_PRIVATE_KEY),
|
|
27
|
+
subjectEmails: { '00u1abcd': 'operator@corp.example' } },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
// GET /signin: "Continue with…" for each linked, reachable provider, then the email link.
|
|
32
|
+
const page = await createSignInHandler(auth)(request);
|
|
33
|
+
// /auth/oidc/<id>/start and /auth/oidc/<id>/callback, with the rest of /auth/*.
|
|
34
|
+
const response = page ?? await createAuthHandler(auth)(request);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Register each provider's own redirect URI: `${origin}/auth/oidc/<id>/callback`,
|
|
38
|
+
or an explicit same-origin `redirectUri` for an incumbent route. Both exported
|
|
39
|
+
HTTP handlers own that configured callback outside `/auth/oidc/` too. The old
|
|
40
|
+
default route is unowned after an override. Conflicting callbacks, foreign
|
|
41
|
+
origins, credentials, query and fragment components are refused.
|
|
42
|
+
`createOidcProviders({ admission, db, config, clientIp, fetch, clock, waitUntil })`
|
|
43
|
+
is the same client for a seed whose accounts adapter is its own.
|
|
44
|
+
|
|
45
|
+
## Presets
|
|
46
|
+
|
|
47
|
+
| Preset | Issuer | ID-token algorithms | Client authentication | Email trust |
|
|
48
|
+
| --- | --- | --- | --- | --- |
|
|
49
|
+
| `auth` (RER's Auth) | the install's exact HTTPS issuer | ES256 | `private_key_jwt` only | `email_verified` with `email_verified_at` in the last 30 days |
|
|
50
|
+
| `google` | `https://accounts.google.com` (fixed) | RS256 | `private_key_jwt` (default), `client_secret_basic`, `client_secret_post` | `email_verified`, and only for `@gmail.com` or tokens carrying a matching `hd` |
|
|
51
|
+
| `microsoft` | `https://login.microsoftonline.com/<tenant>/v2.0` from `tenant` (never `common`, `organizations` or `consumers`) | RS256 | as Google | never by email: only `subjectEmails` |
|
|
52
|
+
| `okta` | the organisation's exact HTTPS issuer | RS256 | as Google | never by email: only `subjectEmails` |
|
|
53
|
+
| `legacy-rs256` (explicit incumbent opt-in) | exact HTTPS issuer plus required exact `endpointPins` | RS256 | as Google | signed verified email; missing timestamp counts at sign-in, supplied timestamp must be current and valid |
|
|
54
|
+
|
|
55
|
+
`none` and every `HS*` algorithm are never accepted; a token whose `alg` is not
|
|
56
|
+
in its link's pin is refused before any key is looked up. GitHub is not an
|
|
57
|
+
OpenID provider and is not offered. Outside the explicit incumbent preset, other upstreams identify people only
|
|
58
|
+
through explicit `subjectEmails`, a trusted server map from the provider's `sub`
|
|
59
|
+
to an existing operator's email (never populated from a callback, a token email
|
|
60
|
+
or a request).
|
|
61
|
+
|
|
62
|
+
An `auth` provider must state, in discovery, the interface it serves:
|
|
63
|
+
`"rer_interfaces": ["oidc-provider@1.0.0"]`. One below the minimum
|
|
64
|
+
(`MINIMUM_PROVIDER_VERSION`, or a provider's higher `minimumVersion`) is refused
|
|
65
|
+
and nothing is pinned.
|
|
66
|
+
|
|
67
|
+
The incumbent preset requires `endpointPins: { authorization, token, jwks }`.
|
|
68
|
+
Discovery must match these exact HTTPS URLs; this does not loosen Okta,
|
|
69
|
+
Microsoft, Google or Auth trust. See [native adoption](../../docs/native-accounts.md)
|
|
70
|
+
for the full persistence and callback contract.
|
|
71
|
+
|
|
72
|
+
Callback overrides cannot occupy shared owner-claim, sign-in, auth, dependency
|
|
73
|
+
or dev routes, including `/auth/owner-claim`, `/auth/dev/enter`, `/__dev/key` and
|
|
74
|
+
`/__dev/personas`. These routes keep their original handlers; an incumbent path
|
|
75
|
+
such as `/api/auth/identity/callback` is supported with the same transaction
|
|
76
|
+
binding as the default callback.
|
|
77
|
+
|
|
78
|
+
## Linking and pinning
|
|
79
|
+
|
|
80
|
+
A provider is linked at its first use: discovery is fetched and the link pins
|
|
81
|
+
the issuer, client id, JWKS URI, authorization and token endpoints, the allowed
|
|
82
|
+
algorithms (the preset's, as advertised), the client authentication method, and
|
|
83
|
+
whether the provider sends RFC 9207 `iss`. Every later start fetches discovery
|
|
84
|
+
again and refuses (and hides the provider) if it no longer matches the pin; only
|
|
85
|
+
the owner's re-link changes a pin.
|
|
86
|
+
|
|
87
|
+
A changed configuration (for example a new `IDENTITY_PROVIDER` answer: another
|
|
88
|
+
issuer or client id) only records a **pending re-link**. Sign-in keeps using the
|
|
89
|
+
pinned provider; nothing is swapped in place. The owner completes the re-link at
|
|
90
|
+
`POST /auth/providers/relink {provider}`, or ends a link at
|
|
91
|
+
`POST /auth/providers/unlink {provider}`, each with a fresh step-up proof.
|
|
92
|
+
`GET /auth/providers` lists the pins and pending re-links. Completing a re-link
|
|
93
|
+
invalidates logins in flight. An unlinked provider stays unlinked even while its
|
|
94
|
+
configuration remains, until the owner re-links it.
|
|
95
|
+
|
|
96
|
+
## The protocol
|
|
97
|
+
|
|
98
|
+
`POST /auth/oidc/<id>/start` (exact Origin, matching Host, no cross-site Fetch
|
|
99
|
+
Metadata; JSON or a native form with `returnTo`, at most 4096 bytes) sends
|
|
100
|
+
`response_type=code`, `response_mode=query`, the preset's scope, independent
|
|
101
|
+
256-bit state and nonce, and mandatory S256 PKCE to the pinned authorization
|
|
102
|
+
endpoint, with a per-state browser-binding cookie (`__Host-oa_oidc_<state>`, or
|
|
103
|
+
`oa_oidc_local_<port>_<state>` on a loopback origin). State is keyed to its provider:
|
|
104
|
+
an authorization response for one provider presented at another provider's
|
|
105
|
+
redirect URI finds no transaction and is refused (the mix-up defence), and no
|
|
106
|
+
code reaches the wrong token endpoint.
|
|
107
|
+
|
|
108
|
+
`GET /auth/oidc/<id>/callback`, in order: exact route and method, no duplicate
|
|
109
|
+
parameters, state and its browser binding consume the durable transaction once
|
|
110
|
+
(`DELETE … RETURNING`, before any exchange), the transaction belongs to this
|
|
111
|
+
provider, RFC 9207: `iss` must equal the pinned issuer where the provider sends
|
|
112
|
+
it (and is required where its discovery says so), a provider error is reported
|
|
113
|
+
without its text, then the code is exchanged at the pinned token endpoint. With
|
|
114
|
+
`private_key_jwt` the app sends a one-minute RFC 7523 assertion signed by its own
|
|
115
|
+
key (`iss`=`sub`=client id, `aud` the token endpoint, unique `jti`); no secret is
|
|
116
|
+
shared. The ID token's header must name a pinned algorithm and a kid; the key
|
|
117
|
+
comes from the link's cached JWKS, refetched **once** when the kid is unknown and
|
|
118
|
+
refused if still unknown; a substituted key under a known kid fails without a
|
|
119
|
+
refetch. Then the signature, `iss` (the pinned issuer), `aud`/`azp` (this client,
|
|
120
|
+
plus explicitly trusted `additionalAudiences`), `exp`/`iat`/`nbf`, `nonce`, and
|
|
121
|
+
`at_hash`/`c_hash` when present. Access and refresh tokens are never stored or
|
|
122
|
+
returned.
|
|
123
|
+
|
|
124
|
+
The principal then carries the provider's email only as far as its preset
|
|
125
|
+
trusts it, and the admission refuses, in production (not `local`), `amr`
|
|
126
|
+
containing `dev` and addresses under `.localhost`, `.test` or `.invalid`. A
|
|
127
|
+
proof older than 30 days does not count. Successful callback returns 303 to the
|
|
128
|
+
**stored** local `returnTo` with the session cookie.
|
|
129
|
+
|
|
130
|
+
Errors are JSON `{error:code}` for API callers; a browser navigation or native
|
|
131
|
+
form post (`Accept: text/html`) is sent back to the sign-in page with
|
|
132
|
+
`?notice=<code>` and the email link. Codes include `oidc_invalid_correlation`,
|
|
133
|
+
`oidc_issuer_mismatch`, `oidc_audience_mismatch`, `oidc_algorithm_refused`,
|
|
134
|
+
`oidc_unknown_key`, `oidc_provider_denied`, `oidc_admission_denied`,
|
|
135
|
+
`oidc_provider_required`, `oidc_provider_unavailable`, `oidc_relink_required`,
|
|
136
|
+
`oidc_provider_version_unsupported`, `oidc_rate_limited`, `oidc_storage_unavailable`.
|
|
137
|
+
|
|
138
|
+
## Reachability
|
|
139
|
+
|
|
140
|
+
Every discovery and JWKS call records the provider's health in
|
|
141
|
+
`oa_identity_provider_health`. A provider that has failed discovery or JWKS for
|
|
142
|
+
5 minutes (`HIDE_AFTER_MS`), or answered 404 or a DNS failure (a resolver error,
|
|
143
|
+
or the 530 a Worker receives for an origin DNS failure), is hidden from the
|
|
144
|
+
sign-in page, which offers the email link instead of redirecting. A hidden
|
|
145
|
+
provider is checked again at most once a minute, after the page answers
|
|
146
|
+
(`waitUntil`), and shown again once a check succeeds.
|
|
147
|
+
|
|
148
|
+
## Storage and limits
|
|
149
|
+
|
|
150
|
+
`oa_oidc_transactions` stores domain-separated HMAC hashes of state, binding and
|
|
151
|
+
the provider's pin, plus the AES-GCM sealed nonce, verifier, returnTo, expiry and
|
|
152
|
+
provider id. `oa_oidc_limits` counts starts per provider, client address and
|
|
153
|
+
15-minute window (default 30, `identityStartLimit`). `oa_identity_providers`
|
|
154
|
+
holds the pins and cached JWKS. Delete expired rows from the two transaction
|
|
155
|
+
tables periodically (`expires_at <= now`, milliseconds). Discovery and token
|
|
156
|
+
JSON are limited to 64 KiB, JWKS to 256 KiB and 100 keys; each call has a
|
|
157
|
+
deadline including the body (default 5000 ms, `identityTimeoutMs`); redirects are
|
|
158
|
+
never followed.
|
|
159
|
+
|
|
160
|
+
## Checks and proof boundary
|
|
161
|
+
|
|
162
|
+
`node --test modules/operator-auth/identity/oidc/tests/*.test.mjs` runs the
|
|
163
|
+
protocol suite on Node SQLite (`client.test.mjs`, `boundaries.test.mjs`) and the
|
|
164
|
+
fake-provider suite through the public HTTP door in real workerd with D1
|
|
165
|
+
(`providers.test.mjs`): a happy path per preset, the mix-up, wrong `iss` and
|
|
166
|
+
`aud`, `none`/HS256/unlisted algorithms, an unknown kid refetched once then
|
|
167
|
+
refused, a replayed state, `amr: ["dev"]`, unverified, stale and unproven
|
|
168
|
+
emails, bindings, the domain rule, step-up for each action, reachability, the
|
|
169
|
+
minimum version and the re-link. Fakes stand only at the provider edge
|
|
170
|
+
(`idp.mjs`, `fake-worker.mjs`). This proves the module, not a real provider
|
|
171
|
+
account, client registration or deployment.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { encode, utf8, random } from './crypto.mjs';
|
|
2
|
+
|
|
3
|
+
const ALGORITHMS = { ES256: { name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-256' }, RS256: { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' } };
|
|
4
|
+
|
|
5
|
+
// RFC 7523 client authentication (`private_key_jwt`): a one-minute assertion,
|
|
6
|
+
// unique by jti, addressed to the pinned token endpoint.
|
|
7
|
+
export async function clientAssertion(provider, link, now) {
|
|
8
|
+
const algorithm = ALGORITHMS[provider.keyAlgorithm];
|
|
9
|
+
const { kid, alg: _, use: __, key_ops: ___, ...jwk } = provider.privateKey;
|
|
10
|
+
const key = await crypto.subtle.importKey('jwk', jwk, algorithm, false, ['sign']);
|
|
11
|
+
const iat = Math.floor(now / 1000);
|
|
12
|
+
const header = { alg: provider.keyAlgorithm, kid, typ: 'JWT' };
|
|
13
|
+
const claims = { iss: link.clientId, sub: link.clientId, aud: link.token, jti: random(), iat, exp: iat + 60 };
|
|
14
|
+
const input = `${encode(utf8.encode(JSON.stringify(header)))}.${encode(utf8.encode(JSON.stringify(claims)))}`;
|
|
15
|
+
return `${input}.${encode(await crypto.subtle.sign(algorithm, key, utf8.encode(input)))}`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { normalizeEmail } from '../../policy.mjs';
|
|
2
|
+
import { isLoopbackHost } from '../../http.mjs';
|
|
3
|
+
import { check } from './errors.mjs';
|
|
4
|
+
import { PRESETS, MINIMUM_PROVIDER_VERSION, compareVersions, validVersion } from './presets.mjs';
|
|
5
|
+
|
|
6
|
+
function integer(value, defaultValue, min, max) {
|
|
7
|
+
const v = value ?? defaultValue;
|
|
8
|
+
check(Number.isSafeInteger(v) && v >= min && v <= max, 'oidc_invalid_configuration', 503); return v;
|
|
9
|
+
}
|
|
10
|
+
function httpsUrl(value) {
|
|
11
|
+
const u = new URL(value);
|
|
12
|
+
check(typeof value === 'string' && u.protocol === 'https:' && !u.username && !u.password && !u.search && !u.hash && !/[\s\\]/.test(value));
|
|
13
|
+
return u;
|
|
14
|
+
}
|
|
15
|
+
// A private JWK for private_key_jwt. Its public half is what the provider holds.
|
|
16
|
+
function privateKey(jwk, algorithm) {
|
|
17
|
+
check(jwk && typeof jwk === 'object' && typeof jwk.kid === 'string' && jwk.kid.length > 0 && jwk.kid.length <= 256 && typeof jwk.d === 'string');
|
|
18
|
+
check(algorithm === 'ES256' ? jwk.kty === 'EC' && jwk.crv === 'P-256' : jwk.kty === 'RSA');
|
|
19
|
+
return Object.freeze({ ...jwk });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function provider(input, origin) {
|
|
23
|
+
check(input && typeof input === 'object' && typeof input.id === 'string' && /^[a-z][a-z0-9-]{0,31}$/.test(input.id));
|
|
24
|
+
const preset = PRESETS[input.preset];
|
|
25
|
+
check(preset && Object.hasOwn(PRESETS, input.preset));
|
|
26
|
+
let issuer = preset.issuer ?? input.issuer;
|
|
27
|
+
if (preset.tenantIssuer) {
|
|
28
|
+
// A tenant-specific issuer only: never common, organizations or consumers.
|
|
29
|
+
check(typeof input.tenant === 'string' && /^[A-Za-z0-9-]{1,64}(\.[A-Za-z0-9-]{1,63})*$/.test(input.tenant) && !['common', 'organizations', 'consumers'].includes(input.tenant.toLowerCase()));
|
|
30
|
+
issuer = preset.tenantIssuer(input.tenant);
|
|
31
|
+
}
|
|
32
|
+
check(input.issuer === undefined || input.issuer === issuer);
|
|
33
|
+
const issuerUrl = httpsUrl(issuer);
|
|
34
|
+
check(typeof input.clientId === 'string' && input.clientId.length > 0 && input.clientId.length <= 512 && !/[\x00-\x1f\x7f]/.test(input.clientId));
|
|
35
|
+
const tokenAuthMethod = input.tokenAuthMethod ?? preset.methods[0];
|
|
36
|
+
check(preset.methods.includes(tokenAuthMethod));
|
|
37
|
+
check(tokenAuthMethod === 'private_key_jwt' ? input.clientSecret === undefined : input.privateKey === undefined);
|
|
38
|
+
check(!tokenAuthMethod.startsWith('client_secret') || typeof input.clientSecret === 'string' && input.clientSecret.length > 0 && input.clientSecret.length <= 4096);
|
|
39
|
+
const keyAlgorithm = tokenAuthMethod === 'private_key_jwt' ? input.privateKey?.alg ?? (input.privateKey?.kty === 'EC' ? 'ES256' : 'RS256') : undefined;
|
|
40
|
+
check(keyAlgorithm === undefined || ['ES256', 'RS256'].includes(keyAlgorithm));
|
|
41
|
+
const additionalAudiences = input.additionalAudiences ?? [];
|
|
42
|
+
check(Array.isArray(additionalAudiences) && additionalAudiences.length <= 10 && additionalAudiences.every(a => typeof a === 'string' && a.length > 0 && a.length <= 512));
|
|
43
|
+
const endpointOrigins = input.endpointOrigins ?? preset.endpointOrigins ?? [issuerUrl.origin];
|
|
44
|
+
check(Array.isArray(endpointOrigins) && endpointOrigins.length > 0 && endpointOrigins.every(o => { const u = new URL(o); return u.origin === o && u.protocol === 'https:'; }));
|
|
45
|
+
const subjectEmails = Object.fromEntries(Object.entries(input.subjectEmails ?? {}).map(([subject, email]) => {
|
|
46
|
+
check(subject.length > 0 && subject.length <= 255); return [subject, normalizeEmail(email)];
|
|
47
|
+
}));
|
|
48
|
+
const minimumVersion = preset.interface ? input.minimumVersion ?? MINIMUM_PROVIDER_VERSION : undefined;
|
|
49
|
+
check(minimumVersion === undefined || validVersion(minimumVersion) && compareVersions(minimumVersion, MINIMUM_PROVIDER_VERSION) >= 0);
|
|
50
|
+
const label = input.label ?? preset.label;
|
|
51
|
+
check(typeof label === 'string' && label.length > 0 && label.length <= 60 && !/[\x00-\x1f\x7f]/.test(label));
|
|
52
|
+
const redirectUri = input.redirectUri ?? `${origin}/auth/oidc/${input.id}/callback`;
|
|
53
|
+
const redirect = new URL(redirectUri);
|
|
54
|
+
check(typeof redirectUri === 'string' && redirect.href === redirectUri && redirect.origin === origin && !redirect.username && !redirect.password && !redirect.search && !redirect.hash && !/[\s\\]/.test(redirectUri));
|
|
55
|
+
// Incumbent trust is a separate explicit preset, scoped to exact server-owned
|
|
56
|
+
// endpoints. No verified-email switch can broaden Okta/Microsoft defaults.
|
|
57
|
+
let endpointPins;
|
|
58
|
+
if (input.preset === 'legacy-rs256') {
|
|
59
|
+
check(input.endpointPins && typeof input.endpointPins === 'object');
|
|
60
|
+
endpointPins = Object.freeze(Object.fromEntries(['authorization', 'token', 'jwks'].map(key => {
|
|
61
|
+
const value = input.endpointPins[key], u = httpsUrl(value);
|
|
62
|
+
check(u.href === value && endpointOrigins.includes(u.origin));
|
|
63
|
+
return [key, value];
|
|
64
|
+
})));
|
|
65
|
+
} else check(input.endpointPins === undefined);
|
|
66
|
+
return Object.freeze({ id: input.id, preset: input.preset, label, issuer, clientId: input.clientId, tokenAuthMethod,
|
|
67
|
+
clientSecret: input.clientSecret, privateKey: keyAlgorithm ? privateKey(input.privateKey, keyAlgorithm) : undefined, keyAlgorithm,
|
|
68
|
+
algorithms: Object.freeze([...preset.algorithms]), email: preset.email, scope: preset.scope, interface: preset.interface, minimumVersion,
|
|
69
|
+
additionalAudiences: Object.freeze([...new Set(additionalAudiences)]), endpointOrigins: Object.freeze([...new Set(endpointOrigins)]), subjectEmails: Object.freeze(subjectEmails),
|
|
70
|
+
// Each provider has its own redirect URI; its state is bound to it.
|
|
71
|
+
endpointPins, startPath: `/auth/oidc/${input.id}/start`, redirectUri });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function configure(input) {
|
|
75
|
+
try {
|
|
76
|
+
const origin = new URL(input.origin);
|
|
77
|
+
check(origin.origin === input.origin && !origin.username && !origin.password);
|
|
78
|
+
const local = input.local === true;
|
|
79
|
+
check(local ? isLoopbackHost(origin.hostname) && ['http:', 'https:'].includes(origin.protocol) : origin.protocol === 'https:');
|
|
80
|
+
check(typeof input.transactionSecret === 'string' && input.transactionSecret.length >= 32 && input.transactionSecret.length <= 1024);
|
|
81
|
+
check(Array.isArray(input.providers) && input.providers.length <= 8);
|
|
82
|
+
const providers = input.providers.map(entry => provider(entry, input.origin));
|
|
83
|
+
check(new Set(providers.map(p => p.id)).size === providers.length);
|
|
84
|
+
const callbacks = providers.map(p => new URL(p.redirectUri).pathname);
|
|
85
|
+
check(new Set(callbacks).size === providers.length);
|
|
86
|
+
// Auth routes, owner-claim handlers and the local dev gate retain their
|
|
87
|
+
// handlers even when an edition chooses its own OIDC callback pathname.
|
|
88
|
+
const reserved = new Set([input.signInPath ?? '/signin', '/auth/owner-claim', '/auth/dev/enter', '/__dev/personas', '/__dev/key', '/auth/bootstrap', '/auth/magic-link', '/auth/confirm', '/auth/signout', '/auth/session', '/auth/invite', '/auth/invite/cancel', '/auth/review-session', '/auth/operators', '/auth/sessions', '/auth/operator/status', '/auth/operator/role', '/auth/session/revoke', '/auth/step-up', '/auth/domain-rules', '/auth/providers', '/auth/providers/unlink', '/auth/providers/relink', '/auth/settings/identity', '/auth/setup-complete']);
|
|
89
|
+
check(callbacks.every((path, index) => !reserved.has(path) && !path.startsWith('/.well-known/') && !path.startsWith('/auth/dependencies/') && !providers.some(p => path === p.startPath) && (!path.startsWith('/auth/oidc/') || path === `/auth/oidc/${providers[index].id}/callback`)));
|
|
90
|
+
|
|
91
|
+
return Object.freeze({ origin: input.origin, local, secure: origin.protocol === 'https:', transactionSecret: input.transactionSecret, providers: Object.freeze(providers),
|
|
92
|
+
signInPath: input.signInPath ?? '/signin',
|
|
93
|
+
transactionSeconds: integer(input.transactionSeconds, 600, 60, 900), timeoutMs: integer(input.timeoutMs, 5000, 100, 15000),
|
|
94
|
+
clockSkewSeconds: integer(input.clockSkewSeconds, 30, 0, 120), maxTokenAgeSeconds: integer(input.maxTokenAgeSeconds, 600, 60, 3600),
|
|
95
|
+
startLimit: integer(input.startLimit, 30, 1, 1000) });
|
|
96
|
+
} catch { check(false, 'oidc_invalid_configuration', 503); }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function endpoint(value, provider) {
|
|
100
|
+
try { const u = new URL(value); check(typeof value === 'string' && u.href === value && u.protocol === 'https:' && !u.username && !u.password && !u.hash && !u.search && provider.endpointOrigins.includes(u.origin)); return u.href; }
|
|
101
|
+
catch { check(false, 'oidc_untrusted_endpoint', 503); }
|
|
102
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { check } from './errors.mjs';
|
|
2
|
+
export const utf8 = new TextEncoder();
|
|
3
|
+
export function encode(bytes) { return btoa(String.fromCharCode(...new Uint8Array(bytes))).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', ''); }
|
|
4
|
+
export function decode(value) {
|
|
5
|
+
check(typeof value === 'string' && /^[A-Za-z0-9_-]+$/.test(value));
|
|
6
|
+
const bytes = Uint8Array.from(atob(value.replaceAll('-', '+').replaceAll('_', '/')), c => c.charCodeAt(0));
|
|
7
|
+
check(encode(bytes) === value);
|
|
8
|
+
return bytes;
|
|
9
|
+
}
|
|
10
|
+
export function random() { return encode(crypto.getRandomValues(new Uint8Array(32))); }
|
|
11
|
+
export function isRandom(value) { return typeof value === 'string' && /^[A-Za-z0-9_-]{43}$/.test(value); }
|
|
12
|
+
export async function digest(value) { return encode(await crypto.subtle.digest('SHA-256', utf8.encode(value))); }
|
|
13
|
+
export async function keyedHash(secret, purpose, value) {
|
|
14
|
+
const key = await crypto.subtle.importKey('raw', utf8.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
15
|
+
return encode(await crypto.subtle.sign('HMAC', key, utf8.encode(`oidc-v1:${purpose}\0${value}`)));
|
|
16
|
+
}
|
|
17
|
+
async function encryptionKey(secret) {
|
|
18
|
+
return crypto.subtle.importKey('raw', await crypto.subtle.digest('SHA-256', utf8.encode(`oidc-v1:encryption\0${secret}`)), 'AES-GCM', false, ['encrypt', 'decrypt']);
|
|
19
|
+
}
|
|
20
|
+
export async function seal(secret, stateHash, value) {
|
|
21
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
22
|
+
const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData: utf8.encode(stateHash) }, await encryptionKey(secret), utf8.encode(JSON.stringify(value)));
|
|
23
|
+
return `${encode(iv)}.${encode(ciphertext)}`;
|
|
24
|
+
}
|
|
25
|
+
export async function unseal(secret, stateHash, value) {
|
|
26
|
+
const [iv, ciphertext, extra] = value.split('.');
|
|
27
|
+
check(!extra);
|
|
28
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(await crypto.subtle.decrypt({ name: 'AES-GCM', iv: decode(iv), additionalData: utf8.encode(stateHash) }, await encryptionKey(secret), decode(ciphertext))));
|
|
29
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export class OidcError extends Error {
|
|
2
|
+
constructor(code, status = 400) { super(code); this.name = 'OidcError'; this.code = code; this.status = status; }
|
|
3
|
+
}
|
|
4
|
+
export function fail(code = 'oidc_invalid_response', status = 400) { throw new OidcError(code, status); }
|
|
5
|
+
export function check(value, code, status) { if (!value) fail(code, status); }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Admission, Role, Database, IdentityProviderConfig, IdentityProviders } from '../../index.mjs';
|
|
2
|
+
export interface OidcProvidersConfig {
|
|
3
|
+
origin: string;
|
|
4
|
+
local?: boolean;
|
|
5
|
+
/** Independent high-entropy secret of at least 32 characters; never browser data. */
|
|
6
|
+
transactionSecret: string;
|
|
7
|
+
providers: IdentityProviderConfig[];
|
|
8
|
+
signInPath?: string;
|
|
9
|
+
transactionSeconds?: number;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
clockSkewSeconds?: number;
|
|
12
|
+
maxTokenAgeSeconds?: number;
|
|
13
|
+
startLimit?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface OidcProvidersOptions<R extends string = Role> {
|
|
16
|
+
admission: Pick<Admission<R>, 'admit'> & Partial<Pick<Admission<R>,'accounts'>>;
|
|
17
|
+
enabled?:()=>Promise<boolean>;
|
|
18
|
+
policyVersion?:()=>Promise<number>;
|
|
19
|
+
claimScope?:()=>Promise<boolean>;
|
|
20
|
+
claim?:(request:Request,token:string,pin:{id:string;issuer:string;clientId:string;linkedAt:number})=>Promise<Response|null>;
|
|
21
|
+
db: Database;
|
|
22
|
+
config: OidcProvidersConfig;
|
|
23
|
+
clientIp: (request: Request) => string | Promise<string>;
|
|
24
|
+
fetch?: typeof globalThis.fetch;
|
|
25
|
+
clock?: () => number;
|
|
26
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
27
|
+
}
|
|
28
|
+
export const HIDE_AFTER_MS: number;
|
|
29
|
+
export const MINIMUM_PROVIDER_VERSION: string;
|
|
30
|
+
export const PRESETS: Readonly<Record<'auth' | 'google' | 'microsoft' | 'okta' | 'legacy-rs256', { label: string; algorithms: string[]; methods: string[]; email: 'verified-at' | 'google' | 'subject' | 'legacy-verified' }>>;
|
|
31
|
+
export function createOidcProviders<R extends string>(options: OidcProvidersOptions<R>): IdentityProviders;
|
|
32
|
+
export function createOidcHandler(providers: IdentityProviders): (request: Request) => Promise<Response | null>;
|
|
33
|
+
export class OidcError extends Error { constructor(code: string, status?: number); code: string; status: number }
|