@estifanos-sh/convex-auth 0.0.1-preview.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/README.md +52 -0
- package/dist/_generated/component.d.ts +2981 -0
- package/dist/bin.js +7194 -0
- package/dist/browser/index.d.ts +22 -0
- package/dist/browser/index.js +61 -0
- package/dist/browser/locks.js +20 -0
- package/dist/browser/navigation.js +14 -0
- package/dist/browser/runtime.js +90 -0
- package/dist/browser/webauthn.js +99 -0
- package/dist/client/core/types.d.ts +615 -0
- package/dist/client/core/types.js +23 -0
- package/dist/client/errors.js +30 -0
- package/dist/client/factors/device.js +100 -0
- package/dist/client/factors/totp.js +111 -0
- package/dist/client/factors/webauthn.js +69 -0
- package/dist/client/index.d.ts +60 -0
- package/dist/client/index.js +976 -0
- package/dist/client/runtime/invite.js +50 -0
- package/dist/client/runtime/mutex.js +25 -0
- package/dist/client/runtime/proxy.js +75 -0
- package/dist/client/runtime/storage.js +65 -0
- package/dist/component/_generated/api.js +26 -0
- package/dist/component/_generated/component.d.ts +2981 -0
- package/dist/component/_generated/component.js +1 -0
- package/dist/component/_generated/dataModel.js +1 -0
- package/dist/component/_generated/server.js +74 -0
- package/dist/component/account.js +197 -0
- package/dist/component/connection/audit.js +152 -0
- package/dist/component/connection/cache.js +205 -0
- package/dist/component/connection/domain/verification.js +63 -0
- package/dist/component/connection/domain.js +108 -0
- package/dist/component/connection/saml/assertion.js +41 -0
- package/dist/component/connection/saml/request.js +61 -0
- package/dist/component/connection/scim/config.js +55 -0
- package/dist/component/connection/scim/identity.js +162 -0
- package/dist/component/connection/secret.js +65 -0
- package/dist/component/connection/webhook/delivery.js +396 -0
- package/dist/component/connection/webhook/endpoint.js +77 -0
- package/dist/component/connection.js +204 -0
- package/dist/component/convex.config.d.ts +7 -0
- package/dist/component/convex.config.js +15 -0
- package/dist/component/crons.js +27 -0
- package/dist/component/event.js +341 -0
- package/dist/component/factor/device.js +141 -0
- package/dist/component/factor/passkey.js +385 -0
- package/dist/component/factor/totp.js +253 -0
- package/dist/component/functions.js +3 -0
- package/dist/component/group/active.js +63 -0
- package/dist/component/group/invite.js +300 -0
- package/dist/component/group/member.js +188 -0
- package/dist/component/group.js +431 -0
- package/dist/component/http.js +9 -0
- package/dist/component/index.d.ts +5 -0
- package/dist/component/index.js +3 -0
- package/dist/component/limits.js +103 -0
- package/dist/component/maintenance.js +205 -0
- package/dist/component/migrations.js +277 -0
- package/dist/component/model.d.ts +384 -0
- package/dist/component/model.js +709 -0
- package/dist/component/modules.js +53 -0
- package/dist/component/oauth/client.js +196 -0
- package/dist/component/oauth/code.js +97 -0
- package/dist/component/oauth/refresh.js +230 -0
- package/dist/component/schema.d.ts +1530 -0
- package/dist/component/schema.js +401 -0
- package/dist/component/session.js +134 -0
- package/dist/component/token/pkce.js +121 -0
- package/dist/component/token/refresh.js +189 -0
- package/dist/component/token/verification.js +155 -0
- package/dist/component/user/email.js +147 -0
- package/dist/component/user/key.js +273 -0
- package/dist/component/user.js +173 -0
- package/dist/core/index.d.ts +531 -0
- package/dist/core/index.js +81 -0
- package/dist/expo/index.d.ts +43 -0
- package/dist/expo/index.js +131 -0
- package/dist/expo/webauthn.js +88 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/NoopLogger.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/api/logs.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/index.d.ts +7 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/types/AnyValue.d.ts +21 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/types/LogRecord.d.ts +36 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/types/Logger.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/types/LoggerOptions.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_api-logs@0.214.0/node_modules/@opentelemetry/api-logs/build/src/types/LoggerProvider.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/baggage/propagation/W3CBaggagePropagator.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/baggage/utils.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/common/attributes.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/common/global-error-handler.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/common/logging-error-handler.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/common/time.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/common/types.d.ts +16 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/index.d.ts +13 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/propagation/composite.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/trace/TraceState.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/trace/W3CTraceContextPropagator.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/trace/rpc-metadata.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/trace/suppress-tracing.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_core@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/core/build/src/utils/configuration.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/LogRecordProcessor.d.ts +24 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/LoggerProvider.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/config/LoggerConfigurators.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/BatchLogRecordProcessorBase.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/ConsoleLogRecordExporter.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/InMemoryLogRecordExporter.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/LogRecordExporter.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/ReadableLogRecord.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/SdkLogRecord.d.ts +71 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/SimpleLogRecordProcessor.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/index.d.ts +11 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/platform/index.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/platform/node/export/BatchLogRecordProcessor.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/platform/node/index.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/types.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/InstrumentDescriptor.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/MeterProvider.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/aggregator/Drop.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/aggregator/ExponentialHistogram.d.ts +5 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/aggregator/Histogram.d.ts +5 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/aggregator/LastValue.d.ts +5 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/aggregator/Sum.d.ts +5 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/aggregator/index.d.ts +6 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/aggregator/types.d.ts +53 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/AggregationSelector.d.ts +16 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/AggregationTemporality.d.ts +11 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/CardinalitySelector.d.ts +10 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/ConsoleMetricExporter.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/InMemoryMetricExporter.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/MetricData.d.ts +148 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/MetricExporter.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/MetricProducer.d.ts +27 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/MetricReader.d.ts +138 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/PeriodicExportingMetricReader.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/index.d.ts +15 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/types.d.ts +10 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/utils.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/view/Aggregation.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/view/AggregationOption.d.ts +41 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/view/AttributesProcessor.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/view/InstrumentSelector.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/view/View.d.ts +5 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/BasicTracerProvider.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/IdGenerator.d.ts +11 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/Sampler.d.ts +74 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/Span.d.ts +17 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/SpanProcessor.d.ts +43 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/TimedEvent.d.ts +19 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/BatchSpanProcessorBase.d.ts +6 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/ConsoleSpanExporter.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/InMemorySpanExporter.d.ts +3 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/NoopSpanProcessor.d.ts +4 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/ReadableSpan.d.ts +29 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/SimpleSpanProcessor.d.ts +5 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/SpanExporter.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/index.d.ts +18 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/platform/index.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/platform/node/export/BatchSpanProcessor.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/platform/node/index.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/sampler/AlwaysOffSampler.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/sampler/AlwaysOnSampler.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/sampler/ParentBasedSampler.d.ts +2 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/sampler/TraceIdRatioBasedSampler.d.ts +1 -0
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/types.d.ts +66 -0
- package/dist/otel.d.ts +73 -0
- package/dist/otel.js +82 -0
- package/dist/permissions/index.d.ts +81 -0
- package/dist/permissions/index.js +18 -0
- package/dist/providers/anonymous.d.ts +35 -0
- package/dist/providers/anonymous.js +40 -0
- package/dist/providers/apple.d.ts +61 -0
- package/dist/providers/apple.js +57 -0
- package/dist/providers/connection.d.ts +30 -0
- package/dist/providers/connection.js +33 -0
- package/dist/providers/credentials.d.ts +78 -0
- package/dist/providers/credentials.js +31 -0
- package/dist/providers/custom.d.ts +96 -0
- package/dist/providers/custom.js +108 -0
- package/dist/providers/device.d.ts +33 -0
- package/dist/providers/device.js +30 -0
- package/dist/providers/email.d.ts +45 -0
- package/dist/providers/email.js +52 -0
- package/dist/providers/github.d.ts +56 -0
- package/dist/providers/github.js +79 -0
- package/dist/providers/google.d.ts +56 -0
- package/dist/providers/google.js +59 -0
- package/dist/providers/index.d.ts +16 -0
- package/dist/providers/index.js +16 -0
- package/dist/providers/microsoft.d.ts +59 -0
- package/dist/providers/microsoft.js +98 -0
- package/dist/providers/password.d.ts +68 -0
- package/dist/providers/password.js +348 -0
- package/dist/providers/phone.d.ts +33 -0
- package/dist/providers/phone.js +35 -0
- package/dist/providers/redirect.d.ts +1 -0
- package/dist/providers/redirect.js +11 -0
- package/dist/providers/totp.d.ts +29 -0
- package/dist/providers/totp.js +29 -0
- package/dist/providers/webauthn/attestation.d.ts +42 -0
- package/dist/providers/webauthn/attestation.js +167 -0
- package/dist/providers/webauthn.d.ts +83 -0
- package/dist/providers/webauthn.js +57 -0
- package/dist/react/index.d.ts +55 -0
- package/dist/react/index.js +80 -0
- package/dist/server/auth.d.ts +443 -0
- package/dist/server/auth.js +178 -0
- package/dist/server/cache/context.js +94 -0
- package/dist/server/component/api.d.ts +14 -0
- package/dist/server/component/api.js +13 -0
- package/dist/server/component/context.d.ts +16 -0
- package/dist/server/component/context.js +1 -0
- package/dist/server/component/factor/db.js +71 -0
- package/dist/server/config.d.ts +3 -0
- package/dist/server/config.js +78 -0
- package/dist/server/connection/config.js +101 -0
- package/dist/server/connection/domain.d.ts +878 -0
- package/dist/server/connection/domain.js +1122 -0
- package/dist/server/connection/group/service.d.ts +25 -0
- package/dist/server/connection/group/service.js +250 -0
- package/dist/server/connection/http.js +1485 -0
- package/dist/server/connection/oidc.js +366 -0
- package/dist/server/connection/policy/domain.js +59 -0
- package/dist/server/connection/policy.d.ts +1 -0
- package/dist/server/connection/policy.js +163 -0
- package/dist/server/connection/profile.js +22 -0
- package/dist/server/connection/provision.js +182 -0
- package/dist/server/connection/saml/api.js +70 -0
- package/dist/server/connection/saml/binding/post.js +140 -0
- package/dist/server/connection/saml/binding/redirect.js +139 -0
- package/dist/server/connection/saml/binding/shared.js +18 -0
- package/dist/server/connection/saml/c14n.js +224 -0
- package/dist/server/connection/saml/constants.js +100 -0
- package/dist/server/connection/saml/crypto.js +170 -0
- package/dist/server/connection/saml/dom/select.js +299 -0
- package/dist/server/connection/saml/encoding.js +93 -0
- package/dist/server/connection/saml/encryption.js +34 -0
- package/dist/server/connection/saml/entity.js +24 -0
- package/dist/server/connection/saml/extractor.js +408 -0
- package/dist/server/connection/saml/flow.js +150 -0
- package/dist/server/connection/saml/identity/provider.js +66 -0
- package/dist/server/connection/saml/index.js +5 -0
- package/dist/server/connection/saml/metadata.js +294 -0
- package/dist/server/connection/saml/service/provider.js +117 -0
- package/dist/server/connection/saml/signature.js +214 -0
- package/dist/server/connection/saml/template.js +68 -0
- package/dist/server/connection/saml/types.js +1 -0
- package/dist/server/connection/saml/validator.js +32 -0
- package/dist/server/connection/saml/xml/builder.js +68 -0
- package/dist/server/connection/saml/xmldsig.js +598 -0
- package/dist/server/connection/saml/xmlenc.js +162 -0
- package/dist/server/connection/saml.d.ts +56 -0
- package/dist/server/connection/saml.js +762 -0
- package/dist/server/connection/scim.js +115 -0
- package/dist/server/connection/shared.js +74 -0
- package/dist/server/connection/webhook.js +163 -0
- package/dist/server/context.d.ts +137 -0
- package/dist/server/context.js +108 -0
- package/dist/server/contract.d.ts +48 -0
- package/dist/server/contract.js +107 -0
- package/dist/server/cookies.js +90 -0
- package/dist/server/core.js +163 -0
- package/dist/server/cors.d.ts +22 -0
- package/dist/server/cors.js +93 -0
- package/dist/server/crypto.js +43 -0
- package/dist/server/db.js +88 -0
- package/dist/server/device.js +140 -0
- package/dist/server/domains/access.js +29 -0
- package/dist/server/domains/account.d.ts +16 -0
- package/dist/server/domains/account.js +92 -0
- package/dist/server/domains/factor.d.ts +28 -0
- package/dist/server/domains/factor.js +115 -0
- package/dist/server/domains/group.js +87 -0
- package/dist/server/domains/invite.js +76 -0
- package/dist/server/domains/key.js +253 -0
- package/dist/server/domains/member.js +149 -0
- package/dist/server/domains/session.js +29 -0
- package/dist/server/domains/user.js +119 -0
- package/dist/server/env.d.ts +46 -0
- package/dist/server/env.js +96 -0
- package/dist/server/errors.js +38 -0
- package/dist/server/events.d.ts +880 -0
- package/dist/server/events.js +406 -0
- package/dist/server/facade.d.ts +83 -0
- package/dist/server/facade.js +124 -0
- package/dist/server/http.d.ts +125 -0
- package/dist/server/http.js +566 -0
- package/dist/server/identity/claims.js +58 -0
- package/dist/server/identity/convex.d.ts +15 -0
- package/dist/server/identity/convex.js +1 -0
- package/dist/server/index.d.ts +17 -0
- package/dist/server/index.js +11 -0
- package/dist/server/keys.js +60 -0
- package/dist/server/limits.js +41 -0
- package/dist/server/log.js +28 -0
- package/dist/server/mcp.d.ts +19 -0
- package/dist/server/mcp.js +250 -0
- package/dist/server/mutations/account.js +47 -0
- package/dist/server/mutations/calls.js +16 -0
- package/dist/server/mutations/code.js +91 -0
- package/dist/server/mutations/credentials/signin.js +210 -0
- package/dist/server/mutations/invalidate.js +60 -0
- package/dist/server/mutations/oauth.js +251 -0
- package/dist/server/mutations/refresh.js +152 -0
- package/dist/server/mutations/register.js +93 -0
- package/dist/server/mutations/retrieve.js +66 -0
- package/dist/server/mutations/signature.js +36 -0
- package/dist/server/mutations/signin.js +81 -0
- package/dist/server/mutations/signout.js +61 -0
- package/dist/server/mutations/store/refs.js +15 -0
- package/dist/server/mutations/store.js +82 -0
- package/dist/server/mutations/verifier.js +25 -0
- package/dist/server/mutations/verify.js +199 -0
- package/dist/server/oauth/authorize.js +98 -0
- package/dist/server/oauth/client.d.ts +93 -0
- package/dist/server/oauth/client.js +115 -0
- package/dist/server/oauth/code.d.ts +63 -0
- package/dist/server/oauth/code.js +124 -0
- package/dist/server/oauth/domain.d.ts +23 -0
- package/dist/server/oauth/domain.js +1 -0
- package/dist/server/oauth/factory.js +46 -0
- package/dist/server/oauth/grant.js +46 -0
- package/dist/server/oauth/handlers.js +108 -0
- package/dist/server/oauth/manage.js +121 -0
- package/dist/server/oauth/normalize.js +27 -0
- package/dist/server/oauth/refresh.d.ts +71 -0
- package/dist/server/oauth/refresh.js +132 -0
- package/dist/server/oauth/register.js +147 -0
- package/dist/server/oauth/runtime.js +280 -0
- package/dist/server/oauth/token.js +355 -0
- package/dist/server/payloads.d.ts +16 -0
- package/dist/server/payloads.js +30 -0
- package/dist/server/preload.d.ts +249 -0
- package/dist/server/preload.js +673 -0
- package/dist/server/random.js +25 -0
- package/dist/server/redirects.js +85 -0
- package/dist/server/runtime.d.ts +845 -0
- package/dist/server/runtime.js +387 -0
- package/dist/server/secret.js +46 -0
- package/dist/server/services/factors.js +117 -0
- package/dist/server/services/providers.js +23 -0
- package/dist/server/services/resolve.js +17 -0
- package/dist/server/session/lifecycle.d.ts +23 -0
- package/dist/server/session/lifecycle.js +139 -0
- package/dist/server/signin/flow.js +359 -0
- package/dist/server/telemetry.js +68 -0
- package/dist/server/templates.js +91 -0
- package/dist/server/token/refresh.js +35 -0
- package/dist/server/tokens.js +164 -0
- package/dist/server/totp.js +282 -0
- package/dist/server/types.d.ts +1154 -0
- package/dist/server/types.js +1 -0
- package/dist/server/url.js +43 -0
- package/dist/server/user/account.js +325 -0
- package/dist/server/utils/bearer.js +24 -0
- package/dist/server/utils/cache.js +51 -0
- package/dist/server/utils/retry.js +3 -0
- package/dist/server/utils/span.js +59 -0
- package/dist/server/validators.d.ts +2061 -0
- package/dist/server/validators.js +320 -0
- package/dist/server/webauthn.js +699 -0
- package/dist/server/wellknown.d.ts +74 -0
- package/dist/server/wellknown.js +195 -0
- package/dist/shared/brand.d.ts +28 -0
- package/dist/shared/brand.js +13 -0
- package/dist/shared/codes.d.ts +99 -0
- package/dist/shared/codes.js +98 -0
- package/dist/shared/compare.js +23 -0
- package/dist/shared/errors.js +24 -0
- package/dist/shared/event/kinds.d.ts +188 -0
- package/dist/shared/event/kinds.js +103 -0
- package/dist/shared/fetch/guard.js +189 -0
- package/dist/shared/log.js +62 -0
- package/dist/shared/results.d.ts +58 -0
- package/dist/shared/retry.js +27 -0
- package/dist/shared/webauthn.js +9 -0
- package/dist/svelte/AuthLoading.svelte +13 -0
- package/dist/svelte/AuthLoading.svelte.d.ts +8 -0
- package/dist/svelte/SignedIn.svelte +14 -0
- package/dist/svelte/SignedIn.svelte.d.ts +8 -0
- package/dist/svelte/SignedOut.svelte +13 -0
- package/dist/svelte/SignedOut.svelte.d.ts +8 -0
- package/dist/svelte/context.svelte.d.ts +26 -0
- package/dist/svelte/context.svelte.js +61 -0
- package/dist/svelte/index.d.ts +5 -0
- package/dist/svelte/index.js +4 -0
- package/dist/test.d.ts +48 -0
- package/dist/test.js +54 -0
- package/package.json +247 -0
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
import { log } from "../log.js";
|
|
2
|
+
import { finalizeNormalizedProfile, normalizeStringArray } from "./profile.js";
|
|
3
|
+
import { asRecord, getGroupSamlUrls } from "./shared.js";
|
|
4
|
+
import { getSamlConfig } from "./config.js";
|
|
5
|
+
import { BINDING_URI, DEFAULT_MAX_SAML_METADATA_SIZE, DEFAULT_MAX_SAML_RESPONSE_SIZE } from "./saml/constants.js";
|
|
6
|
+
import { safeParseXml, setSchemaValidator } from "./saml/api.js";
|
|
7
|
+
import { createIdentityProvider } from "./saml/identity/provider.js";
|
|
8
|
+
import { createServiceProvider } from "./saml/service/provider.js";
|
|
9
|
+
import { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding } from "@oslojs/encoding";
|
|
10
|
+
|
|
11
|
+
//#region src/server/connection/saml.ts
|
|
12
|
+
function formDataEntries(formData) {
|
|
13
|
+
return formData.entries();
|
|
14
|
+
}
|
|
15
|
+
const _samlifyPermissiveValidator = { validate: (_xml) => Promise.resolve("OK") };
|
|
16
|
+
/**
|
|
17
|
+
* Register the SAML schema validator before parsing any SAML response.
|
|
18
|
+
*
|
|
19
|
+
* A permissive validator that always resolves is used because Convex's edge
|
|
20
|
+
* runtime has no file-system access for XML schema files, and structural
|
|
21
|
+
* correctness is already ensured by the XML parser. This is called directly
|
|
22
|
+
* before each parse operation since Convex can restart the V8 isolate between
|
|
23
|
+
* requests, resetting module-level state.
|
|
24
|
+
*/
|
|
25
|
+
function ensureSamlifyValidator() {
|
|
26
|
+
setSchemaValidator(_samlifyPermissiveValidator);
|
|
27
|
+
}
|
|
28
|
+
function escapeHtmlAttribute(value) {
|
|
29
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build an auto-submitting HTML form that POSTs a SAML message to the IdP/SP
|
|
33
|
+
* endpoint (HTTP-POST binding). All interpolated values are HTML-attribute
|
|
34
|
+
* escaped to prevent markup injection.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function createSamlPostBindingResponse(opts) {
|
|
38
|
+
const fields = [`<input type="hidden" name="${opts.parameter}" value="${escapeHtmlAttribute(opts.value)}" />`, opts.relayState ? `<input type="hidden" name="RelayState" value="${escapeHtmlAttribute(opts.relayState)}" />` : ""].join("");
|
|
39
|
+
return new Response(`<!doctype html><html><body><form method="POST" action="${escapeHtmlAttribute(opts.endpoint)}">${fields}</form><script>document.forms[0].submit();<\/script></body></html>`, {
|
|
40
|
+
status: 200,
|
|
41
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Decode a base64url RelayState into its JSON object, returning `{}` on any
|
|
46
|
+
* decode/parse failure.
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
function decodeRelayState(value) {
|
|
50
|
+
if (!value) return {};
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(new TextDecoder().decode(decodeBase64urlIgnorePadding(value)));
|
|
53
|
+
} catch {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Encode a group connection's SAML RelayState (source, signature, requestId,
|
|
59
|
+
* state, redirectTo) as a base64url JSON string.
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
function encodeGroupSamlRelayState(value) {
|
|
63
|
+
return encodeBase64urlNoPadding(new TextEncoder().encode(JSON.stringify({
|
|
64
|
+
source: `${value.source.kind}:${value.source.id}`,
|
|
65
|
+
signature: value.signature,
|
|
66
|
+
requestId: value.requestId,
|
|
67
|
+
state: value.state,
|
|
68
|
+
redirectTo: value.redirectTo
|
|
69
|
+
})));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Decode and validate a group connection's SAML RelayState, throwing if it is
|
|
73
|
+
* missing, malformed, or not a `connection:<id>` source.
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
function decodeGroupSamlRelayStateOrThrow(value) {
|
|
77
|
+
if (!value) throw new Error("Missing SAML RelayState.");
|
|
78
|
+
const decoded = decodeRelayState(value);
|
|
79
|
+
if (typeof decoded.source !== "string" || typeof decoded.signature !== "string" || typeof decoded.requestId !== "string" || typeof decoded.state !== "string") throw new Error("Invalid SAML RelayState.");
|
|
80
|
+
const [kind, ...rest] = decoded.source.split(":");
|
|
81
|
+
const id = rest.join(":");
|
|
82
|
+
if (kind !== "connection" || id.length === 0) throw new Error("Invalid group connection SAML source.");
|
|
83
|
+
return {
|
|
84
|
+
source: {
|
|
85
|
+
kind,
|
|
86
|
+
id
|
|
87
|
+
},
|
|
88
|
+
signature: decoded.signature,
|
|
89
|
+
requestId: decoded.requestId,
|
|
90
|
+
state: decoded.state,
|
|
91
|
+
redirectTo: typeof decoded.redirectTo === "string" ? decoded.redirectTo : void 0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Read a form-encoded (urlencoded or multipart) request body into a flat
|
|
96
|
+
* string map; returns `{}` for any other content type.
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
async function readRequestBody(request) {
|
|
100
|
+
const contentType = request.headers.get("Content-Type") ?? "";
|
|
101
|
+
if (contentType.includes("application/x-www-form-urlencoded") || contentType.includes("multipart/form-data")) {
|
|
102
|
+
const form = await request.formData();
|
|
103
|
+
const body = {};
|
|
104
|
+
for (const [key, value] of formDataEntries(form)) body[key] = typeof value === "string" ? value : value.name;
|
|
105
|
+
return body;
|
|
106
|
+
}
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Normalize an incoming SAML HTTP request into a {@link GroupSamlHttpRequest},
|
|
111
|
+
* resolving the binding (redirect vs post) and extracting RelayState and the
|
|
112
|
+
* presence of SAMLRequest/SAMLResponse from query or body.
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
async function readGroupConnectionSamlHttpRequest(request) {
|
|
116
|
+
const url = new URL(request.url);
|
|
117
|
+
const body = await readRequestBody(request);
|
|
118
|
+
return {
|
|
119
|
+
url,
|
|
120
|
+
body,
|
|
121
|
+
query: Object.fromEntries(url.searchParams),
|
|
122
|
+
binding: request.method === "GET" ? "redirect" : body.SAMLResponse || body.SAMLRequest ? "post" : "redirect",
|
|
123
|
+
relayState: body.RelayState ?? url.searchParams.get("RelayState") ?? void 0,
|
|
124
|
+
hasSamlRequest: Boolean(body.SAMLRequest ?? url.searchParams.get("SAMLRequest")),
|
|
125
|
+
hasSamlResponse: Boolean(body.SAMLResponse ?? url.searchParams.get("SAMLResponse"))
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function getSamlSecurityConfig(config) {
|
|
129
|
+
return getSamlConfig(config).security ?? {};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Parse IdP SAML metadata XML into a {@link ParsedSamlMetadata} (entityID,
|
|
133
|
+
* SSO/SLO bindings, signing/encryption certs, NameID formats).
|
|
134
|
+
*
|
|
135
|
+
* Rejects metadata containing DTD or entity declarations to prevent XXE /
|
|
136
|
+
* entity-expansion attacks against the downstream XML parser.
|
|
137
|
+
* @internal
|
|
138
|
+
*/
|
|
139
|
+
function parseSamlIdpMetadata(metadata) {
|
|
140
|
+
const source = typeof metadata === "string" ? metadata : String(metadata);
|
|
141
|
+
if (/<!DOCTYPE|<!ENTITY/i.test(source)) throw new Error("SAML metadata must not contain DTD or entity declarations.");
|
|
142
|
+
const entityId = source.match(/<[^>]*EntityDescriptor\b[^>]*\bentityID="([^"]+)"/i)?.[1] ?? null;
|
|
143
|
+
if (!entityId) throw new Error("SAML metadata is missing EntityDescriptor@entityID.");
|
|
144
|
+
const parseAttributes = (source$1) => {
|
|
145
|
+
const attributes = {};
|
|
146
|
+
for (const match of source$1.matchAll(/([A-Za-z_:][\w:.-]*)="([^"]*)"/g)) attributes[match[1]] = match[2];
|
|
147
|
+
return attributes;
|
|
148
|
+
};
|
|
149
|
+
const readServiceBindings = (tagName) => {
|
|
150
|
+
const bindings = {};
|
|
151
|
+
const pattern = new RegExp(`<(?:[A-Za-z0-9_.-]+:)?${tagName}\\b([^>]*)\\/?>(?:<\\/(?:[A-Za-z0-9_.-]+:)?${tagName}>)?`, "gi");
|
|
152
|
+
for (const match of source.matchAll(pattern)) {
|
|
153
|
+
const attrs = parseAttributes(match[1] ?? "");
|
|
154
|
+
const binding = attrs.Binding ?? attrs.binding;
|
|
155
|
+
const location = attrs.Location ?? attrs.location;
|
|
156
|
+
if (!binding || !location) continue;
|
|
157
|
+
if (binding.includes("HTTP-Redirect")) bindings.redirect = location;
|
|
158
|
+
if (binding.includes("HTTP-POST")) bindings.post = location;
|
|
159
|
+
}
|
|
160
|
+
return bindings;
|
|
161
|
+
};
|
|
162
|
+
const readCertificates = (use) => {
|
|
163
|
+
const certs = [];
|
|
164
|
+
const blockPattern = new RegExp(`<(?:[A-Za-z0-9_.-]+:)?KeyDescriptor\\b([^>]*)>([\\s\\S]*?)<\\/(?:[A-Za-z0-9_.-]+:)?KeyDescriptor>`, "gi");
|
|
165
|
+
for (const match of source.matchAll(blockPattern)) {
|
|
166
|
+
const attrs = parseAttributes(match[1] ?? "");
|
|
167
|
+
if ((attrs.use ?? attrs.Use ?? "signing").toLowerCase() !== use) continue;
|
|
168
|
+
for (const certMatch of (match[2] ?? "").matchAll(/<(?:[A-Za-z0-9_.-]+:)?X509Certificate\b[^>]*>([\s\S]*?)<\/(?:[A-Za-z0-9_.-]+:)?X509Certificate>/gi)) {
|
|
169
|
+
const certificate = certMatch[1]?.replace(/\s+/g, "").trim();
|
|
170
|
+
if (certificate) certs.push(certificate);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (certs.length === 0) return null;
|
|
174
|
+
return certs.length === 1 ? certs[0] : certs;
|
|
175
|
+
};
|
|
176
|
+
const nameIdFormats = [...source.matchAll(/<(?:[A-Za-z0-9_.-]+:)?NameIDFormat>([\s\S]*?)<\/(?:[A-Za-z0-9_.-]+:)?NameIDFormat>/gi)].map((match) => match[1]?.trim()).filter((value) => Boolean(value));
|
|
177
|
+
return {
|
|
178
|
+
entityId,
|
|
179
|
+
issuer: entityId,
|
|
180
|
+
connection: readServiceBindings("SingleSignOnService"),
|
|
181
|
+
slo: readServiceBindings("SingleLogoutService"),
|
|
182
|
+
signingCert: readCertificates("signing"),
|
|
183
|
+
encryptionCert: readCertificates("encryption"),
|
|
184
|
+
nameIdFormats,
|
|
185
|
+
wantsSignedAuthnRequests: /WantAuthnRequestsSigned="(?:true|1)"/i.test(source)
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Throw if the IdP metadata XML exceeds the connection's configured
|
|
190
|
+
* `maxMetadataSize`, bounding parser work against oversized payloads.
|
|
191
|
+
*
|
|
192
|
+
* When no limit is configured a generous default
|
|
193
|
+
* ({@link DEFAULT_MAX_SAML_METADATA_SIZE}) applies so the cap is never silently
|
|
194
|
+
* off; an operator may override it per connection or disable it entirely with a
|
|
195
|
+
* non-positive value.
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
function enforceSamlMetadataSize(opts) {
|
|
199
|
+
const configured = getSamlSecurityConfig(opts.config).maxMetadataSize;
|
|
200
|
+
const maxMetadataSize = typeof configured === "number" ? configured : DEFAULT_MAX_SAML_METADATA_SIZE;
|
|
201
|
+
if (maxMetadataSize > 0 && opts.metadataXml.length > maxMetadataSize) throw new Error("SAML metadata exceeds the configured size limit.");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Size-check then parse IdP metadata: {@link enforceSamlMetadataSize} followed
|
|
205
|
+
* by {@link parseSamlIdpMetadata}.
|
|
206
|
+
* @internal
|
|
207
|
+
*/
|
|
208
|
+
function parseSamlIdpMetadataChecked(opts) {
|
|
209
|
+
enforceSamlMetadataSize(opts);
|
|
210
|
+
return parseSamlIdpMetadata(opts.metadataXml);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Throw if the encoded SAMLResponse exceeds the connection's configured
|
|
214
|
+
* `maxResponseSize`, bounding decode/parse work against oversized payloads.
|
|
215
|
+
*
|
|
216
|
+
* The ACS handler base64-decodes and DOM-parses the response *before* verifying
|
|
217
|
+
* its signature, so this runs on unauthenticated input. When no limit is
|
|
218
|
+
* configured a generous default ({@link DEFAULT_MAX_SAML_RESPONSE_SIZE}) applies
|
|
219
|
+
* so the pre-auth parser can never be handed an unbounded document; an operator
|
|
220
|
+
* may override it per connection or disable it with a non-positive value.
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
function enforceSamlResponseSize(opts) {
|
|
224
|
+
const configured = getSamlSecurityConfig(opts.config).maxResponseSize;
|
|
225
|
+
const maxResponseSize = typeof configured === "number" ? configured : DEFAULT_MAX_SAML_RESPONSE_SIZE;
|
|
226
|
+
if (maxResponseSize <= 0) return;
|
|
227
|
+
const encoded = opts.request.body.SAMLResponse ?? opts.request.query.SAMLResponse;
|
|
228
|
+
if (typeof encoded === "string" && encoded.length > maxResponseSize) throw new Error("SAML response exceeds the configured size limit.");
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Build the SP's SAML metadata XML (entityID, ACS/SLO endpoints, signing and
|
|
232
|
+
* encryption certs) for publication to the IdP.
|
|
233
|
+
* @internal
|
|
234
|
+
*/
|
|
235
|
+
function createServiceProviderMetadata(opts) {
|
|
236
|
+
const binding = BINDING_URI;
|
|
237
|
+
return createServiceProvider({
|
|
238
|
+
entityID: opts.entityId,
|
|
239
|
+
authnRequestsSigned: opts.authnRequestsSigned ?? false,
|
|
240
|
+
privateKey: opts.privateKey,
|
|
241
|
+
privateKeyPass: opts.privateKeyPass,
|
|
242
|
+
signingCert: opts.signingCert,
|
|
243
|
+
encryptCert: opts.encryptCert,
|
|
244
|
+
encPrivateKey: opts.encPrivateKey,
|
|
245
|
+
encPrivateKeyPass: opts.encPrivateKeyPass,
|
|
246
|
+
assertionConsumerService: [{
|
|
247
|
+
Binding: binding.post,
|
|
248
|
+
Location: opts.acsUrl
|
|
249
|
+
}],
|
|
250
|
+
singleLogoutService: opts.sloUrl ? [{
|
|
251
|
+
Binding: binding.redirect,
|
|
252
|
+
Location: opts.sloUrl
|
|
253
|
+
}, {
|
|
254
|
+
Binding: binding.post,
|
|
255
|
+
Location: opts.sloUrl
|
|
256
|
+
}] : void 0
|
|
257
|
+
}).getMetadata();
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Render the SP metadata XML for a specific group connection, deriving SP
|
|
261
|
+
* options from its stored config via {@link getSamlServiceProviderOptions}.
|
|
262
|
+
* @internal
|
|
263
|
+
*/
|
|
264
|
+
function createGroupConnectionSamlMetadataXml(opts) {
|
|
265
|
+
return createServiceProviderMetadata(getSamlServiceProviderOptions({
|
|
266
|
+
rootUrl: opts.rootUrl,
|
|
267
|
+
source: opts.source,
|
|
268
|
+
config: opts.config
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Resolve effective SP options for a group connection, layering caller
|
|
273
|
+
* `overrides` over stored `serviceProvider` config over the default group
|
|
274
|
+
* SAML URLs.
|
|
275
|
+
* @internal
|
|
276
|
+
*/
|
|
277
|
+
function getSamlServiceProviderOptions(opts) {
|
|
278
|
+
const saml = getSamlConfig(opts.config);
|
|
279
|
+
const sp = asRecord(saml.serviceProvider) ?? {};
|
|
280
|
+
const clockSkewMs = ((asRecord(saml.security) ?? {}).clockSkewSeconds ?? 300) * 1e3;
|
|
281
|
+
const urls = getGroupSamlUrls({
|
|
282
|
+
rootUrl: opts.rootUrl,
|
|
283
|
+
source: opts.source
|
|
284
|
+
});
|
|
285
|
+
return {
|
|
286
|
+
entityId: opts.overrides?.entityId ?? sp.entityId ?? urls.metadataUrl,
|
|
287
|
+
acsUrl: opts.overrides?.acsUrl ?? sp.acsUrl ?? urls.acsUrl,
|
|
288
|
+
sloUrl: opts.overrides?.sloUrl ?? sp.sloUrl ?? urls.sloUrl,
|
|
289
|
+
relayState: opts.relayState,
|
|
290
|
+
authnRequestsSigned: saml.request?.signAuthnRequests,
|
|
291
|
+
signingCert: sp.signingCert,
|
|
292
|
+
encryptCert: sp.encryptCert,
|
|
293
|
+
privateKey: sp.privateKey,
|
|
294
|
+
privateKeyPass: sp.privateKeyPass,
|
|
295
|
+
encPrivateKey: sp.encPrivateKey,
|
|
296
|
+
encPrivateKeyPass: sp.encPrivateKeyPass,
|
|
297
|
+
clockDrifts: [-clockSkewMs, clockSkewMs]
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Construct a samlify ServiceProvider from resolved SP options (entityID,
|
|
302
|
+
* ACS/SLO endpoints, signing config).
|
|
303
|
+
* @internal
|
|
304
|
+
*/
|
|
305
|
+
function createSamlServiceProvider(opts) {
|
|
306
|
+
const binding = BINDING_URI;
|
|
307
|
+
return createServiceProvider({
|
|
308
|
+
entityID: opts.entityId,
|
|
309
|
+
relayState: opts.relayState ?? "",
|
|
310
|
+
authnRequestsSigned: opts.authnRequestsSigned ?? false,
|
|
311
|
+
privateKey: opts.privateKey,
|
|
312
|
+
privateKeyPass: opts.privateKeyPass,
|
|
313
|
+
signingCert: opts.signingCert,
|
|
314
|
+
encryptCert: opts.encryptCert,
|
|
315
|
+
encPrivateKey: opts.encPrivateKey,
|
|
316
|
+
encPrivateKeyPass: opts.encPrivateKeyPass,
|
|
317
|
+
clockDrifts: opts.clockDrifts,
|
|
318
|
+
assertionConsumerService: [{
|
|
319
|
+
Binding: binding.post,
|
|
320
|
+
Location: opts.acsUrl
|
|
321
|
+
}],
|
|
322
|
+
singleLogoutService: opts.sloUrl ? [{
|
|
323
|
+
Binding: binding.redirect,
|
|
324
|
+
Location: opts.sloUrl
|
|
325
|
+
}, {
|
|
326
|
+
Binding: binding.post,
|
|
327
|
+
Location: opts.sloUrl
|
|
328
|
+
}] : void 0
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Build the per-request SAML runtime (SP, IdP, and group URLs) for a group
|
|
333
|
+
* connection. Throws if the connection has no stored IdP metadata XML.
|
|
334
|
+
* @internal
|
|
335
|
+
*/
|
|
336
|
+
function createGroupConnectionSamlRuntime(opts) {
|
|
337
|
+
const saml = getSamlConfig(opts.config);
|
|
338
|
+
const spOptions = getSamlServiceProviderOptions({
|
|
339
|
+
rootUrl: opts.rootUrl,
|
|
340
|
+
source: opts.source,
|
|
341
|
+
config: opts.config,
|
|
342
|
+
relayState: opts.relayState,
|
|
343
|
+
overrides: opts.overrides
|
|
344
|
+
});
|
|
345
|
+
if (typeof saml.idp?.metadataXml !== "string") throw new Error("SAML IdP metadata is missing.");
|
|
346
|
+
return {
|
|
347
|
+
saml,
|
|
348
|
+
sp: createSamlServiceProvider(spOptions),
|
|
349
|
+
idp: createIdentityProvider({ metadata: saml.idp.metadataXml }),
|
|
350
|
+
urls: getGroupSamlUrls({
|
|
351
|
+
rootUrl: opts.rootUrl,
|
|
352
|
+
source: opts.source
|
|
353
|
+
})
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function verifySamlTimeWindow(notBefore, notOnOrAfter, clockSkewSeconds) {
|
|
357
|
+
const now = Date.now();
|
|
358
|
+
const drift = clockSkewSeconds * 1e3;
|
|
359
|
+
if (notBefore) {
|
|
360
|
+
const notBeforeTime = new Date(notBefore).getTime();
|
|
361
|
+
if (!Number.isFinite(notBeforeTime)) throw new Error("SAML assertion has an invalid NotBefore timestamp.");
|
|
362
|
+
if (now < notBeforeTime - drift) throw new Error("SAML assertion is not yet valid.");
|
|
363
|
+
}
|
|
364
|
+
if (notOnOrAfter) {
|
|
365
|
+
const notOnOrAfterTime = new Date(notOnOrAfter).getTime();
|
|
366
|
+
if (!Number.isFinite(notOnOrAfterTime)) throw new Error("SAML assertion has an invalid NotOnOrAfter timestamp.");
|
|
367
|
+
if (now >= notOnOrAfterTime + drift) throw new Error("SAML assertion has expired.");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function normalizeSamlUrl(value) {
|
|
371
|
+
return value.trim().replace(/\/$/, "");
|
|
372
|
+
}
|
|
373
|
+
function toStringArray(value) {
|
|
374
|
+
if (Array.isArray(value)) return value;
|
|
375
|
+
if (typeof value === "string") return [value];
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
function localName(node) {
|
|
379
|
+
return typeof node === "object" && node !== null ? node.localName ?? node.nodeName : void 0;
|
|
380
|
+
}
|
|
381
|
+
function isElementNode(node) {
|
|
382
|
+
return localName(node) !== void 0;
|
|
383
|
+
}
|
|
384
|
+
function elementChildren(node) {
|
|
385
|
+
const childNodes = typeof node === "object" && node !== null ? node.childNodes : void 0;
|
|
386
|
+
return childNodes ? Array.from(childNodes).filter(isElementNode) : [];
|
|
387
|
+
}
|
|
388
|
+
function findDirectChild(node, name) {
|
|
389
|
+
return elementChildren(node).find((child) => localName(child) === name);
|
|
390
|
+
}
|
|
391
|
+
function findDescendant(node, name) {
|
|
392
|
+
for (const child of elementChildren(node)) {
|
|
393
|
+
if (localName(child) === name) return child;
|
|
394
|
+
const nested = findDescendant(child, name);
|
|
395
|
+
if (nested) return nested;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function getAttribute(node, name) {
|
|
399
|
+
if (typeof node !== "object" || node === null) return;
|
|
400
|
+
const value = node.getAttribute?.call(node, name);
|
|
401
|
+
return typeof value === "string" ? value : void 0;
|
|
402
|
+
}
|
|
403
|
+
function signatureMetadataFromNode(signatureNode) {
|
|
404
|
+
if (!signatureNode) return;
|
|
405
|
+
const signatureMethod = findDescendant(signatureNode, "SignatureMethod");
|
|
406
|
+
const digestMethod = findDescendant(signatureNode, "DigestMethod");
|
|
407
|
+
return {
|
|
408
|
+
signatureAlgorithm: getAttribute(signatureMethod, "Algorithm"),
|
|
409
|
+
digestAlgorithm: getAttribute(digestMethod, "Algorithm")
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function extractSignatureMetadataFromXml(content) {
|
|
413
|
+
try {
|
|
414
|
+
const root = safeParseXml(content, "text/xml").documentElement;
|
|
415
|
+
if (localName(root) === "Signature") return { responseSignature: signatureMetadataFromNode(root) };
|
|
416
|
+
const responseSignature = signatureMetadataFromNode(findDirectChild(root, "Signature"));
|
|
417
|
+
const assertion = localName(root) === "Assertion" ? root : findDirectChild(root, "Assertion");
|
|
418
|
+
return {
|
|
419
|
+
responseSignature,
|
|
420
|
+
assertionSignature: localName(root) === "Assertion" ? signatureMetadataFromNode(findDirectChild(root, "Signature")) : signatureMetadataFromNode(findDirectChild(assertion, "Signature"))
|
|
421
|
+
};
|
|
422
|
+
} catch {
|
|
423
|
+
return {};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Read the `Assertion/@ID` from cryptographically-verified SAML content,
|
|
428
|
+
* whether the content is the assertion element itself or a response wrapping
|
|
429
|
+
* one. Returns `undefined` when no assertion ID can be resolved (in which case
|
|
430
|
+
* the caller must fail closed rather than treat the response as unique).
|
|
431
|
+
*/
|
|
432
|
+
function assertionIdFromXml(content) {
|
|
433
|
+
try {
|
|
434
|
+
const root = safeParseXml(content, "text/xml").documentElement;
|
|
435
|
+
const id = getAttribute(localName(root) === "Assertion" ? root : findDirectChild(root, "Assertion"), "ID");
|
|
436
|
+
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
437
|
+
} catch {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Derive the identifiers a SAML ACS handler needs to prevent replay from a
|
|
443
|
+
* parsed login response: the assertion's `ID` (verified from the canonical
|
|
444
|
+
* bytes), the `InResponseTo` binding the response to a pending AuthnRequest,
|
|
445
|
+
* and the tightest `NotOnOrAfter` upper bound (from `<SubjectConfirmationData>`
|
|
446
|
+
* or `<Conditions>`) used to bound the seen-assertion cache's TTL.
|
|
447
|
+
* @internal
|
|
448
|
+
*/
|
|
449
|
+
function samlReplayIdentifiers(parsed) {
|
|
450
|
+
const extract = parsed.extract;
|
|
451
|
+
const notOnOrAfter = extract?.subjectConfirmation?.notOnOrAfter ?? extract?.conditions?.notOnOrAfter ?? void 0;
|
|
452
|
+
return {
|
|
453
|
+
assertionId: assertionIdFromXml(parsed.samlContent),
|
|
454
|
+
inResponseTo: extract?.subjectConfirmation?.inResponseTo ?? extract?.response?.inResponseTo,
|
|
455
|
+
notOnOrAfter
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function normalizeSignatureMetadata(value) {
|
|
459
|
+
if (typeof value === "object" && value !== null) {
|
|
460
|
+
const signatureAlgorithm = value.signatureAlgorithm;
|
|
461
|
+
const digestAlgorithm = value.digestAlgorithm;
|
|
462
|
+
if (typeof signatureAlgorithm === "string" || typeof digestAlgorithm === "string") return {
|
|
463
|
+
signatureAlgorithm: typeof signatureAlgorithm === "string" ? signatureAlgorithm : void 0,
|
|
464
|
+
digestAlgorithm: typeof digestAlgorithm === "string" ? digestAlgorithm : void 0
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
if (typeof value === "string") return extractSignatureMetadataFromXml(value).responseSignature;
|
|
468
|
+
}
|
|
469
|
+
function attachSamlSignatureMetadata(parsed) {
|
|
470
|
+
const fromXml = extractSignatureMetadataFromXml(parsed.samlContent);
|
|
471
|
+
const responseSignature = normalizeSignatureMetadata(parsed.extract?.signature) ?? fromXml.responseSignature;
|
|
472
|
+
const assertionSignature = normalizeSignatureMetadata(parsed.extract?.assertionSignature) ?? fromXml.assertionSignature;
|
|
473
|
+
return {
|
|
474
|
+
...parsed,
|
|
475
|
+
extract: {
|
|
476
|
+
...parsed.extract,
|
|
477
|
+
signature: responseSignature,
|
|
478
|
+
assertionSignature
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function signatureMetadataList(extract) {
|
|
483
|
+
return [normalizeSignatureMetadata(extract?.signature), normalizeSignatureMetadata(extract?.assertionSignature)].filter((metadata) => metadata !== void 0);
|
|
484
|
+
}
|
|
485
|
+
function validateSamlRequiredSignature(extract, security) {
|
|
486
|
+
if (security.requireSignedAssertions === true && typeof normalizeSignatureMetadata(extract?.assertionSignature)?.signatureAlgorithm !== "string") throw new Error("SAML assertion must be signed.");
|
|
487
|
+
}
|
|
488
|
+
function validateSamlRequiredTimestamps(extract, security) {
|
|
489
|
+
if (security.requireTimestamps === false) return;
|
|
490
|
+
if (!(Boolean(extract?.conditions?.notOnOrAfter) || Boolean(extract?.subjectConfirmation?.notOnOrAfter))) throw new Error("SAML assertion is missing a validity window (NotOnOrAfter).");
|
|
491
|
+
}
|
|
492
|
+
function validateSamlTimeWindow(extract, security) {
|
|
493
|
+
const skew = security.clockSkewSeconds ?? 300;
|
|
494
|
+
const conditions = extract?.conditions;
|
|
495
|
+
if (conditions?.notBefore || conditions?.notOnOrAfter) verifySamlTimeWindow(conditions.notBefore, conditions.notOnOrAfter, skew);
|
|
496
|
+
const subjectNotOnOrAfter = extract?.subjectConfirmation?.notOnOrAfter;
|
|
497
|
+
if (subjectNotOnOrAfter) verifySamlTimeWindow(void 0, subjectNotOnOrAfter, skew);
|
|
498
|
+
}
|
|
499
|
+
function validateSamlAudience(extract, expectedAudience) {
|
|
500
|
+
if (expectedAudience !== void 0) {
|
|
501
|
+
if (!toStringArray(extract?.audience).includes(expectedAudience)) throw new Error("SAML assertion audience does not match this service provider.");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function validateSamlRecipient(extract, expectedAcsUrl) {
|
|
505
|
+
if (expectedAcsUrl !== void 0) {
|
|
506
|
+
const normalizedAcsUrl = normalizeSamlUrl(expectedAcsUrl);
|
|
507
|
+
const recipient = extract?.subjectConfirmation?.recipient;
|
|
508
|
+
if (typeof recipient !== "string" || normalizeSamlUrl(recipient) !== normalizedAcsUrl) throw new Error("SAML assertion recipient does not match this ACS URL.");
|
|
509
|
+
const destination = extract?.response?.destination;
|
|
510
|
+
if (typeof destination === "string" && normalizeSamlUrl(destination) !== normalizedAcsUrl) throw new Error("SAML response destination does not match this ACS URL.");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Enforce the connection's SAML security policy on a parsed assertion:
|
|
515
|
+
* weak-algorithm rejection, required assertion signature, required timestamp
|
|
516
|
+
* conditions, the NotBefore/NotOnOrAfter validity window (with clock skew),
|
|
517
|
+
* the expected audience, and recipient/destination matching the SP's ACS URL.
|
|
518
|
+
* Throws on the first violation.
|
|
519
|
+
* @internal
|
|
520
|
+
*/
|
|
521
|
+
function enforceGroupConnectionSamlSecurity(opts) {
|
|
522
|
+
enforceSamlAlgorithmPolicy(opts);
|
|
523
|
+
const security = asRecord(getSamlConfig(opts.config).security) ?? {};
|
|
524
|
+
validateSamlRequiredSignature(opts.extract, security);
|
|
525
|
+
validateSamlRequiredTimestamps(opts.extract, security);
|
|
526
|
+
validateSamlTimeWindow(opts.extract, security);
|
|
527
|
+
validateSamlAudience(opts.extract, opts.expectedAudience);
|
|
528
|
+
validateSamlRecipient(opts.extract, opts.expectedAcsUrl);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Reconstruct the HTTP-Redirect binding's signed octet string from the raw query
|
|
532
|
+
* string, in the SAML-mandated order (`SAMLRequest`|`SAMLResponse`, then
|
|
533
|
+
* `RelayState` if present, then `SigAlg`), preserving each value's exact
|
|
534
|
+
* URL-encoded bytes — re-encoding decoded values would not reproduce what the IdP
|
|
535
|
+
* signed. Returns `undefined` when no SAML message parameter is present, in which
|
|
536
|
+
* case message-signature verification stays fail-closed.
|
|
537
|
+
* @internal
|
|
538
|
+
*/
|
|
539
|
+
function reconstructRedirectOctetString(url) {
|
|
540
|
+
const raw = url.search.replace(/^\?/, "");
|
|
541
|
+
const encoded = /* @__PURE__ */ new Map();
|
|
542
|
+
for (const pair of raw.split("&")) {
|
|
543
|
+
if (pair.length === 0) continue;
|
|
544
|
+
const eq = pair.indexOf("=");
|
|
545
|
+
const key = eq === -1 ? pair : pair.slice(0, eq);
|
|
546
|
+
if (!encoded.has(key)) encoded.set(key, eq === -1 ? "" : pair.slice(eq + 1));
|
|
547
|
+
}
|
|
548
|
+
const message = encoded.has("SAMLRequest") ? `SAMLRequest=${encoded.get("SAMLRequest")}` : encoded.has("SAMLResponse") ? `SAMLResponse=${encoded.get("SAMLResponse")}` : void 0;
|
|
549
|
+
if (message === void 0) return;
|
|
550
|
+
const parts = [message];
|
|
551
|
+
if (encoded.has("RelayState")) parts.push(`RelayState=${encoded.get("RelayState")}`);
|
|
552
|
+
if (encoded.has("SigAlg")) parts.push(`SigAlg=${encoded.get("SigAlg")}`);
|
|
553
|
+
return parts.join("&");
|
|
554
|
+
}
|
|
555
|
+
function toSamlHttpRequest(request) {
|
|
556
|
+
return {
|
|
557
|
+
query: request.query,
|
|
558
|
+
body: request.body,
|
|
559
|
+
...request.binding === "redirect" ? { octetString: reconstructRedirectOctetString(request.url) } : {}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Create the IdP-bound SAML AuthnRequest for a group connection, returning the
|
|
564
|
+
* binding plus either a redirect URL (HTTP-Redirect) or POST endpoint/value
|
|
565
|
+
* (HTTP-POST), along with the encoded RelayState carrying the signed flow
|
|
566
|
+
* state and request id.
|
|
567
|
+
* @internal
|
|
568
|
+
*/
|
|
569
|
+
async function createGroupConnectionSamlSignInRequest(opts) {
|
|
570
|
+
const runtime = createGroupConnectionSamlRuntime({
|
|
571
|
+
rootUrl: opts.rootUrl,
|
|
572
|
+
source: opts.source,
|
|
573
|
+
config: opts.config
|
|
574
|
+
});
|
|
575
|
+
const binding = runtime.saml.idp?.connection?.redirect ? "redirect" : "post";
|
|
576
|
+
const loginRequest = await runtime.sp.createLoginRequest(runtime.idp, binding);
|
|
577
|
+
const relayState = encodeGroupSamlRelayState({
|
|
578
|
+
source: opts.source,
|
|
579
|
+
signature: opts.signature,
|
|
580
|
+
requestId: loginRequest.id,
|
|
581
|
+
state: opts.state,
|
|
582
|
+
redirectTo: opts.redirectTo
|
|
583
|
+
});
|
|
584
|
+
return {
|
|
585
|
+
requestId: loginRequest.id,
|
|
586
|
+
binding,
|
|
587
|
+
relayState,
|
|
588
|
+
redirectUrl: binding === "redirect" ? (() => {
|
|
589
|
+
const redirectUrl = new URL(loginRequest.context);
|
|
590
|
+
redirectUrl.searchParams.set("RelayState", relayState);
|
|
591
|
+
return redirectUrl.toString();
|
|
592
|
+
})() : void 0,
|
|
593
|
+
post: binding === "post" ? {
|
|
594
|
+
endpoint: loginRequest.entityEndpoint,
|
|
595
|
+
value: loginRequest.context
|
|
596
|
+
} : void 0
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Parse and partially validate a SAML login Response for a group connection:
|
|
601
|
+
* enforces the response size limit, builds the runtime, parses the response,
|
|
602
|
+
* attaches signature metadata, warns on weak algorithms, and decodes the
|
|
603
|
+
* RelayState. Does not itself enforce the full security policy — callers must
|
|
604
|
+
* also run {@link enforceGroupConnectionSamlSecurity}.
|
|
605
|
+
* @internal
|
|
606
|
+
*/
|
|
607
|
+
async function parseGroupConnectionSamlLoginResponse(opts) {
|
|
608
|
+
ensureSamlifyValidator();
|
|
609
|
+
const httpRequest = await readGroupConnectionSamlHttpRequest(opts.request);
|
|
610
|
+
enforceSamlResponseSize({
|
|
611
|
+
request: httpRequest,
|
|
612
|
+
config: opts.config
|
|
613
|
+
});
|
|
614
|
+
const runtime = createGroupConnectionSamlRuntime({
|
|
615
|
+
rootUrl: opts.rootUrl,
|
|
616
|
+
source: opts.source,
|
|
617
|
+
config: opts.config
|
|
618
|
+
});
|
|
619
|
+
const parsed = attachSamlSignatureMetadata(await runtime.sp.parseLoginResponse(runtime.idp, httpRequest.binding, toSamlHttpRequest(httpRequest)));
|
|
620
|
+
warnWeakSamlAlgorithms(parsed);
|
|
621
|
+
return {
|
|
622
|
+
...httpRequest,
|
|
623
|
+
runtime,
|
|
624
|
+
parsed,
|
|
625
|
+
relayState: decodeGroupSamlRelayStateOrThrow(httpRequest.relayState ?? null)
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
const WEAK_SAML_ALGORITHMS = new Set([
|
|
629
|
+
"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
|
|
630
|
+
"http://www.w3.org/2000/09/xmldsig#dsa-sha1",
|
|
631
|
+
"http://www.w3.org/2000/09/xmldsig#sha1",
|
|
632
|
+
"http://www.w3.org/2001/04/xmlenc#rsa-1_5",
|
|
633
|
+
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
|
|
634
|
+
]);
|
|
635
|
+
/**
|
|
636
|
+
* Warn when the SAML response uses weak cryptographic algorithms
|
|
637
|
+
* such as SHA-1, RSA 1.5, or 3DES. Algorithm-check failures are non-critical
|
|
638
|
+
* and swallowed so they never break the auth flow.
|
|
639
|
+
*/
|
|
640
|
+
function warnWeakSamlAlgorithms(parsed) {
|
|
641
|
+
try {
|
|
642
|
+
for (const metadata of signatureMetadataList(parsed.extract)) {
|
|
643
|
+
const sigAlg = metadata.signatureAlgorithm;
|
|
644
|
+
const digestAlg = metadata.digestAlgorithm;
|
|
645
|
+
if (sigAlg && WEAK_SAML_ALGORITHMS.has(sigAlg)) log("WARN", `[convex-auth] SAML response uses weak signature algorithm: ${sigAlg}. Consider upgrading your IdP to use RSA-SHA256 or stronger.`);
|
|
646
|
+
if (digestAlg && WEAK_SAML_ALGORITHMS.has(digestAlg)) log("WARN", `[convex-auth] SAML response uses weak digest algorithm: ${digestAlg}. Consider upgrading your IdP to use SHA-256 or stronger.`);
|
|
647
|
+
}
|
|
648
|
+
} catch {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Reject a SAML response/assertion whose signature or digest uses a known-weak
|
|
654
|
+
* algorithm (SHA-1, RSA-1.5, 3DES). Secure by default: rejection is the default
|
|
655
|
+
* and operators must explicitly set `security.weakAlgorithmHandling: "warn"` to
|
|
656
|
+
* downgrade to a warning (mirroring the `requireTimestamps` opt-out).
|
|
657
|
+
* @internal
|
|
658
|
+
*/
|
|
659
|
+
function enforceSamlAlgorithmPolicy(opts) {
|
|
660
|
+
if (getSamlSecurityConfig(opts.config).weakAlgorithmHandling === "warn") return;
|
|
661
|
+
for (const metadata of signatureMetadataList(opts.extract)) {
|
|
662
|
+
const sigAlg = metadata.signatureAlgorithm;
|
|
663
|
+
const digestAlg = metadata.digestAlgorithm;
|
|
664
|
+
if (sigAlg && WEAK_SAML_ALGORITHMS.has(sigAlg) || digestAlg && WEAK_SAML_ALGORITHMS.has(digestAlg)) throw new Error("SAML response uses a rejected weak cryptographic algorithm.");
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Bind the returned RelayState to the pending login request: throws unless its
|
|
669
|
+
* source matches the connection and its requestId matches the assertion's
|
|
670
|
+
* `InResponseTo`, defeating cross-connection and replayed responses.
|
|
671
|
+
* @internal
|
|
672
|
+
*/
|
|
673
|
+
function validateGroupConnectionSamlLoginRelayState(opts) {
|
|
674
|
+
if (opts.relayState.source.kind !== opts.source.kind || opts.relayState.source.id !== opts.source.id || opts.relayState.requestId !== opts.inResponseTo) throw new Error("SAML RelayState did not match the pending login request.");
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Parse an inbound SAML single-logout message (request and/or response) for a
|
|
678
|
+
* group connection, returning the parsed flow(s) alongside the runtime.
|
|
679
|
+
*
|
|
680
|
+
* SECURITY — SLO messages are treated as UNAUTHENTICATED. samlify only verifies
|
|
681
|
+
* an inbound `LogoutRequest`/`LogoutResponse` signature when the SP is built
|
|
682
|
+
* with `wantLogoutRequestSigned`/`wantLogoutResponseSigned`; both default to
|
|
683
|
+
* `false` and are intentionally left unset in {@link createSamlServiceProvider}
|
|
684
|
+
* (many IdPs do not sign logout messages, and requiring it would break those
|
|
685
|
+
* legitimate SLO flows). This is safe only because the sole caller,
|
|
686
|
+
* `handleSamlSlo` in the connection HTTP router, DOES NOT terminate any local
|
|
687
|
+
* session: for an inbound `LogoutRequest` it merely emits the protocol
|
|
688
|
+
* `LogoutResponse` acknowledgment, and for an inbound `LogoutResponse` it
|
|
689
|
+
* returns `204` and does nothing. A forged, replayed, or attacker-supplied
|
|
690
|
+
* logout message therefore cannot force-log-out a user.
|
|
691
|
+
*
|
|
692
|
+
* DO NOT wire session termination onto this parse result without FIRST requiring
|
|
693
|
+
* signed logout messages (via the `wantLogout*Signed` SP options above) and
|
|
694
|
+
* rejecting unsigned ones — otherwise an unauthenticated caller could trigger a
|
|
695
|
+
* logout-CSRF / denial-of-service.
|
|
696
|
+
* @internal
|
|
697
|
+
*/
|
|
698
|
+
async function parseGroupConnectionSamlLogoutMessage(opts) {
|
|
699
|
+
ensureSamlifyValidator();
|
|
700
|
+
const httpRequest = await readGroupConnectionSamlHttpRequest(opts.request);
|
|
701
|
+
const runtime = createGroupConnectionSamlRuntime({
|
|
702
|
+
rootUrl: opts.rootUrl,
|
|
703
|
+
source: opts.source,
|
|
704
|
+
config: opts.config,
|
|
705
|
+
relayState: httpRequest.relayState
|
|
706
|
+
});
|
|
707
|
+
const parsedRequest = httpRequest.hasSamlRequest ? await runtime.sp.parseLogoutRequest(runtime.idp, httpRequest.binding, toSamlHttpRequest(httpRequest)) : void 0;
|
|
708
|
+
const parsedResponse = httpRequest.hasSamlResponse ? await runtime.sp.parseLogoutResponse(runtime.idp, httpRequest.binding, toSamlHttpRequest(httpRequest)) : void 0;
|
|
709
|
+
return {
|
|
710
|
+
...httpRequest,
|
|
711
|
+
runtime,
|
|
712
|
+
parsedRequest,
|
|
713
|
+
parsedResponse
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Map a parsed SAML assertion's attributes (via the connection's attribute
|
|
718
|
+
* mapping, falling back to NameID for the subject) into a normalized profile.
|
|
719
|
+
* Throws if no subject can be resolved.
|
|
720
|
+
*
|
|
721
|
+
* @remarks
|
|
722
|
+
* `emailVerified` is always left `undefined`: SAML carries no `email_verified`
|
|
723
|
+
* claim, so the assertion alone never proves the email. Verification is decided
|
|
724
|
+
* downstream (`userOAuthImpl`) from whether the email's domain is a verified
|
|
725
|
+
* connection domain, keeping a SAML-asserted email unverified by default so it
|
|
726
|
+
* cannot drive cross-connection `verifiedEmail` account linking.
|
|
727
|
+
* @internal
|
|
728
|
+
*/
|
|
729
|
+
function profileFromSamlExtract(extract, mapping) {
|
|
730
|
+
const attributes = typeof extract?.attributes === "object" && extract.attributes !== null ? extract.attributes : {};
|
|
731
|
+
const resolveFirst = (...keys) => {
|
|
732
|
+
for (const key of keys) {
|
|
733
|
+
if (!key) continue;
|
|
734
|
+
const attribute = attributes[key];
|
|
735
|
+
const value = Array.isArray(attribute) ? attribute[0] : attribute;
|
|
736
|
+
if (value !== void 0) return value;
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
const fieldResolvers = {
|
|
740
|
+
email: () => resolveFirst(mapping?.email),
|
|
741
|
+
groups: () => normalizeStringArray(resolveFirst(mapping?.groups)),
|
|
742
|
+
name: () => resolveFirst(mapping?.name) ?? ([resolveFirst(mapping?.firstName), resolveFirst(mapping?.lastName)].filter(Boolean).join(" ") || void 0),
|
|
743
|
+
roles: () => normalizeStringArray(resolveFirst(mapping?.roles)),
|
|
744
|
+
subject: () => resolveFirst(mapping?.subject) ?? extract?.nameID
|
|
745
|
+
};
|
|
746
|
+
const subject = fieldResolvers.subject();
|
|
747
|
+
if (subject === void 0) throw new Error("SAML profile is missing a subject. Configure `attributeMapping.subject` or ensure the assertion includes a NameID.");
|
|
748
|
+
return finalizeNormalizedProfile({
|
|
749
|
+
id: subject,
|
|
750
|
+
email: fieldResolvers.email(),
|
|
751
|
+
emailVerified: void 0,
|
|
752
|
+
groups: fieldResolvers.groups(),
|
|
753
|
+
name: fieldResolvers.name(),
|
|
754
|
+
roles: fieldResolvers.roles(),
|
|
755
|
+
samlAttributes: attributes,
|
|
756
|
+
samlSessionIndex: extract?.sessionIndex?.sessionIndex
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
//#endregion
|
|
761
|
+
export { createGroupConnectionSamlMetadataXml, createGroupConnectionSamlSignInRequest, createSamlPostBindingResponse, createServiceProviderMetadata, encodeGroupSamlRelayState, enforceGroupConnectionSamlSecurity, getSamlServiceProviderOptions, parseGroupConnectionSamlLoginResponse, parseGroupConnectionSamlLogoutMessage, parseSamlIdpMetadataChecked, profileFromSamlExtract, samlReplayIdentifiers, validateGroupConnectionSamlLoginRelayState };
|
|
762
|
+
//# sourceMappingURL=saml.js.map
|