@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,699 @@
|
|
|
1
|
+
import { LOG_LEVELS } from "../shared/log.js";
|
|
2
|
+
import { ErrorCode } from "../shared/codes.js";
|
|
3
|
+
import { getAuthenticatedUserIdOrNull } from "./identity/claims.js";
|
|
4
|
+
import { requireEnv } from "./env.js";
|
|
5
|
+
import { appUrlFromEnv } from "./url.js";
|
|
6
|
+
import { queueAuthEvent } from "./events.js";
|
|
7
|
+
import { log } from "./log.js";
|
|
8
|
+
import { encodeRefreshToken, refreshTokenExpirationTime } from "./token/refresh.js";
|
|
9
|
+
import { setActiveSpanAttributes } from "./utils/span.js";
|
|
10
|
+
import { buildSessionIdentity, finalizeSessionIssuance, getAuthSessionId, sessionExpirationTime } from "./session/lifecycle.js";
|
|
11
|
+
import { authFlowError } from "../shared/errors.js";
|
|
12
|
+
import { toConvexError } from "./errors.js";
|
|
13
|
+
import { buildKnownSignInIdentityAttributes } from "./telemetry.js";
|
|
14
|
+
import { consumeVerifierById, mutatePasskeyBeginAssertion, mutatePasskeyBeginRegistration, mutatePasskeyBeginSignIn, mutatePasskeyCompleteAssertion, mutatePasskeyCompleteRegistration } from "./component/factor/db.js";
|
|
15
|
+
import { MAX_WEBAUTHN_CREDENTIALS_PER_USER, MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH } from "../shared/webauthn.js";
|
|
16
|
+
import { ConvexError } from "convex/values";
|
|
17
|
+
import { sha256 } from "@oslojs/crypto/sha2";
|
|
18
|
+
import { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding } from "@oslojs/encoding";
|
|
19
|
+
import { decodePKIXECDSASignature, decodeSEC1PublicKey, p256, verifyECDSASignature } from "@oslojs/crypto/ecdsa";
|
|
20
|
+
import { RSAPublicKey, decodePKCS1RSAPublicKey, sha256ObjectIdentifier, verifyRSASSAPKCS1v15Signature } from "@oslojs/crypto/rsa";
|
|
21
|
+
import { decodeCBORToNativeValueNoLeftoverBytes } from "@oslojs/cbor";
|
|
22
|
+
import { COSEKeyType, ClientDataType, coseAlgorithmES256, coseAlgorithmRS256, createAssertionSignatureMessage, parseAttestationObject, parseAuthenticatorData, parseClientDataJSON } from "@oslojs/webauthn";
|
|
23
|
+
|
|
24
|
+
//#region src/server/webauthn.ts
|
|
25
|
+
/**
|
|
26
|
+
* Server-side WebAuthn ceremony logic.
|
|
27
|
+
*
|
|
28
|
+
* Handles the four phases of the WebAuthn flow:
|
|
29
|
+
* 1. registerOptions — generate PublicKeyCredentialCreationOptions
|
|
30
|
+
* 2. registerVerify — verify attestation and store credential
|
|
31
|
+
* 3. authOptions — generate PublicKeyCredentialRequestOptions
|
|
32
|
+
* 4. authVerify — verify assertion signature and sign in
|
|
33
|
+
*
|
|
34
|
+
* Uses `@oslojs/webauthn` for attestation/assertion parsing and
|
|
35
|
+
* `@oslojs/crypto` for signature verification.
|
|
36
|
+
*
|
|
37
|
+
* @module
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* The WebAuthn provider has three single-word flows:
|
|
41
|
+
*
|
|
42
|
+
* - `register` — issue a WebAuthn registration challenge (phase 1).
|
|
43
|
+
* - `signIn` — issue a WebAuthn authentication challenge (phase 1).
|
|
44
|
+
* - `verify` — consume the WebAuthn response (phase 2). The server
|
|
45
|
+
* auto-detects whether this completes a registration
|
|
46
|
+
* (`attestationObject` present) or a sign-in
|
|
47
|
+
* (`signature` + `credentialId` present).
|
|
48
|
+
*/
|
|
49
|
+
const WEBAUTHN_FLOWS = [
|
|
50
|
+
"register",
|
|
51
|
+
"signIn",
|
|
52
|
+
"verify"
|
|
53
|
+
];
|
|
54
|
+
const requireStringParam = (value, name) => {
|
|
55
|
+
if (typeof value !== "string") throw convexError(ErrorCode.INVALID_PARAMETERS, `Missing \`${name}\` parameter.`);
|
|
56
|
+
return value;
|
|
57
|
+
};
|
|
58
|
+
const convexError = (code, message) => toConvexError(authFlowError(code, message));
|
|
59
|
+
const asConvexError = (error, code, message) => error instanceof ConvexError ? error : error instanceof Error ? toConvexError(authFlowError(code, error.message || message)) : convexError(code, message);
|
|
60
|
+
const logPasskeyError = (err) => log(LOG_LEVELS.ERROR, "passkey error:", err instanceof Error ? err.message : String(err));
|
|
61
|
+
function resolveRpOptions(provider) {
|
|
62
|
+
try {
|
|
63
|
+
let appUrl;
|
|
64
|
+
if (provider.options.rpId === void 0 || provider.options.origin === void 0) try {
|
|
65
|
+
appUrl = appUrlFromEnv();
|
|
66
|
+
} catch {
|
|
67
|
+
throw convexError(ErrorCode.PASSKEY_MISSING_CONFIG, "Passkey provider requires APP_URL env var (your frontend URL) or explicit rpId and origin in the provider config. CONVEX_SITE_URL cannot be used because WebAuthn RP ID must match the frontend domain.");
|
|
68
|
+
}
|
|
69
|
+
const appHostname = appUrl ? new URL(appUrl).hostname : void 0;
|
|
70
|
+
return {
|
|
71
|
+
rpName: provider.options.rpName ?? appHostname ?? provider.options.rpId ?? "localhost",
|
|
72
|
+
rpId: provider.options.rpId ?? appHostname ?? "localhost",
|
|
73
|
+
origin: provider.options.origin ?? appUrl ?? "http://localhost",
|
|
74
|
+
challengeExpirationMs: provider.options.challengeExpirationMs ?? 3e5,
|
|
75
|
+
registration: {
|
|
76
|
+
userVerification: provider.options.registration.userVerification ?? "required",
|
|
77
|
+
residentKey: provider.options.registration.residentKey ?? "preferred",
|
|
78
|
+
algorithms: provider.options.registration.algorithms ?? [coseAlgorithmES256, coseAlgorithmRS256],
|
|
79
|
+
...provider.options.registration.authenticatorAttachment ? { authenticatorAttachment: provider.options.registration.authenticatorAttachment } : {},
|
|
80
|
+
...provider.options.registration.hints ? { hints: provider.options.registration.hints } : {},
|
|
81
|
+
...provider.options.registration.attestation ? { attestation: provider.options.registration.attestation } : {}
|
|
82
|
+
},
|
|
83
|
+
authentication: {
|
|
84
|
+
userVerification: provider.options.authentication.userVerification ?? "required",
|
|
85
|
+
...provider.options.authentication.hints ? { hints: provider.options.authentication.hints } : {}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw asConvexError(error, ErrorCode.PASSKEY_MISSING_CONFIG, "Passkey relying party configuration is invalid.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function verifySameOrigin(clientData) {
|
|
93
|
+
if (clientData.crossOrigin === true) throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, "Cross-origin WebAuthn ceremonies are not supported.");
|
|
94
|
+
return clientData;
|
|
95
|
+
}
|
|
96
|
+
function verifyClientDataType(clientData, expectedType, label) {
|
|
97
|
+
if (clientData.type !== expectedType) throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, `Invalid client data type: expected ${label}`);
|
|
98
|
+
return clientData;
|
|
99
|
+
}
|
|
100
|
+
function verifyOrigin(clientData, rp) {
|
|
101
|
+
const allowed = Array.isArray(rp.origin) ? rp.origin : [rp.origin];
|
|
102
|
+
if (!allowed.includes(clientData.origin)) throw convexError(ErrorCode.PASSKEY_INVALID_ORIGIN, `Invalid origin: ${clientData.origin}, expected one of: ${allowed.join(", ")}`);
|
|
103
|
+
return clientData;
|
|
104
|
+
}
|
|
105
|
+
async function verifyAndConsumeChallenge(clientData, ctx, verifierValue) {
|
|
106
|
+
const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(clientData.challenge)));
|
|
107
|
+
let doc;
|
|
108
|
+
try {
|
|
109
|
+
doc = await consumeVerifierById(ctx, verifierValue, challengeHash);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error("[auth] passkey error:", err);
|
|
112
|
+
throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, "Invalid or expired passkey challenge.");
|
|
113
|
+
}
|
|
114
|
+
if (!doc) throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, "Invalid or expired passkey challenge.");
|
|
115
|
+
return clientData;
|
|
116
|
+
}
|
|
117
|
+
function verifyRpId(authData, rpId) {
|
|
118
|
+
if (!authData.verifyRelyingPartyIdHash(rpId)) throw convexError(ErrorCode.PASSKEY_RP_MISMATCH, "Relying party ID mismatch.");
|
|
119
|
+
return authData;
|
|
120
|
+
}
|
|
121
|
+
function verifyUserFlags(authData, userVerification) {
|
|
122
|
+
if (!authData.userPresent) throw convexError(ErrorCode.PASSKEY_USER_PRESENCE, "User presence flag not set.");
|
|
123
|
+
if (userVerification === "required" && !authData.userVerified) throw convexError(ErrorCode.PASSKEY_USER_VERIFICATION, "User verification required but not performed.");
|
|
124
|
+
return authData;
|
|
125
|
+
}
|
|
126
|
+
/** @internal */
|
|
127
|
+
function parseBackupState(authenticatorData) {
|
|
128
|
+
if (authenticatorData.byteLength < 33) throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, "Authenticator data is too short.");
|
|
129
|
+
const flags = authenticatorData[32];
|
|
130
|
+
const backupEligible = (flags & 8) !== 0;
|
|
131
|
+
const backedUp = (flags & 16) !== 0;
|
|
132
|
+
if (backedUp && !backupEligible) throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, "Authenticator backup state is invalid.");
|
|
133
|
+
return {
|
|
134
|
+
deviceType: backupEligible ? "multiDevice" : "singleDevice",
|
|
135
|
+
backedUp
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function extractAttestationAuthenticatorData(attestationObject) {
|
|
139
|
+
try {
|
|
140
|
+
const decoded = decodeCBORToNativeValueNoLeftoverBytes(attestationObject, 4);
|
|
141
|
+
if (typeof decoded !== "object" || decoded === null || !("authData" in decoded) || !(decoded.authData instanceof Uint8Array)) throw new Error("Invalid or missing authData");
|
|
142
|
+
return decoded.authData;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw asConvexError(error, ErrorCode.PASSKEY_INVALID_CLIENT_DATA, "Invalid attestation authenticator data.");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** @internal */
|
|
148
|
+
function validateCredentialAlgorithm(algorithm, configuredAlgorithms) {
|
|
149
|
+
if (!configuredAlgorithms.includes(algorithm)) throw convexError(ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM, `Credential algorithm ${algorithm} was not offered by the server.`);
|
|
150
|
+
}
|
|
151
|
+
function resolveWebAuthnDispatch(params) {
|
|
152
|
+
const flow = params.flow;
|
|
153
|
+
if (typeof flow === "string" && WEBAUTHN_FLOWS.includes(flow)) return { flow };
|
|
154
|
+
throw convexError(ErrorCode.PASSKEY_MISSING_FLOW, "Missing `flow` parameter. Expected one of: " + WEBAUTHN_FLOWS.join(", "));
|
|
155
|
+
}
|
|
156
|
+
function requirePasskeyVerifier(verifier) {
|
|
157
|
+
if (verifier != null) return verifier;
|
|
158
|
+
throw convexError(ErrorCode.PASSKEY_MISSING_VERIFIER, "Missing verifier for passkey operation.");
|
|
159
|
+
}
|
|
160
|
+
async function requireAuthenticatedUserId(ctx) {
|
|
161
|
+
try {
|
|
162
|
+
const userId = await getAuthenticatedUserIdOrNull(ctx);
|
|
163
|
+
if (userId === null) throw convexError(ErrorCode.PASSKEY_AUTH_REQUIRED, "Sign in first, then add a passkey to your account.");
|
|
164
|
+
return userId;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.error("[auth] passkey error:", err);
|
|
167
|
+
throw convexError(ErrorCode.PASSKEY_AUTH_REQUIRED, "Sign in first, then add a passkey to your account.");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function resolveRegistrationPublicKeyBytes(publicKey, algorithm) {
|
|
171
|
+
if (algorithm === coseAlgorithmES256) {
|
|
172
|
+
const ec2 = publicKey.ec2();
|
|
173
|
+
const xBytes = new Uint8Array(32);
|
|
174
|
+
let vx = ec2.x;
|
|
175
|
+
for (let i = 31; i >= 0; i--) {
|
|
176
|
+
xBytes[i] = Number(vx & 255n);
|
|
177
|
+
vx >>= 8n;
|
|
178
|
+
}
|
|
179
|
+
const yBytes = new Uint8Array(32);
|
|
180
|
+
let vy = ec2.y;
|
|
181
|
+
for (let i = 31; i >= 0; i--) {
|
|
182
|
+
yBytes[i] = Number(vy & 255n);
|
|
183
|
+
vy >>= 8n;
|
|
184
|
+
}
|
|
185
|
+
const bytes = new Uint8Array(65);
|
|
186
|
+
bytes[0] = 4;
|
|
187
|
+
bytes.set(xBytes, 1);
|
|
188
|
+
bytes.set(yBytes, 33);
|
|
189
|
+
return bytes;
|
|
190
|
+
}
|
|
191
|
+
if (algorithm === coseAlgorithmRS256) {
|
|
192
|
+
const rsa = publicKey.rsa();
|
|
193
|
+
return new RSAPublicKey(rsa.n, rsa.e).encodePKCS1();
|
|
194
|
+
}
|
|
195
|
+
throw convexError(ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM, `Unsupported algorithm: ${algorithm}`);
|
|
196
|
+
}
|
|
197
|
+
function verifyAssertionSignature(passkey, signature, messageHash) {
|
|
198
|
+
try {
|
|
199
|
+
if (passkey.algorithm === coseAlgorithmES256) {
|
|
200
|
+
if (!verifyECDSASignature(decodeSEC1PublicKey(p256, new Uint8Array(passkey.publicKey)), messageHash, decodePKIXECDSASignature(signature))) throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, "Invalid passkey signature.");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (passkey.algorithm === coseAlgorithmRS256) {
|
|
204
|
+
if (!verifyRSASSAPKCS1v15Signature(decodePKCS1RSAPublicKey(new Uint8Array(passkey.publicKey)), sha256ObjectIdentifier, messageHash, signature)) throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, "Invalid passkey signature.");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
throw convexError(ErrorCode.PASSKEY_UNSUPPORTED_ALGORITHM, `Unsupported algorithm: ${passkey.algorithm}`);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (error instanceof ConvexError) throw error;
|
|
210
|
+
throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, "Invalid passkey signature.");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const DUMMY_ES256_PUBLIC_KEY = decodeBase64urlIgnorePadding("BGsX0fLhLEJH-Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT-NC4v4af5uO5-tKfA-eFivOM1drMV7Oy7ZAaDe_UfU");
|
|
214
|
+
const DUMMY_RS256_PUBLIC_KEY = decodeBase64urlIgnorePadding("MIIBCgKCAQEAxumpcTeR9zlJNbNZinnjj_eb75YcIeNHf92QdRZ86g_wGzz6WW4VmpRSrkMJC1u3EYdN7rRSw1tveNaw7g6NTfaLjJdyrxkpNdx6niAXq8dMHGe5-Y9MyoGNwP0T_c6PrBdu_M-xGEtpDIG9GwNCM9RHsOxcf7PxVaHBZqR_BoWWLTHrVw6weXBYALsp3iz-jFDl5mG7JW9G_sKVr9dV0BAzeNMAKJEAoEO4swJB4qWGhHaSzCyy8YMngsuQrO9L5L_m8dG0GjYjjkV1G2aoRIl8VR7nnJdGFaAlSgqO_icJN6QY3WS2aalDEr1yRx2mjJPRUIr6rZMCfsuyjq1NZwIDAQAB");
|
|
215
|
+
function copyArrayBuffer(bytes) {
|
|
216
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
217
|
+
}
|
|
218
|
+
function signatureIsValid(credential, signature, messageHash) {
|
|
219
|
+
try {
|
|
220
|
+
verifyAssertionSignature(credential, signature, messageHash);
|
|
221
|
+
return true;
|
|
222
|
+
} catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Perform both supported verification algorithms for real and unknown IDs so
|
|
228
|
+
* the selected algorithm is not a credential-existence timing oracle.
|
|
229
|
+
*/
|
|
230
|
+
function verifyAssertionSignatureUniformly(passkey, signature, messageHash) {
|
|
231
|
+
const es256Valid = signatureIsValid(passkey?.algorithm === coseAlgorithmES256 ? passkey : {
|
|
232
|
+
algorithm: coseAlgorithmES256,
|
|
233
|
+
publicKey: copyArrayBuffer(DUMMY_ES256_PUBLIC_KEY)
|
|
234
|
+
}, signature, messageHash);
|
|
235
|
+
const rs256Valid = signatureIsValid(passkey?.algorithm === coseAlgorithmRS256 ? passkey : {
|
|
236
|
+
algorithm: coseAlgorithmRS256,
|
|
237
|
+
publicKey: copyArrayBuffer(DUMMY_RS256_PUBLIC_KEY)
|
|
238
|
+
}, signature, messageHash);
|
|
239
|
+
if (!(passkey?.algorithm === coseAlgorithmES256 ? es256Valid : passkey?.algorithm === coseAlgorithmRS256 ? rs256Valid : false)) throw convexError(ErrorCode.PASSKEY_INVALID_SIGNATURE, "Invalid passkey signature.");
|
|
240
|
+
}
|
|
241
|
+
/** @internal */
|
|
242
|
+
function validateBackupEligibility(storedDeviceType, assertionDeviceType) {
|
|
243
|
+
if (storedDeviceType !== assertionDeviceType) throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, "Authenticator backup eligibility changed for an existing credential.");
|
|
244
|
+
}
|
|
245
|
+
/** @internal */
|
|
246
|
+
async function assertStoredAttestationTrusted(policy, evidence) {
|
|
247
|
+
if (!evidence) throw convexError(ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION, "This credential predates the current attestation policy and must be registered again.");
|
|
248
|
+
try {
|
|
249
|
+
await policy.verifier.assertTrusted(evidence);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
throw asConvexError(error, ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION, "Authenticator attestation is no longer trusted.");
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const EMAIL_ALLOW_REAL_CREDENTIALS = 32 / 2;
|
|
255
|
+
const MAX_EMAIL_LENGTH = 254;
|
|
256
|
+
async function keyedDigest(key, value) {
|
|
257
|
+
const encoder = new TextEncoder();
|
|
258
|
+
return new Uint8Array(await crypto.subtle.sign("HMAC", key, encoder.encode(value)));
|
|
259
|
+
}
|
|
260
|
+
async function keyedBytes(key, domain, length) {
|
|
261
|
+
const blocks = await Promise.all(Array.from({ length: Math.ceil(length / 32) }, (_, block) => keyedDigest(key, `${domain}:block:${block}`)));
|
|
262
|
+
const bytes = new Uint8Array(length);
|
|
263
|
+
for (let offset = 0; offset < length; offset += 32) bytes.set(blocks[offset / 32].subarray(0, Math.min(32, length - offset)), offset);
|
|
264
|
+
return bytes;
|
|
265
|
+
}
|
|
266
|
+
function normalizeEmail(value) {
|
|
267
|
+
const email = requireStringParam(value, "email").trim().toLowerCase();
|
|
268
|
+
if (email.length === 0 || email.length > MAX_EMAIL_LENGTH) throw convexError(ErrorCode.INVALID_PARAMETERS, "Email must be between 1 and 254 characters.");
|
|
269
|
+
return email;
|
|
270
|
+
}
|
|
271
|
+
async function deriveEmailAllowCredentials(normalizedEmail, rpId, realCredentialIds) {
|
|
272
|
+
const secret = requireEnv("JWT_PRIVATE_KEY");
|
|
273
|
+
const emailSeed = encodeBase64urlNoPadding(new Uint8Array(sha256(new TextEncoder().encode(normalizedEmail))));
|
|
274
|
+
const realIds = realCredentialIds.flatMap((id) => {
|
|
275
|
+
try {
|
|
276
|
+
const byteLength = decodeBase64urlIgnorePadding(id).byteLength;
|
|
277
|
+
return byteLength >= 1 && byteLength <= MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH ? [{
|
|
278
|
+
id,
|
|
279
|
+
byteLength
|
|
280
|
+
}] : [];
|
|
281
|
+
} catch {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
}).slice(0, EMAIL_ALLOW_REAL_CREDENTIALS);
|
|
285
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), {
|
|
286
|
+
name: "HMAC",
|
|
287
|
+
hash: "SHA-256"
|
|
288
|
+
}, false, ["sign"]);
|
|
289
|
+
const makeCredential = async (id, discriminator) => ({
|
|
290
|
+
type: "public-key",
|
|
291
|
+
id,
|
|
292
|
+
order: encodeBase64urlNoPadding(await keyedDigest(key, `convex-auth:webauthn-order:v2:${rpId}:${emailSeed}:${discriminator}:${id}`))
|
|
293
|
+
});
|
|
294
|
+
const lengthSeed = await keyedDigest(key, `convex-auth:webauthn-lengths:v2:${rpId}:${emailSeed}`);
|
|
295
|
+
const credentials = (await Promise.all(Array.from({ length: EMAIL_ALLOW_REAL_CREDENTIALS }, async (_, pairIndex) => {
|
|
296
|
+
const real = realIds[pairIndex];
|
|
297
|
+
const derivedLength = 1 + (lengthSeed[pairIndex * 2] << 8 | lengthSeed[pairIndex * 2 + 1]) % MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH;
|
|
298
|
+
const byteLength = real?.byteLength ?? derivedLength;
|
|
299
|
+
return [real ? await makeCredential(real.id, `real:${pairIndex}`) : await keyedBytes(key, `convex-auth:webauthn-decoy:v2:${rpId}:${emailSeed}:pair:${pairIndex}:first`, byteLength).then((bytes) => makeCredential(encodeBase64urlNoPadding(bytes), `pair:${pairIndex}:first`)), await keyedBytes(key, `convex-auth:webauthn-decoy:v2:${rpId}:${emailSeed}:pair:${pairIndex}:companion`, byteLength).then((bytes) => makeCredential(encodeBase64urlNoPadding(bytes), `pair:${pairIndex}:companion`))];
|
|
300
|
+
}))).flat();
|
|
301
|
+
credentials.sort((a, b) => a.order.localeCompare(b.order));
|
|
302
|
+
return credentials.map(({ type, id }) => ({
|
|
303
|
+
type,
|
|
304
|
+
id
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Drive the passkey provider's `register` / `signIn` / `verify` flow.
|
|
309
|
+
*
|
|
310
|
+
* Dispatches on `args.params.flow`; for `verify` it auto-detects whether the
|
|
311
|
+
* response completes a registration or a sign-in (see {@link WEBAUTHN_FLOWS}).
|
|
312
|
+
*
|
|
313
|
+
* @param ctx - Auth-enriched action context.
|
|
314
|
+
* @param provider - The resolved WebAuthn provider config.
|
|
315
|
+
* @param args - The flow params and, for `verify`, the issued challenge verifier.
|
|
316
|
+
* @returns A WebAuthn challenge (`webauthnOptions`) or a signed-in session.
|
|
317
|
+
*/
|
|
318
|
+
async function handleWebAuthn(ctx, provider, args) {
|
|
319
|
+
const params = args.params ?? {};
|
|
320
|
+
const dispatch = resolveWebAuthnDispatch(params);
|
|
321
|
+
const handleRegisterVerify = async () => {
|
|
322
|
+
const userId = await requireAuthenticatedUserId(ctx);
|
|
323
|
+
const rp = resolveRpOptions(provider);
|
|
324
|
+
const verifier = requirePasskeyVerifier(args.verifier);
|
|
325
|
+
const encodedClientDataJSON = requireStringParam(params.clientDataJSON, "clientDataJSON");
|
|
326
|
+
const clientData = parseClientDataJSON(decodeBase64urlIgnorePadding(encodedClientDataJSON));
|
|
327
|
+
verifyClientDataType(clientData, ClientDataType.Create, "webauthn.create");
|
|
328
|
+
verifySameOrigin(clientData);
|
|
329
|
+
verifyOrigin(clientData, rp);
|
|
330
|
+
await verifyAndConsumeChallenge(clientData, ctx, verifier);
|
|
331
|
+
const encodedAttestationObject = requireStringParam(params.attestationObject, "attestationObject");
|
|
332
|
+
const attestationObjectBytes = decodeBase64urlIgnorePadding(encodedAttestationObject);
|
|
333
|
+
const rawAuthenticatorData = extractAttestationAuthenticatorData(attestationObjectBytes);
|
|
334
|
+
const authData = parseAttestationObject(attestationObjectBytes).authenticatorData;
|
|
335
|
+
verifyRpId(authData, rp.rpId);
|
|
336
|
+
verifyUserFlags(authData, rp.registration.userVerification);
|
|
337
|
+
if (authData.credential == null) throw convexError(ErrorCode.PASSKEY_NO_CREDENTIAL, "No credential in attestation.");
|
|
338
|
+
const credential = authData.credential;
|
|
339
|
+
if (credential.id.byteLength < 1 || credential.id.byteLength > MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH) throw convexError(ErrorCode.PASSKEY_INVALID_CLIENT_DATA, `Credential ID must be between 1 and ${MAX_WEBAUTHN_CREDENTIAL_ID_LENGTH} bytes.`);
|
|
340
|
+
const credentialId = encodeBase64urlNoPadding(credential.id);
|
|
341
|
+
const publicKey = credential.publicKey;
|
|
342
|
+
const algorithm = publicKey.isAlgorithmDefined() ? publicKey.algorithm() : publicKey.type() === COSEKeyType.EC2 ? coseAlgorithmES256 : publicKey.type() === COSEKeyType.RSA ? coseAlgorithmRS256 : coseAlgorithmES256;
|
|
343
|
+
validateCredentialAlgorithm(algorithm, rp.registration.algorithms);
|
|
344
|
+
const publicKeyBytes = resolveRegistrationPublicKeyBytes(publicKey, algorithm);
|
|
345
|
+
const backupState = parseBackupState(rawAuthenticatorData);
|
|
346
|
+
let attestationEvidence;
|
|
347
|
+
if (rp.registration.attestation) {
|
|
348
|
+
const { verifier: attestationVerifier } = rp.registration.attestation;
|
|
349
|
+
try {
|
|
350
|
+
attestationEvidence = {
|
|
351
|
+
...await attestationVerifier.verify({
|
|
352
|
+
clientDataJSON: encodedClientDataJSON,
|
|
353
|
+
attestationObject: encodedAttestationObject,
|
|
354
|
+
credentialId,
|
|
355
|
+
transports: params.transports,
|
|
356
|
+
expectedChallenge: encodeBase64urlNoPadding(clientData.challenge),
|
|
357
|
+
expectedOrigin: rp.origin,
|
|
358
|
+
expectedRpId: rp.rpId,
|
|
359
|
+
requireUserVerification: rp.registration.userVerification === "required",
|
|
360
|
+
supportedAlgorithms: rp.registration.algorithms
|
|
361
|
+
}),
|
|
362
|
+
verifier: attestationVerifier.id,
|
|
363
|
+
verifiedAt: Date.now(),
|
|
364
|
+
status: "trusted"
|
|
365
|
+
};
|
|
366
|
+
} catch (error) {
|
|
367
|
+
throw asConvexError(error, ErrorCode.PASSKEY_UNTRUSTED_ATTESTATION, "Authenticator attestation is not trusted.");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const replaceSessionId = await getAuthSessionId(ctx) ?? void 0;
|
|
371
|
+
let completed;
|
|
372
|
+
try {
|
|
373
|
+
completed = await mutatePasskeyCompleteRegistration(ctx, {
|
|
374
|
+
userId,
|
|
375
|
+
credentialId,
|
|
376
|
+
publicKey: publicKeyBytes.buffer.slice(publicKeyBytes.byteOffset, publicKeyBytes.byteOffset + publicKeyBytes.byteLength),
|
|
377
|
+
algorithm,
|
|
378
|
+
counter: authData.signatureCounter,
|
|
379
|
+
transports: params.transports,
|
|
380
|
+
deviceType: backupState.deviceType,
|
|
381
|
+
backedUp: backupState.backedUp,
|
|
382
|
+
name: params.passkeyName,
|
|
383
|
+
...attestationEvidence ? { attestation: attestationEvidence } : {},
|
|
384
|
+
createdAt: Date.now(),
|
|
385
|
+
replaceSessionId,
|
|
386
|
+
sessionExpirationTime: sessionExpirationTime(ctx.auth.config),
|
|
387
|
+
refreshTokenExpirationTime: refreshTokenExpirationTime(ctx.auth.config)
|
|
388
|
+
});
|
|
389
|
+
const passkeyId = completed.passkeyId;
|
|
390
|
+
await queueAuthEvent(ctx, ctx.auth.config, {
|
|
391
|
+
kind: "passkey.added",
|
|
392
|
+
actor: {
|
|
393
|
+
type: "user",
|
|
394
|
+
id: userId
|
|
395
|
+
},
|
|
396
|
+
subject: {
|
|
397
|
+
type: "passkey",
|
|
398
|
+
id: passkeyId
|
|
399
|
+
},
|
|
400
|
+
targets: [{
|
|
401
|
+
kind: "user",
|
|
402
|
+
id: userId
|
|
403
|
+
}],
|
|
404
|
+
outcome: "success",
|
|
405
|
+
data: {
|
|
406
|
+
passkeyId,
|
|
407
|
+
credentialId
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
} catch (err) {
|
|
411
|
+
logPasskeyError(err);
|
|
412
|
+
if (err instanceof ConvexError) throw err;
|
|
413
|
+
throw convexError(ErrorCode.INTERNAL_ERROR, "An unexpected error occurred.");
|
|
414
|
+
}
|
|
415
|
+
const signedInUserId = userId;
|
|
416
|
+
const sessionId = completed.sessionId;
|
|
417
|
+
setActiveSpanAttributes({
|
|
418
|
+
"auth.signin.result": "success",
|
|
419
|
+
...buildKnownSignInIdentityAttributes(ctx.auth.config, {
|
|
420
|
+
userId: signedInUserId,
|
|
421
|
+
sessionId
|
|
422
|
+
}, completed.user.email)
|
|
423
|
+
});
|
|
424
|
+
if (completed.replacedSessionId !== void 0) {
|
|
425
|
+
const replacedSessionId = completed.replacedSessionId;
|
|
426
|
+
await queueAuthEvent(ctx, ctx.auth.config, {
|
|
427
|
+
kind: "session.invalidated",
|
|
428
|
+
actor: { type: "system" },
|
|
429
|
+
subject: {
|
|
430
|
+
type: "session",
|
|
431
|
+
id: replacedSessionId
|
|
432
|
+
},
|
|
433
|
+
targets: [{
|
|
434
|
+
kind: "user",
|
|
435
|
+
id: signedInUserId
|
|
436
|
+
}, {
|
|
437
|
+
kind: "session",
|
|
438
|
+
id: replacedSessionId
|
|
439
|
+
}],
|
|
440
|
+
outcome: "success",
|
|
441
|
+
data: {
|
|
442
|
+
userId: signedInUserId,
|
|
443
|
+
reason: "replaced"
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
await queueAuthEvent(ctx, ctx.auth.config, {
|
|
448
|
+
kind: "session.signed_in",
|
|
449
|
+
actor: {
|
|
450
|
+
type: "user",
|
|
451
|
+
id: signedInUserId
|
|
452
|
+
},
|
|
453
|
+
subject: {
|
|
454
|
+
type: "session",
|
|
455
|
+
id: sessionId
|
|
456
|
+
},
|
|
457
|
+
targets: [{
|
|
458
|
+
kind: "user",
|
|
459
|
+
id: signedInUserId
|
|
460
|
+
}, {
|
|
461
|
+
kind: "session",
|
|
462
|
+
id: sessionId
|
|
463
|
+
}],
|
|
464
|
+
outcome: "success",
|
|
465
|
+
data: { provider: "session" }
|
|
466
|
+
});
|
|
467
|
+
return {
|
|
468
|
+
kind: "signedIn",
|
|
469
|
+
session: await finalizeSessionIssuance(ctx.auth.config, {
|
|
470
|
+
userId: signedInUserId,
|
|
471
|
+
sessionId,
|
|
472
|
+
identity: buildSessionIdentity(signedInUserId, sessionId, completed.user),
|
|
473
|
+
refreshToken: encodeRefreshToken(completed.refreshTokenId, sessionId)
|
|
474
|
+
})
|
|
475
|
+
};
|
|
476
|
+
};
|
|
477
|
+
const handleAuthVerify = async () => {
|
|
478
|
+
const rp = resolveRpOptions(provider);
|
|
479
|
+
const verifier = requirePasskeyVerifier(args.verifier);
|
|
480
|
+
const clientDataJSON = decodeBase64urlIgnorePadding(requireStringParam(params.clientDataJSON, "clientDataJSON"));
|
|
481
|
+
const clientData = parseClientDataJSON(clientDataJSON);
|
|
482
|
+
verifyClientDataType(clientData, ClientDataType.Get, "webauthn.get");
|
|
483
|
+
verifySameOrigin(clientData);
|
|
484
|
+
verifyOrigin(clientData, rp);
|
|
485
|
+
const credentialId = requireStringParam(params.credentialId, "credentialId");
|
|
486
|
+
const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(clientData.challenge)));
|
|
487
|
+
let assertion;
|
|
488
|
+
try {
|
|
489
|
+
assertion = await mutatePasskeyBeginAssertion(ctx, {
|
|
490
|
+
verifierId: verifier,
|
|
491
|
+
expectedChallenge: challengeHash,
|
|
492
|
+
credentialId
|
|
493
|
+
});
|
|
494
|
+
} catch (error) {
|
|
495
|
+
logPasskeyError(error);
|
|
496
|
+
throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, "Invalid or expired passkey challenge.");
|
|
497
|
+
}
|
|
498
|
+
if (!assertion.verifierAccepted) throw convexError(ErrorCode.PASSKEY_INVALID_CHALLENGE, "Invalid or expired passkey challenge.");
|
|
499
|
+
const authenticatorDataBytes = decodeBase64urlIgnorePadding(requireStringParam(params.authenticatorData, "authenticatorData"));
|
|
500
|
+
const authenticatorData = parseAuthenticatorData(authenticatorDataBytes);
|
|
501
|
+
const backupState = parseBackupState(authenticatorDataBytes);
|
|
502
|
+
const signatureBytes = decodeBase64urlIgnorePadding(requireStringParam(params.signature, "signature"));
|
|
503
|
+
const messageHash = sha256(createAssertionSignatureMessage(authenticatorDataBytes, clientDataJSON));
|
|
504
|
+
verifyRpId(authenticatorData, rp.rpId);
|
|
505
|
+
verifyUserFlags(authenticatorData, rp.authentication.userVerification);
|
|
506
|
+
const passkey = assertion.passkey;
|
|
507
|
+
verifyAssertionSignatureUniformly(passkey, signatureBytes, messageHash);
|
|
508
|
+
validateBackupEligibility(passkey.deviceType, backupState.deviceType);
|
|
509
|
+
if (rp.registration.attestation) await assertStoredAttestationTrusted(rp.registration.attestation, passkey.attestation);
|
|
510
|
+
if ((passkey.counter !== 0 || authenticatorData.signatureCounter !== 0) && authenticatorData.signatureCounter <= passkey.counter) throw convexError(ErrorCode.PASSKEY_COUNTER_ERROR, "Authenticator counter did not increase — possible credential cloning detected.");
|
|
511
|
+
const replaceSessionId = await getAuthSessionId(ctx) ?? void 0;
|
|
512
|
+
let completed;
|
|
513
|
+
try {
|
|
514
|
+
completed = await mutatePasskeyCompleteAssertion(ctx, {
|
|
515
|
+
id: passkey._id,
|
|
516
|
+
counter: authenticatorData.signatureCounter,
|
|
517
|
+
lastUsedAt: Date.now(),
|
|
518
|
+
backedUp: backupState.backedUp,
|
|
519
|
+
replaceSessionId,
|
|
520
|
+
sessionExpirationTime: sessionExpirationTime(ctx.auth.config),
|
|
521
|
+
refreshTokenExpirationTime: refreshTokenExpirationTime(ctx.auth.config)
|
|
522
|
+
});
|
|
523
|
+
} catch (error) {
|
|
524
|
+
throw asConvexError(error, ErrorCode.INTERNAL_ERROR, "Failed to finalize passkey sign-in.");
|
|
525
|
+
}
|
|
526
|
+
if (completed.status === "rejected") throw convexError(ErrorCode.PASSKEY_COUNTER_ERROR, "Authenticator counter did not increase — possible credential cloning detected.");
|
|
527
|
+
const userId = completed.user._id;
|
|
528
|
+
const sessionId = completed.sessionId;
|
|
529
|
+
setActiveSpanAttributes({
|
|
530
|
+
"auth.signin.result": "success",
|
|
531
|
+
...buildKnownSignInIdentityAttributes(ctx.auth.config, {
|
|
532
|
+
userId,
|
|
533
|
+
sessionId
|
|
534
|
+
}, completed.user.email)
|
|
535
|
+
});
|
|
536
|
+
if (completed.replacedSessionId !== void 0) {
|
|
537
|
+
const replacedSessionId = completed.replacedSessionId;
|
|
538
|
+
await queueAuthEvent(ctx, ctx.auth.config, {
|
|
539
|
+
kind: "session.invalidated",
|
|
540
|
+
actor: { type: "system" },
|
|
541
|
+
subject: {
|
|
542
|
+
type: "session",
|
|
543
|
+
id: replacedSessionId
|
|
544
|
+
},
|
|
545
|
+
targets: [{
|
|
546
|
+
kind: "user",
|
|
547
|
+
id: userId
|
|
548
|
+
}, {
|
|
549
|
+
kind: "session",
|
|
550
|
+
id: replacedSessionId
|
|
551
|
+
}],
|
|
552
|
+
outcome: "success",
|
|
553
|
+
data: {
|
|
554
|
+
userId,
|
|
555
|
+
reason: "replaced"
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
await queueAuthEvent(ctx, ctx.auth.config, {
|
|
560
|
+
kind: "session.signed_in",
|
|
561
|
+
actor: {
|
|
562
|
+
type: "user",
|
|
563
|
+
id: userId
|
|
564
|
+
},
|
|
565
|
+
subject: {
|
|
566
|
+
type: "session",
|
|
567
|
+
id: sessionId
|
|
568
|
+
},
|
|
569
|
+
targets: [{
|
|
570
|
+
kind: "user",
|
|
571
|
+
id: userId
|
|
572
|
+
}, {
|
|
573
|
+
kind: "session",
|
|
574
|
+
id: sessionId
|
|
575
|
+
}],
|
|
576
|
+
outcome: "success",
|
|
577
|
+
data: { provider: "session" }
|
|
578
|
+
});
|
|
579
|
+
return {
|
|
580
|
+
kind: "signedIn",
|
|
581
|
+
session: await finalizeSessionIssuance(ctx.auth.config, {
|
|
582
|
+
userId,
|
|
583
|
+
sessionId,
|
|
584
|
+
identity: buildSessionIdentity(userId, sessionId, completed.user),
|
|
585
|
+
refreshToken: encodeRefreshToken(completed.refreshTokenId, sessionId)
|
|
586
|
+
})
|
|
587
|
+
};
|
|
588
|
+
};
|
|
589
|
+
const handler = {
|
|
590
|
+
register: async () => {
|
|
591
|
+
const userId = await requireAuthenticatedUserId(ctx);
|
|
592
|
+
const rp = resolveRpOptions(provider);
|
|
593
|
+
const challenge = new Uint8Array(32);
|
|
594
|
+
crypto.getRandomValues(challenge);
|
|
595
|
+
const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));
|
|
596
|
+
const sessionId = await getAuthSessionId(ctx) ?? void 0;
|
|
597
|
+
let registration;
|
|
598
|
+
try {
|
|
599
|
+
registration = await mutatePasskeyBeginRegistration(ctx, {
|
|
600
|
+
userId,
|
|
601
|
+
sessionId,
|
|
602
|
+
signature: challengeHash,
|
|
603
|
+
expirationTime: Date.now() + rp.challengeExpirationMs
|
|
604
|
+
});
|
|
605
|
+
} catch (err) {
|
|
606
|
+
logPasskeyError(err);
|
|
607
|
+
throw convexError(ErrorCode.INTERNAL_ERROR, "An unexpected error occurred.");
|
|
608
|
+
}
|
|
609
|
+
const user = registration.user;
|
|
610
|
+
const userName = params.userName ?? user?.email ?? "user";
|
|
611
|
+
const userDisplayName = params.userDisplayName ?? user?.name ?? userName;
|
|
612
|
+
const existing = registration.credentials;
|
|
613
|
+
if (existing.length >= MAX_WEBAUTHN_CREDENTIALS_PER_USER) throw convexError(ErrorCode.INVALID_PARAMETERS, `A user can register at most ${MAX_WEBAUTHN_CREDENTIALS_PER_USER} WebAuthn credentials.`);
|
|
614
|
+
const excludeCredentials = existing.map((credential) => ({
|
|
615
|
+
id: credential.id,
|
|
616
|
+
transports: credential.transports
|
|
617
|
+
}));
|
|
618
|
+
const userHandle = encodeBase64urlNoPadding(new TextEncoder().encode(userId));
|
|
619
|
+
return {
|
|
620
|
+
kind: "webauthnOptions",
|
|
621
|
+
options: {
|
|
622
|
+
rp: {
|
|
623
|
+
name: rp.rpName,
|
|
624
|
+
id: rp.rpId
|
|
625
|
+
},
|
|
626
|
+
user: {
|
|
627
|
+
id: userHandle,
|
|
628
|
+
name: userName,
|
|
629
|
+
displayName: userDisplayName
|
|
630
|
+
},
|
|
631
|
+
challenge: encodeBase64urlNoPadding(challenge),
|
|
632
|
+
pubKeyCredParams: rp.registration.algorithms.map((alg) => ({
|
|
633
|
+
type: "public-key",
|
|
634
|
+
alg
|
|
635
|
+
})),
|
|
636
|
+
timeout: rp.challengeExpirationMs,
|
|
637
|
+
...rp.registration.hints ? { hints: rp.registration.hints } : {},
|
|
638
|
+
attestation: rp.registration.attestation?.conveyance ?? "none",
|
|
639
|
+
authenticatorSelection: {
|
|
640
|
+
residentKey: rp.registration.residentKey,
|
|
641
|
+
requireResidentKey: rp.registration.residentKey === "required",
|
|
642
|
+
userVerification: rp.registration.userVerification,
|
|
643
|
+
...rp.registration.authenticatorAttachment ? { authenticatorAttachment: rp.registration.authenticatorAttachment } : {}
|
|
644
|
+
},
|
|
645
|
+
excludeCredentials
|
|
646
|
+
},
|
|
647
|
+
verifier: registration.verifierId
|
|
648
|
+
};
|
|
649
|
+
},
|
|
650
|
+
signIn: async () => {
|
|
651
|
+
const rp = resolveRpOptions(provider);
|
|
652
|
+
const challenge = new Uint8Array(32);
|
|
653
|
+
crypto.getRandomValues(challenge);
|
|
654
|
+
const challengeHash = encodeBase64urlNoPadding(new Uint8Array(sha256(challenge)));
|
|
655
|
+
let allowCredentials;
|
|
656
|
+
const email = params.email === void 0 ? void 0 : normalizeEmail(params.email);
|
|
657
|
+
const sessionId = await getAuthSessionId(ctx) ?? void 0;
|
|
658
|
+
let signIn;
|
|
659
|
+
try {
|
|
660
|
+
signIn = await mutatePasskeyBeginSignIn(ctx, {
|
|
661
|
+
sessionId,
|
|
662
|
+
signature: challengeHash,
|
|
663
|
+
expirationTime: Date.now() + rp.challengeExpirationMs,
|
|
664
|
+
...email === void 0 ? {} : { verifiedEmail: email }
|
|
665
|
+
});
|
|
666
|
+
} catch (err) {
|
|
667
|
+
logPasskeyError(err);
|
|
668
|
+
throw convexError(ErrorCode.INTERNAL_ERROR, "An unexpected error occurred.");
|
|
669
|
+
}
|
|
670
|
+
if (email !== void 0) allowCredentials = await deriveEmailAllowCredentials(email, rp.rpId, signIn.credentialIds);
|
|
671
|
+
const options = {
|
|
672
|
+
challenge: encodeBase64urlNoPadding(challenge),
|
|
673
|
+
timeout: rp.challengeExpirationMs,
|
|
674
|
+
rpId: rp.rpId,
|
|
675
|
+
userVerification: rp.authentication.userVerification,
|
|
676
|
+
...rp.authentication.hints ? { hints: rp.authentication.hints } : {}
|
|
677
|
+
};
|
|
678
|
+
if (allowCredentials) options.allowCredentials = allowCredentials;
|
|
679
|
+
return {
|
|
680
|
+
kind: "webauthnOptions",
|
|
681
|
+
options,
|
|
682
|
+
verifier: signIn.verifierId
|
|
683
|
+
};
|
|
684
|
+
},
|
|
685
|
+
verify: async () => {
|
|
686
|
+
const isRegistration = typeof params.attestationObject === "string" && params.attestationObject.length > 0;
|
|
687
|
+
const isAuthentication = typeof params.signature === "string" && params.signature.length > 0;
|
|
688
|
+
if (isRegistration && !isAuthentication) return await handleRegisterVerify();
|
|
689
|
+
if (isAuthentication && !isRegistration) return await handleAuthVerify();
|
|
690
|
+
throw convexError(ErrorCode.PASSKEY_INVALID_VERIFY, "`verify` flow requires either `attestationObject` (to complete a `register`) or `signature` + `credentialId` (to complete a `signIn`).");
|
|
691
|
+
}
|
|
692
|
+
}[dispatch.flow];
|
|
693
|
+
if (!handler) throw convexError(ErrorCode.PASSKEY_MISSING_FLOW, `Unknown passkey flow: ${dispatch.flow}`);
|
|
694
|
+
return handler();
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
//#endregion
|
|
698
|
+
export { handleWebAuthn };
|
|
699
|
+
//# sourceMappingURL=webauthn.js.map
|