@robelest/convex-auth 0.0.4-preview.25 → 0.0.4-preview.28
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 +43 -36
- package/dist/bin.js +5765 -4880
- package/dist/browser/index.d.ts +30 -0
- package/dist/browser/index.js +93 -0
- package/dist/browser/locks.js +11 -0
- package/dist/browser/navigation.js +14 -0
- package/dist/{factors → browser}/passkey.js +23 -32
- package/dist/browser/runtime.js +92 -0
- package/dist/client/core/types.d.ts +452 -5
- package/dist/client/core/types.js +17 -0
- package/dist/client/errors.js +19 -0
- package/dist/client/factors/device.js +94 -0
- package/dist/{factors → client/factors}/totp.js +12 -4
- package/dist/client/index.d.ts +47 -1
- package/dist/client/index.js +269 -232
- package/dist/client/runtime/mutex.js +24 -0
- package/dist/client/runtime/proxy.js +30 -0
- package/dist/client/runtime/storage.js +45 -0
- package/dist/client/services/adapters.js +7 -0
- package/dist/client/services/http.js +6 -0
- package/dist/client/services/resolve.js +13 -0
- package/dist/client/services/runtime.js +6 -0
- package/dist/component/_generated/component.d.ts +1355 -1399
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/index.d.ts +4 -26
- package/dist/component/index.js +1 -1
- package/dist/component/model.d.ts +26 -112
- package/dist/component/model.js +76 -54
- package/dist/component/modules.js +38 -0
- package/dist/component/public/factors/devices.js +1 -1
- package/dist/component/public/factors/passkeys.js +1 -1
- package/dist/component/public/factors/totp.js +1 -1
- package/dist/component/public/groups/core.js +2 -2
- package/dist/component/public/groups/invites.js +1 -1
- package/dist/component/public/groups/members.js +1 -1
- package/dist/component/public/identity/accounts.js +1 -1
- package/dist/component/public/identity/codes.js +1 -1
- package/dist/component/public/identity/sessions.js +39 -2
- package/dist/component/public/identity/tokens.js +82 -4
- package/dist/component/public/identity/users.js +1 -1
- package/dist/component/public/identity/verifiers.js +10 -4
- package/dist/component/public/security/keys.js +1 -1
- package/dist/component/public/security/limits.js +1 -1
- package/dist/component/public/{enterprise → sso}/audit.js +26 -26
- package/dist/component/public/sso/core.js +263 -0
- package/dist/component/public/sso/domains.js +280 -0
- package/dist/component/public/{enterprise → sso}/scim.js +87 -87
- package/dist/component/public/sso/secrets.js +125 -0
- package/dist/component/public/{enterprise → sso}/webhooks.js +59 -59
- package/dist/component/public.js +9 -9
- package/dist/component/schema.d.ts +472 -393
- package/dist/component/schema.js +36 -35
- package/dist/core/index.d.ts +380 -0
- package/dist/core/index.js +83 -0
- package/dist/otel.d.ts +69 -0
- package/dist/otel.js +82 -0
- package/dist/providers/anonymous.d.ts +15 -34
- package/dist/providers/anonymous.js +27 -35
- package/dist/providers/apple.d.ts +59 -0
- package/dist/providers/apple.js +58 -0
- package/dist/providers/credentials.d.ts +18 -34
- package/dist/providers/credentials.js +16 -27
- package/dist/providers/custom.d.ts +94 -0
- package/dist/providers/custom.js +119 -0
- package/dist/providers/device.d.ts +15 -49
- package/dist/providers/device.js +17 -34
- package/dist/providers/email.d.ts +21 -38
- package/dist/providers/email.js +36 -55
- package/dist/providers/github.d.ts +54 -0
- package/dist/providers/github.js +75 -0
- package/dist/providers/google.d.ts +54 -0
- package/dist/providers/google.js +61 -0
- package/dist/providers/index.d.ts +16 -12
- package/dist/providers/index.js +15 -11
- package/dist/providers/microsoft.d.ts +57 -0
- package/dist/providers/microsoft.js +101 -0
- package/dist/providers/passkey.d.ts +19 -35
- package/dist/providers/passkey.js +20 -30
- package/dist/providers/password.d.ts +17 -18
- package/dist/providers/password.js +121 -143
- package/dist/providers/phone.d.ts +13 -28
- package/dist/providers/phone.js +21 -46
- package/dist/providers/sso.d.ts +16 -36
- package/dist/providers/sso.js +21 -22
- package/dist/providers/totp.d.ts +13 -29
- package/dist/providers/totp.js +17 -27
- package/dist/server/auth-context.d.ts +204 -0
- package/dist/server/auth-context.js +76 -0
- package/dist/server/auth.d.ts +99 -244
- package/dist/server/auth.js +56 -152
- package/dist/server/componentContext.d.ts +12 -0
- package/dist/server/componentContext.js +1 -0
- package/dist/server/config.js +6 -67
- package/dist/server/constants.js +6 -0
- package/dist/server/contract.d.ts +105 -0
- package/dist/server/contract.js +43 -0
- package/dist/server/cookies.js +3 -2
- package/dist/server/core.js +31 -36
- package/dist/server/crypto.js +34 -44
- package/dist/server/db.js +6 -1
- package/dist/server/device.js +96 -130
- package/dist/server/env.js +48 -0
- package/dist/server/errors.js +20 -0
- package/dist/server/http.d.ts +15 -59
- package/dist/server/http.js +136 -120
- package/dist/server/identity.js +2 -2
- package/dist/server/index.d.ts +5 -4
- package/dist/server/index.js +3 -3
- package/dist/server/keys.js +10 -1
- package/dist/server/limits.js +26 -26
- package/dist/server/log.js +28 -0
- package/dist/server/mounts.d.ts +1107 -296
- package/dist/server/mounts.js +315 -196
- package/dist/server/mutations/account.js +11 -14
- package/dist/server/mutations/code.js +6 -5
- package/dist/server/mutations/invalidate.js +9 -11
- package/dist/server/mutations/oauth.js +112 -73
- package/dist/server/mutations/refresh.js +47 -97
- package/dist/server/mutations/register.js +37 -35
- package/dist/server/mutations/retrieve.js +16 -16
- package/dist/server/mutations/signature.js +15 -18
- package/dist/server/mutations/signin.js +10 -5
- package/dist/server/mutations/signout.js +11 -14
- package/dist/server/mutations/store.js +25 -18
- package/dist/server/mutations/verifier.js +11 -8
- package/dist/server/mutations/verify.js +53 -41
- package/dist/server/oauth/factory.js +44 -0
- package/dist/server/oauth/index.js +12 -0
- package/dist/server/oauth/runtime.js +248 -0
- package/dist/server/passkey.js +331 -365
- package/dist/server/payloads.d.ts +16 -0
- package/dist/server/payloads.js +30 -0
- package/dist/server/{ssr.d.ts → prefetch.d.ts} +2 -2
- package/dist/server/prefetch.js +635 -0
- package/dist/server/random.js +19 -0
- package/dist/server/redirects.js +10 -5
- package/dist/server/refresh.js +14 -86
- package/dist/server/runtime.d.ts +531 -31
- package/dist/server/runtime.js +106 -267
- package/dist/server/secret.js +44 -0
- package/dist/server/services/config.js +10 -0
- package/dist/server/services/group.js +211 -0
- package/dist/server/services/logger.js +8 -0
- package/dist/server/services/providers.js +22 -0
- package/dist/server/services/refresh.js +8 -0
- package/dist/server/services/resolve.js +27 -0
- package/dist/server/services/signin.js +8 -0
- package/dist/server/sessions.js +35 -34
- package/dist/server/signin.js +229 -140
- package/dist/server/{enterprise → sso}/config.js +10 -3
- package/dist/server/sso/domain.d.ts +614 -0
- package/dist/server/sso/domain.js +1175 -0
- package/dist/server/sso/http.js +1060 -0
- package/dist/server/sso/oidc.js +324 -0
- package/dist/server/sso/policies.js +59 -0
- package/dist/server/sso/policy.js +139 -0
- package/dist/server/sso/profile.js +22 -0
- package/dist/server/sso/provision.js +179 -0
- package/dist/{component/server/enterprise → server/sso}/saml.js +142 -56
- package/dist/{component/server/enterprise → server/sso}/scim.js +13 -7
- package/dist/server/sso/shared.js +74 -0
- package/dist/server/sso/validators.js +88 -0
- package/dist/server/sso/webhook.js +94 -0
- package/dist/server/tokens.js +16 -4
- package/dist/server/totp.js +155 -164
- package/dist/server/types.d.ts +306 -296
- package/dist/server/types.js +1 -30
- package/dist/server/url.js +32 -0
- package/dist/server/users.js +74 -40
- package/dist/server/utils/cache.js +51 -0
- package/dist/server/utils/dispatch.js +36 -0
- package/dist/server/utils/retry.js +24 -0
- package/dist/server/utils/span.js +32 -0
- package/dist/shared/errors.js +19 -0
- package/dist/shared/log.js +45 -0
- package/{src/test.ts → dist/test.d.ts} +21 -22
- package/dist/test.js +51 -0
- package/package.json +70 -42
- package/dist/authorization/index.d.ts.map +0 -1
- package/dist/authorization/index.js.map +0 -1
- package/dist/client/core/types.d.ts.map +0 -1
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/index.js.map +0 -1
- package/dist/component/_generated/api.d.ts +0 -75
- package/dist/component/_generated/api.d.ts.map +0 -1
- package/dist/component/_generated/api.js.map +0 -1
- package/dist/component/_generated/component.d.ts.map +0 -1
- package/dist/component/_generated/dataModel.d.ts +0 -42
- package/dist/component/_generated/dataModel.d.ts.map +0 -1
- package/dist/component/_generated/server.d.ts +0 -117
- package/dist/component/_generated/server.d.ts.map +0 -1
- package/dist/component/_generated/server.js.map +0 -1
- package/dist/component/_virtual/rolldown_runtime.js +0 -18
- package/dist/component/client/core/types.d.ts +0 -2
- package/dist/component/client/index.d.ts +0 -1
- package/dist/component/convex.config.d.ts.map +0 -1
- package/dist/component/convex.config.js.map +0 -1
- package/dist/component/functions.d.ts +0 -25
- package/dist/component/functions.d.ts.map +0 -1
- package/dist/component/functions.js.map +0 -1
- package/dist/component/index.d.ts.map +0 -1
- package/dist/component/model.d.ts.map +0 -1
- package/dist/component/model.js.map +0 -1
- package/dist/component/providers/anonymous.d.ts +0 -54
- package/dist/component/providers/anonymous.d.ts.map +0 -1
- package/dist/component/providers/credentials.d.ts +0 -38
- package/dist/component/providers/credentials.d.ts.map +0 -1
- package/dist/component/providers/device.d.ts +0 -67
- package/dist/component/providers/device.d.ts.map +0 -1
- package/dist/component/providers/email.d.ts +0 -62
- package/dist/component/providers/email.d.ts.map +0 -1
- package/dist/component/providers/oauth.d.ts +0 -25
- package/dist/component/providers/oauth.d.ts.map +0 -1
- package/dist/component/providers/oauth.js +0 -13
- package/dist/component/providers/oauth.js.map +0 -1
- package/dist/component/providers/passkey.d.ts +0 -57
- package/dist/component/providers/passkey.d.ts.map +0 -1
- package/dist/component/providers/password.d.ts +0 -88
- package/dist/component/providers/password.d.ts.map +0 -1
- package/dist/component/providers/phone.d.ts +0 -48
- package/dist/component/providers/phone.d.ts.map +0 -1
- package/dist/component/providers/sso.d.ts +0 -50
- package/dist/component/providers/sso.d.ts.map +0 -1
- package/dist/component/providers/totp.d.ts +0 -45
- package/dist/component/providers/totp.d.ts.map +0 -1
- package/dist/component/public/enterprise/audit.d.ts +0 -73
- package/dist/component/public/enterprise/audit.d.ts.map +0 -1
- package/dist/component/public/enterprise/audit.js.map +0 -1
- package/dist/component/public/enterprise/core.d.ts +0 -176
- package/dist/component/public/enterprise/core.d.ts.map +0 -1
- package/dist/component/public/enterprise/core.js +0 -292
- package/dist/component/public/enterprise/core.js.map +0 -1
- package/dist/component/public/enterprise/domains.d.ts +0 -174
- package/dist/component/public/enterprise/domains.d.ts.map +0 -1
- package/dist/component/public/enterprise/domains.js +0 -271
- package/dist/component/public/enterprise/domains.js.map +0 -1
- package/dist/component/public/enterprise/scim.d.ts +0 -245
- package/dist/component/public/enterprise/scim.d.ts.map +0 -1
- package/dist/component/public/enterprise/scim.js.map +0 -1
- package/dist/component/public/enterprise/secrets.d.ts +0 -78
- package/dist/component/public/enterprise/secrets.d.ts.map +0 -1
- package/dist/component/public/enterprise/secrets.js +0 -118
- package/dist/component/public/enterprise/secrets.js.map +0 -1
- package/dist/component/public/enterprise/webhooks.d.ts +0 -211
- package/dist/component/public/enterprise/webhooks.d.ts.map +0 -1
- package/dist/component/public/enterprise/webhooks.js.map +0 -1
- package/dist/component/public/factors/devices.d.ts +0 -157
- package/dist/component/public/factors/devices.d.ts.map +0 -1
- package/dist/component/public/factors/devices.js.map +0 -1
- package/dist/component/public/factors/passkeys.d.ts +0 -175
- package/dist/component/public/factors/passkeys.d.ts.map +0 -1
- package/dist/component/public/factors/passkeys.js.map +0 -1
- package/dist/component/public/factors/totp.d.ts +0 -189
- package/dist/component/public/factors/totp.d.ts.map +0 -1
- package/dist/component/public/factors/totp.js.map +0 -1
- package/dist/component/public/groups/core.d.ts +0 -137
- package/dist/component/public/groups/core.d.ts.map +0 -1
- package/dist/component/public/groups/core.js.map +0 -1
- package/dist/component/public/groups/invites.d.ts +0 -217
- package/dist/component/public/groups/invites.d.ts.map +0 -1
- package/dist/component/public/groups/invites.js.map +0 -1
- package/dist/component/public/groups/members.d.ts +0 -204
- package/dist/component/public/groups/members.d.ts.map +0 -1
- package/dist/component/public/groups/members.js.map +0 -1
- package/dist/component/public/identity/accounts.d.ts +0 -147
- package/dist/component/public/identity/accounts.d.ts.map +0 -1
- package/dist/component/public/identity/accounts.js.map +0 -1
- package/dist/component/public/identity/codes.d.ts +0 -104
- package/dist/component/public/identity/codes.d.ts.map +0 -1
- package/dist/component/public/identity/codes.js.map +0 -1
- package/dist/component/public/identity/sessions.d.ts +0 -128
- package/dist/component/public/identity/sessions.d.ts.map +0 -1
- package/dist/component/public/identity/sessions.js.map +0 -1
- package/dist/component/public/identity/tokens.d.ts +0 -169
- package/dist/component/public/identity/tokens.d.ts.map +0 -1
- package/dist/component/public/identity/tokens.js.map +0 -1
- package/dist/component/public/identity/users.d.ts +0 -212
- package/dist/component/public/identity/users.d.ts.map +0 -1
- package/dist/component/public/identity/users.js.map +0 -1
- package/dist/component/public/identity/verifiers.d.ts +0 -116
- package/dist/component/public/identity/verifiers.d.ts.map +0 -1
- package/dist/component/public/identity/verifiers.js.map +0 -1
- package/dist/component/public/security/keys.d.ts +0 -209
- package/dist/component/public/security/keys.d.ts.map +0 -1
- package/dist/component/public/security/keys.js.map +0 -1
- package/dist/component/public/security/limits.d.ts +0 -114
- package/dist/component/public/security/limits.d.ts.map +0 -1
- package/dist/component/public/security/limits.js.map +0 -1
- package/dist/component/public.d.ts +0 -28
- package/dist/component/public.d.ts.map +0 -1
- package/dist/component/schema.d.ts.map +0 -1
- package/dist/component/schema.js.map +0 -1
- package/dist/component/server/auth.d.ts +0 -447
- package/dist/component/server/auth.d.ts.map +0 -1
- package/dist/component/server/auth.js +0 -254
- package/dist/component/server/auth.js.map +0 -1
- package/dist/component/server/config.js +0 -121
- package/dist/component/server/config.js.map +0 -1
- package/dist/component/server/context.js +0 -53
- package/dist/component/server/context.js.map +0 -1
- package/dist/component/server/cookies.js +0 -47
- package/dist/component/server/cookies.js.map +0 -1
- package/dist/component/server/core.js +0 -576
- package/dist/component/server/core.js.map +0 -1
- package/dist/component/server/crypto.js +0 -56
- package/dist/component/server/crypto.js.map +0 -1
- package/dist/component/server/db.js +0 -87
- package/dist/component/server/db.js.map +0 -1
- package/dist/component/server/device.js +0 -152
- package/dist/component/server/device.js.map +0 -1
- package/dist/component/server/enterprise/config.js +0 -46
- package/dist/component/server/enterprise/config.js.map +0 -1
- package/dist/component/server/enterprise/domain.js +0 -974
- package/dist/component/server/enterprise/domain.js.map +0 -1
- package/dist/component/server/enterprise/http.js +0 -787
- package/dist/component/server/enterprise/http.js.map +0 -1
- package/dist/component/server/enterprise/oidc.js +0 -248
- package/dist/component/server/enterprise/oidc.js.map +0 -1
- package/dist/component/server/enterprise/policy.js +0 -85
- package/dist/component/server/enterprise/policy.js.map +0 -1
- package/dist/component/server/enterprise/saml.js.map +0 -1
- package/dist/component/server/enterprise/scim.js.map +0 -1
- package/dist/component/server/enterprise/shared.js +0 -51
- package/dist/component/server/enterprise/shared.js.map +0 -1
- package/dist/component/server/http.d.ts +0 -85
- package/dist/component/server/http.d.ts.map +0 -1
- package/dist/component/server/http.js +0 -351
- package/dist/component/server/http.js.map +0 -1
- package/dist/component/server/identity.js +0 -16
- package/dist/component/server/identity.js.map +0 -1
- package/dist/component/server/keys.js +0 -96
- package/dist/component/server/keys.js.map +0 -1
- package/dist/component/server/limits.js +0 -52
- package/dist/component/server/limits.js.map +0 -1
- package/dist/component/server/mutations/account.js +0 -46
- package/dist/component/server/mutations/account.js.map +0 -1
- package/dist/component/server/mutations/code.js +0 -68
- package/dist/component/server/mutations/code.js.map +0 -1
- package/dist/component/server/mutations/invalidate.js +0 -32
- package/dist/component/server/mutations/invalidate.js.map +0 -1
- package/dist/component/server/mutations/oauth.js +0 -116
- package/dist/component/server/mutations/oauth.js.map +0 -1
- package/dist/component/server/mutations/refresh.js +0 -119
- package/dist/component/server/mutations/refresh.js.map +0 -1
- package/dist/component/server/mutations/register.js +0 -87
- package/dist/component/server/mutations/register.js.map +0 -1
- package/dist/component/server/mutations/retrieve.js +0 -61
- package/dist/component/server/mutations/retrieve.js.map +0 -1
- package/dist/component/server/mutations/signature.js +0 -38
- package/dist/component/server/mutations/signature.js.map +0 -1
- package/dist/component/server/mutations/signin.js +0 -27
- package/dist/component/server/mutations/signin.js.map +0 -1
- package/dist/component/server/mutations/signout.js +0 -27
- package/dist/component/server/mutations/signout.js.map +0 -1
- package/dist/component/server/mutations/store/refs.js +0 -15
- package/dist/component/server/mutations/store/refs.js.map +0 -1
- package/dist/component/server/mutations/store.js +0 -70
- package/dist/component/server/mutations/store.js.map +0 -1
- package/dist/component/server/mutations/verifier.js +0 -18
- package/dist/component/server/mutations/verifier.js.map +0 -1
- package/dist/component/server/mutations/verify.js +0 -98
- package/dist/component/server/mutations/verify.js.map +0 -1
- package/dist/component/server/oauth.js +0 -242
- package/dist/component/server/oauth.js.map +0 -1
- package/dist/component/server/passkey.js +0 -415
- package/dist/component/server/passkey.js.map +0 -1
- package/dist/component/server/redirects.js +0 -40
- package/dist/component/server/redirects.js.map +0 -1
- package/dist/component/server/refresh.js +0 -99
- package/dist/component/server/refresh.js.map +0 -1
- package/dist/component/server/runtime.d.ts +0 -136
- package/dist/component/server/runtime.d.ts.map +0 -1
- package/dist/component/server/runtime.js +0 -456
- package/dist/component/server/runtime.js.map +0 -1
- package/dist/component/server/sessions.js +0 -71
- package/dist/component/server/sessions.js.map +0 -1
- package/dist/component/server/signin.js +0 -225
- package/dist/component/server/signin.js.map +0 -1
- package/dist/component/server/tokens.js +0 -17
- package/dist/component/server/tokens.js.map +0 -1
- package/dist/component/server/totp.js +0 -208
- package/dist/component/server/totp.js.map +0 -1
- package/dist/component/server/types.d.ts +0 -949
- package/dist/component/server/types.d.ts.map +0 -1
- package/dist/component/server/types.js +0 -79
- package/dist/component/server/types.js.map +0 -1
- package/dist/component/server/users.js +0 -123
- package/dist/component/server/users.js.map +0 -1
- package/dist/component/server/utils.js +0 -140
- package/dist/component/server/utils.js.map +0 -1
- package/dist/core/types.d.ts +0 -361
- package/dist/core/types.d.ts.map +0 -1
- package/dist/factors/device.js +0 -104
- package/dist/factors/device.js.map +0 -1
- package/dist/factors/passkey.js.map +0 -1
- package/dist/factors/totp.js.map +0 -1
- package/dist/providers/anonymous.d.ts.map +0 -1
- package/dist/providers/anonymous.js.map +0 -1
- package/dist/providers/credentials.d.ts.map +0 -1
- package/dist/providers/credentials.js.map +0 -1
- package/dist/providers/device.d.ts.map +0 -1
- package/dist/providers/device.js.map +0 -1
- package/dist/providers/email.d.ts.map +0 -1
- package/dist/providers/email.js.map +0 -1
- package/dist/providers/oauth.d.ts +0 -69
- package/dist/providers/oauth.d.ts.map +0 -1
- package/dist/providers/oauth.js +0 -43
- package/dist/providers/oauth.js.map +0 -1
- package/dist/providers/passkey.d.ts.map +0 -1
- package/dist/providers/passkey.js.map +0 -1
- package/dist/providers/password.d.ts.map +0 -1
- package/dist/providers/password.js.map +0 -1
- package/dist/providers/phone.d.ts.map +0 -1
- package/dist/providers/phone.js.map +0 -1
- package/dist/providers/sso.d.ts.map +0 -1
- package/dist/providers/sso.js.map +0 -1
- package/dist/providers/totp.d.ts.map +0 -1
- package/dist/providers/totp.js.map +0 -1
- package/dist/runtime/browser.js +0 -68
- package/dist/runtime/browser.js.map +0 -1
- package/dist/runtime/invite.js.map +0 -1
- package/dist/runtime/proxy.js +0 -70
- package/dist/runtime/proxy.js.map +0 -1
- package/dist/runtime/storage.js +0 -37
- package/dist/runtime/storage.js.map +0 -1
- package/dist/server/auth.d.ts.map +0 -1
- package/dist/server/auth.js.map +0 -1
- package/dist/server/config.d.ts +0 -1
- package/dist/server/config.js.map +0 -1
- package/dist/server/context.d.ts +0 -1
- package/dist/server/context.js.map +0 -1
- package/dist/server/cookies.d.ts +0 -1
- package/dist/server/cookies.js.map +0 -1
- package/dist/server/core.d.ts +0 -1315
- package/dist/server/core.d.ts.map +0 -1
- package/dist/server/core.js.map +0 -1
- package/dist/server/crypto.d.ts +0 -8
- package/dist/server/crypto.d.ts.map +0 -1
- package/dist/server/crypto.js.map +0 -1
- package/dist/server/db.d.ts +0 -1
- package/dist/server/db.js.map +0 -1
- package/dist/server/device.d.ts +0 -1
- package/dist/server/device.js.map +0 -1
- package/dist/server/enterprise/config.d.ts +0 -1
- package/dist/server/enterprise/config.js.map +0 -1
- package/dist/server/enterprise/domain.d.ts +0 -401
- package/dist/server/enterprise/domain.d.ts.map +0 -1
- package/dist/server/enterprise/domain.js +0 -974
- package/dist/server/enterprise/domain.js.map +0 -1
- package/dist/server/enterprise/http.d.ts +0 -26
- package/dist/server/enterprise/http.d.ts.map +0 -1
- package/dist/server/enterprise/http.js +0 -787
- package/dist/server/enterprise/http.js.map +0 -1
- package/dist/server/enterprise/oidc.d.ts +0 -1
- package/dist/server/enterprise/oidc.js +0 -248
- package/dist/server/enterprise/oidc.js.map +0 -1
- package/dist/server/enterprise/policy.d.ts +0 -1
- package/dist/server/enterprise/policy.js +0 -85
- package/dist/server/enterprise/policy.js.map +0 -1
- package/dist/server/enterprise/saml.d.ts +0 -1
- package/dist/server/enterprise/saml.js +0 -338
- package/dist/server/enterprise/saml.js.map +0 -1
- package/dist/server/enterprise/scim.d.ts +0 -1
- package/dist/server/enterprise/scim.js +0 -97
- package/dist/server/enterprise/scim.js.map +0 -1
- package/dist/server/enterprise/shared.d.ts +0 -5
- package/dist/server/enterprise/shared.d.ts.map +0 -1
- package/dist/server/enterprise/shared.js +0 -51
- package/dist/server/enterprise/shared.js.map +0 -1
- package/dist/server/enterprise/validators.d.ts +0 -1
- package/dist/server/enterprise/validators.js +0 -60
- package/dist/server/enterprise/validators.js.map +0 -1
- package/dist/server/http.d.ts.map +0 -1
- package/dist/server/http.js.map +0 -1
- package/dist/server/identity.d.ts +0 -1
- package/dist/server/identity.js.map +0 -1
- package/dist/server/keys.d.ts +0 -1
- package/dist/server/keys.js.map +0 -1
- package/dist/server/limits.d.ts +0 -1
- package/dist/server/limits.js.map +0 -1
- package/dist/server/mounts.d.ts.map +0 -1
- package/dist/server/mounts.js.map +0 -1
- package/dist/server/mutations/account.d.ts +0 -29
- package/dist/server/mutations/account.d.ts.map +0 -1
- package/dist/server/mutations/account.js.map +0 -1
- package/dist/server/mutations/code.d.ts +0 -30
- package/dist/server/mutations/code.d.ts.map +0 -1
- package/dist/server/mutations/code.js.map +0 -1
- package/dist/server/mutations/index.d.ts +0 -14
- package/dist/server/mutations/invalidate.d.ts +0 -20
- package/dist/server/mutations/invalidate.d.ts.map +0 -1
- package/dist/server/mutations/invalidate.js.map +0 -1
- package/dist/server/mutations/oauth.d.ts +0 -30
- package/dist/server/mutations/oauth.d.ts.map +0 -1
- package/dist/server/mutations/oauth.js.map +0 -1
- package/dist/server/mutations/refresh.d.ts +0 -21
- package/dist/server/mutations/refresh.d.ts.map +0 -1
- package/dist/server/mutations/refresh.js.map +0 -1
- package/dist/server/mutations/register.d.ts +0 -38
- package/dist/server/mutations/register.d.ts.map +0 -1
- package/dist/server/mutations/register.js.map +0 -1
- package/dist/server/mutations/retrieve.d.ts +0 -33
- package/dist/server/mutations/retrieve.d.ts.map +0 -1
- package/dist/server/mutations/retrieve.js.map +0 -1
- package/dist/server/mutations/signature.d.ts +0 -21
- package/dist/server/mutations/signature.d.ts.map +0 -1
- package/dist/server/mutations/signature.js.map +0 -1
- package/dist/server/mutations/signin.d.ts +0 -22
- package/dist/server/mutations/signin.d.ts.map +0 -1
- package/dist/server/mutations/signin.js.map +0 -1
- package/dist/server/mutations/signout.d.ts +0 -16
- package/dist/server/mutations/signout.d.ts.map +0 -1
- package/dist/server/mutations/signout.js.map +0 -1
- package/dist/server/mutations/store/refs.d.ts +0 -12
- package/dist/server/mutations/store/refs.d.ts.map +0 -1
- package/dist/server/mutations/store/refs.js.map +0 -1
- package/dist/server/mutations/store.d.ts +0 -306
- package/dist/server/mutations/store.d.ts.map +0 -1
- package/dist/server/mutations/store.js.map +0 -1
- package/dist/server/mutations/verifier.d.ts +0 -13
- package/dist/server/mutations/verifier.d.ts.map +0 -1
- package/dist/server/mutations/verifier.js.map +0 -1
- package/dist/server/mutations/verify.d.ts +0 -26
- package/dist/server/mutations/verify.d.ts.map +0 -1
- package/dist/server/mutations/verify.js.map +0 -1
- package/dist/server/oauth.d.ts +0 -1
- package/dist/server/oauth.js +0 -242
- package/dist/server/oauth.js.map +0 -1
- package/dist/server/passkey.d.ts +0 -27
- package/dist/server/passkey.d.ts.map +0 -1
- package/dist/server/passkey.js.map +0 -1
- package/dist/server/redirects.d.ts +0 -1
- package/dist/server/redirects.js.map +0 -1
- package/dist/server/refresh.d.ts +0 -1
- package/dist/server/refresh.js.map +0 -1
- package/dist/server/runtime.d.ts.map +0 -1
- package/dist/server/runtime.js.map +0 -1
- package/dist/server/sessions.d.ts +0 -1
- package/dist/server/sessions.js.map +0 -1
- package/dist/server/signin.d.ts +0 -1
- package/dist/server/signin.js.map +0 -1
- package/dist/server/ssr.d.ts.map +0 -1
- package/dist/server/ssr.js +0 -777
- package/dist/server/ssr.js.map +0 -1
- package/dist/server/templates.d.ts +0 -1
- package/dist/server/templates.js.map +0 -1
- package/dist/server/tokens.d.ts +0 -1
- package/dist/server/tokens.js.map +0 -1
- package/dist/server/totp.d.ts +0 -1
- package/dist/server/totp.js.map +0 -1
- package/dist/server/types.d.ts.map +0 -1
- package/dist/server/types.js.map +0 -1
- package/dist/server/users.d.ts +0 -1
- package/dist/server/users.js.map +0 -1
- package/dist/server/utils.d.ts +0 -1
- package/dist/server/utils.js +0 -140
- package/dist/server/utils.js.map +0 -1
- package/src/authorization/index.ts +0 -83
- package/src/cli/bin.ts +0 -5
- package/src/cli/command.ts +0 -70
- package/src/cli/index.ts +0 -1112
- package/src/cli/keys.ts +0 -23
- package/src/client/core/types.ts +0 -437
- package/src/client/factors/device.ts +0 -158
- package/src/client/factors/passkey.ts +0 -279
- package/src/client/factors/totp.ts +0 -150
- package/src/client/index.ts +0 -1124
- package/src/client/runtime/browser.ts +0 -112
- package/src/client/runtime/invite.ts +0 -63
- package/src/client/runtime/proxy.ts +0 -111
- package/src/client/runtime/storage.ts +0 -79
- package/src/component/_generated/api.ts +0 -96
- package/src/component/_generated/component.ts +0 -3774
- package/src/component/_generated/dataModel.ts +0 -60
- package/src/component/_generated/server.ts +0 -156
- package/src/component/convex.config.ts +0 -5
- package/src/component/functions.ts +0 -104
- package/src/component/index.ts +0 -42
- package/src/component/model.ts +0 -449
- package/src/component/public/enterprise/audit.ts +0 -125
- package/src/component/public/enterprise/core.ts +0 -355
- package/src/component/public/enterprise/domains.ts +0 -327
- package/src/component/public/enterprise/scim.ts +0 -397
- package/src/component/public/enterprise/secrets.ts +0 -133
- package/src/component/public/enterprise/webhooks.ts +0 -307
- package/src/component/public/factors/devices.ts +0 -224
- package/src/component/public/factors/passkeys.ts +0 -243
- package/src/component/public/factors/totp.ts +0 -259
- package/src/component/public/groups/core.ts +0 -481
- package/src/component/public/groups/invites.ts +0 -608
- package/src/component/public/groups/members.ts +0 -410
- package/src/component/public/identity/accounts.ts +0 -207
- package/src/component/public/identity/codes.ts +0 -149
- package/src/component/public/identity/sessions.ts +0 -210
- package/src/component/public/identity/tokens.ts +0 -251
- package/src/component/public/identity/users.ts +0 -355
- package/src/component/public/identity/verifiers.ts +0 -158
- package/src/component/public/security/keys.ts +0 -366
- package/src/component/public/security/limits.ts +0 -174
- package/src/component/public.ts +0 -27
- package/src/component/schema.ts +0 -505
- package/src/providers/anonymous.ts +0 -99
- package/src/providers/credentials.ts +0 -102
- package/src/providers/device.ts +0 -87
- package/src/providers/email.ts +0 -99
- package/src/providers/index.ts +0 -31
- package/src/providers/oauth.ts +0 -117
- package/src/providers/passkey.ts +0 -77
- package/src/providers/password.ts +0 -441
- package/src/providers/phone.ts +0 -93
- package/src/providers/sso.ts +0 -54
- package/src/providers/totp.ts +0 -62
- package/src/samlify.d.ts +0 -53
- package/src/server/auth.ts +0 -949
- package/src/server/config.ts +0 -200
- package/src/server/context.ts +0 -90
- package/src/server/cookies.ts +0 -49
- package/src/server/core.ts +0 -2004
- package/src/server/crypto.ts +0 -90
- package/src/server/db.ts +0 -203
- package/src/server/device.ts +0 -254
- package/src/server/enterprise/config.ts +0 -51
- package/src/server/enterprise/domain.ts +0 -1739
- package/src/server/enterprise/http.ts +0 -1331
- package/src/server/enterprise/oidc.ts +0 -500
- package/src/server/enterprise/policy.ts +0 -128
- package/src/server/enterprise/saml.ts +0 -578
- package/src/server/enterprise/scim.ts +0 -135
- package/src/server/enterprise/shared.ts +0 -134
- package/src/server/enterprise/validators.ts +0 -93
- package/src/server/http.ts +0 -790
- package/src/server/identity.ts +0 -18
- package/src/server/index.ts +0 -40
- package/src/server/keys.ts +0 -158
- package/src/server/limits.ts +0 -107
- package/src/server/mounts.ts +0 -924
- package/src/server/mutations/account.ts +0 -62
- package/src/server/mutations/code.ts +0 -119
- package/src/server/mutations/index.ts +0 -13
- package/src/server/mutations/invalidate.ts +0 -50
- package/src/server/mutations/oauth.ts +0 -243
- package/src/server/mutations/refresh.ts +0 -299
- package/src/server/mutations/register.ts +0 -155
- package/src/server/mutations/retrieve.ts +0 -109
- package/src/server/mutations/signature.ts +0 -57
- package/src/server/mutations/signin.ts +0 -54
- package/src/server/mutations/signout.ts +0 -43
- package/src/server/mutations/store/refs.ts +0 -10
- package/src/server/mutations/store.ts +0 -123
- package/src/server/mutations/verifier.ts +0 -34
- package/src/server/mutations/verify.ts +0 -200
- package/src/server/oauth.ts +0 -418
- package/src/server/passkey.ts +0 -838
- package/src/server/redirects.ts +0 -59
- package/src/server/refresh.ts +0 -218
- package/src/server/runtime.ts +0 -918
- package/src/server/sessions.ts +0 -132
- package/src/server/signin.ts +0 -445
- package/src/server/ssr.ts +0 -1747
- package/src/server/templates.ts +0 -82
- package/src/server/tokens.ts +0 -35
- package/src/server/totp.ts +0 -399
- package/src/server/types.ts +0 -1942
- package/src/server/users.ts +0 -291
- package/src/server/utils.ts +0 -220
- /package/dist/{runtime → client/runtime}/invite.js +0 -0
|
@@ -0,0 +1,1175 @@
|
|
|
1
|
+
import { log } from "../log.js";
|
|
2
|
+
import { addConnectionDomain, createGroupConnection, deleteConnectionDomain, deleteConnectionDomainVerification, deleteGroupConnection, getConnectionDomainVerification, getGroupConnection, getGroupConnectionByDomain, getScimConfigByConnection, listAuditEvents, listConnectionDomains, listGroupConnections, updateGroupConnection, upsertConnectionDomainVerification, upsertGroupConnectionSecret, verifyConnectionDomain } from "../contract.js";
|
|
3
|
+
import { retryWithBackoff } from "../utils/retry.js";
|
|
4
|
+
import { getGroupOidcUrls, getGroupSamlUrls, groupOidcProviderId, groupSamlProviderId, normalizeDomain } from "./shared.js";
|
|
5
|
+
import { getOidcConfig, getPublicOidcConfig, getSamlConfig, upsertProtocolConfig, withOidcSecretState } from "./config.js";
|
|
6
|
+
import { createGroupPolicyDomain } from "./policies.js";
|
|
7
|
+
import { createGroupScimDomain } from "./provision.js";
|
|
8
|
+
import { createServiceProviderMetadata, getSamlServiceProviderOptions, parseSamlIdpMetadataChecked } from "./saml.js";
|
|
9
|
+
import { createGroupWebhookDomain } from "./webhook.js";
|
|
10
|
+
import { ConvexError } from "convex/values";
|
|
11
|
+
|
|
12
|
+
//#region src/server/sso/domain.ts
|
|
13
|
+
const convexError = (data) => new ConvexError(data);
|
|
14
|
+
/**
|
|
15
|
+
* Build the connection and SSO management domain.
|
|
16
|
+
*/
|
|
17
|
+
function createGroupConnectionDomain(deps) {
|
|
18
|
+
const { config, getGroupConnectionSecret, loadConnectionOrThrow, validateGroupConnectionPolicy, recordGroupAuditEvent, emitGroupWebhookDeliveries, connectionNotFoundError, GROUP_CONNECTION_OIDC_CLIENT_SECRET_KIND, requireEnv, generateRandomString, INVITE_TOKEN_ALPHABET, sha256, encryptSecret, loadGroupPolicyOrThrow } = deps;
|
|
19
|
+
const webhook = createGroupWebhookDomain({
|
|
20
|
+
config,
|
|
21
|
+
sha256,
|
|
22
|
+
loadConnectionOrThrow,
|
|
23
|
+
recordGroupAuditEvent,
|
|
24
|
+
emitGroupWebhookDeliveries
|
|
25
|
+
});
|
|
26
|
+
const scim = createGroupScimDomain({
|
|
27
|
+
config,
|
|
28
|
+
requireEnv,
|
|
29
|
+
generateRandomString,
|
|
30
|
+
INVITE_TOKEN_ALPHABET,
|
|
31
|
+
sha256,
|
|
32
|
+
loadGroupPolicyOrThrow,
|
|
33
|
+
recordGroupAuditEvent,
|
|
34
|
+
emitGroupWebhookDeliveries
|
|
35
|
+
});
|
|
36
|
+
const policy = createGroupPolicyDomain({
|
|
37
|
+
config,
|
|
38
|
+
loadGroupPolicyOrThrow,
|
|
39
|
+
validateGroupConnectionPolicy,
|
|
40
|
+
recordGroupAuditEvent
|
|
41
|
+
});
|
|
42
|
+
const resolveGroupConnectionProtocol = (connection) => {
|
|
43
|
+
if (connection.protocol === "oidc") return "oidc";
|
|
44
|
+
if (connection.protocol === "saml") return "saml";
|
|
45
|
+
throw convexError({
|
|
46
|
+
code: "PROVIDER_NOT_CONFIGURED",
|
|
47
|
+
message: "Group connection protocol is not configured."
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
const GROUP_CONNECTION_DOMAIN_VERIFICATION_PREFIX = "_convex-auth-verification";
|
|
51
|
+
const GROUP_CONNECTION_DOMAIN_VERIFICATION_TTL_MS = 1e3 * 60 * 60 * 24 * 7;
|
|
52
|
+
const toDomainSummary = (domain) => ({
|
|
53
|
+
domainId: domain._id,
|
|
54
|
+
domain: domain.domain,
|
|
55
|
+
isPrimary: domain.isPrimary,
|
|
56
|
+
verified: domain.verifiedAt !== void 0,
|
|
57
|
+
verifiedAt: domain.verifiedAt ?? null
|
|
58
|
+
});
|
|
59
|
+
const getDomainVerificationRecordName = (domain) => `${GROUP_CONNECTION_DOMAIN_VERIFICATION_PREFIX}.${normalizeDomain(domain)}`;
|
|
60
|
+
const parseTxtAnswer = (value) => {
|
|
61
|
+
const quoted = [...value.matchAll(/"([^"]*)"/g)].map((match) => match[1]);
|
|
62
|
+
if (quoted.length > 0) return quoted.join("");
|
|
63
|
+
return value.replace(/^"|"$/g, "").trim();
|
|
64
|
+
};
|
|
65
|
+
const resolveTxtValues = async (recordName) => {
|
|
66
|
+
const url = new URL("https://dns.google/resolve");
|
|
67
|
+
url.searchParams.set("name", recordName);
|
|
68
|
+
url.searchParams.set("type", "TXT");
|
|
69
|
+
const response = await fetch(url, { headers: { accept: "application/json" } });
|
|
70
|
+
if (!response.ok) throw new Error(`DNS TXT lookup failed with status ${response.status}.`);
|
|
71
|
+
return ((await response.json()).Answer ?? []).map((answer) => typeof answer.data === "string" ? parseTxtAnswer(answer.data) : null).filter((value) => value !== null && value.length > 0);
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
connection: {
|
|
75
|
+
create: async (ctx, data) => {
|
|
76
|
+
return {
|
|
77
|
+
connectionId: await createGroupConnection(ctx, config.component.public, data),
|
|
78
|
+
groupId: data.groupId
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
get: async (ctx, connectionId) => {
|
|
82
|
+
return await getGroupConnection(ctx, config.component.public, connectionId);
|
|
83
|
+
},
|
|
84
|
+
getByDomain: async (ctx, domain) => {
|
|
85
|
+
return await getGroupConnectionByDomain(ctx, config.component.public, normalizeDomain(domain));
|
|
86
|
+
},
|
|
87
|
+
list: async (ctx, opts) => {
|
|
88
|
+
return await listGroupConnections(ctx, config.component.public, {
|
|
89
|
+
where: opts?.where,
|
|
90
|
+
limit: opts?.limit,
|
|
91
|
+
cursor: opts?.cursor,
|
|
92
|
+
orderBy: opts?.orderBy,
|
|
93
|
+
order: opts?.order
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
update: async (ctx, connectionId, data) => {
|
|
97
|
+
await updateGroupConnection(ctx, config.component.public, {
|
|
98
|
+
connectionId,
|
|
99
|
+
data
|
|
100
|
+
});
|
|
101
|
+
return { connectionId };
|
|
102
|
+
},
|
|
103
|
+
delete: async (ctx, connectionId) => {
|
|
104
|
+
await deleteGroupConnection(ctx, config.component.public, connectionId);
|
|
105
|
+
return { connectionId };
|
|
106
|
+
},
|
|
107
|
+
status: async (ctx, connectionId) => {
|
|
108
|
+
const connection = await getGroupConnection(ctx, config.component.public, connectionId);
|
|
109
|
+
if (!connection) throw convexError({
|
|
110
|
+
code: "INVALID_PARAMETERS",
|
|
111
|
+
message: connectionNotFoundError
|
|
112
|
+
});
|
|
113
|
+
const policy$1 = await loadGroupPolicyOrThrow(ctx, connection.groupId);
|
|
114
|
+
const oidcConfig = getOidcConfig(connection.config);
|
|
115
|
+
const oidcSecret = await getGroupConnectionSecret(ctx, connection._id, GROUP_CONNECTION_OIDC_CLIENT_SECRET_KIND);
|
|
116
|
+
const samlConfig = getSamlConfig(connection.config);
|
|
117
|
+
const scimConfig = await getScimConfigByConnection(ctx, config.component.public, connectionId);
|
|
118
|
+
const domains = await listConnectionDomains(ctx, config.component.public, connectionId);
|
|
119
|
+
const oidcReady = oidcConfig?.enabled === true && typeof oidcConfig?.client?.id === "string" && oidcConfig.client.id.length > 0 && oidcSecret !== null && (typeof oidcConfig?.discovery?.issuer === "string" || typeof oidcConfig?.discovery?.discoveryUrl === "string");
|
|
120
|
+
const samlReady = samlConfig?.enabled === true && typeof samlConfig?.idp?.entityId === "string";
|
|
121
|
+
const scimReady = scimConfig?.status === "active";
|
|
122
|
+
const ready = connection.status === "active" && (connection.protocol === "oidc" ? oidcReady : samlReady);
|
|
123
|
+
return {
|
|
124
|
+
connectionId: connection._id,
|
|
125
|
+
status: connection.status,
|
|
126
|
+
ready,
|
|
127
|
+
domainCount: domains.length,
|
|
128
|
+
protocols: {
|
|
129
|
+
oidc: {
|
|
130
|
+
configured: connection.protocol === "oidc" ? oidcReady : false,
|
|
131
|
+
ready: connection.protocol === "oidc" ? oidcReady : false,
|
|
132
|
+
clientId: oidcConfig?.client?.id ?? null,
|
|
133
|
+
issuer: oidcConfig?.discovery?.issuer ?? oidcConfig?.discovery?.discoveryUrl ?? null
|
|
134
|
+
},
|
|
135
|
+
saml: {
|
|
136
|
+
configured: connection.protocol === "saml" ? samlReady : false,
|
|
137
|
+
ready: connection.protocol === "saml" ? samlReady : false,
|
|
138
|
+
entityId: samlConfig?.idp?.entityId ?? samlConfig?.idp?.issuer ?? null
|
|
139
|
+
},
|
|
140
|
+
scim: {
|
|
141
|
+
configured: scimReady,
|
|
142
|
+
ready: scimReady,
|
|
143
|
+
basePath: scimConfig?.basePath ?? null,
|
|
144
|
+
deprovisionMode: policy$1.provisioning.deprovision.mode
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
domain: {
|
|
151
|
+
add: async (ctx, data) => {
|
|
152
|
+
return await addConnectionDomain(ctx, config.component.public, {
|
|
153
|
+
...data,
|
|
154
|
+
domain: normalizeDomain(data.domain)
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
list: async (ctx, connectionId) => {
|
|
158
|
+
return await listConnectionDomains(ctx, config.component.public, connectionId);
|
|
159
|
+
},
|
|
160
|
+
validate: async (ctx, connectionId) => {
|
|
161
|
+
const connection = await getGroupConnection(ctx, config.component.public, connectionId);
|
|
162
|
+
if (connection === null) throw convexError({
|
|
163
|
+
code: "INVALID_PARAMETERS",
|
|
164
|
+
message: connectionNotFoundError
|
|
165
|
+
});
|
|
166
|
+
const domains = await listConnectionDomains(ctx, config.component.public, connectionId);
|
|
167
|
+
const primaryDomains = domains.filter((domain) => domain.isPrimary);
|
|
168
|
+
const verifiedDomains = domains.filter((domain) => domain.verifiedAt !== void 0);
|
|
169
|
+
const warnings = [];
|
|
170
|
+
if (domains.length === 0) warnings.push("No domains configured.");
|
|
171
|
+
if (primaryDomains.length === 0 && domains.length > 0) warnings.push("No primary domain configured.");
|
|
172
|
+
if (primaryDomains.length > 1) warnings.push("Multiple primary domains configured.");
|
|
173
|
+
if (verifiedDomains.length === 0 && domains.length > 0) warnings.push("No verified domains yet.");
|
|
174
|
+
return {
|
|
175
|
+
connectionId,
|
|
176
|
+
ready: connection.status === "active" && domains.length > 0 && primaryDomains.length === 1 && verifiedDomains.length > 0,
|
|
177
|
+
summary: {
|
|
178
|
+
domainCount: domains.length,
|
|
179
|
+
primaryCount: primaryDomains.length,
|
|
180
|
+
verifiedCount: verifiedDomains.length
|
|
181
|
+
},
|
|
182
|
+
domains: domains.map((domain) => toDomainSummary(domain)),
|
|
183
|
+
warnings
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
status: async (ctx, connectionId) => {
|
|
187
|
+
const connection = await getGroupConnection(ctx, config.component.public, connectionId);
|
|
188
|
+
if (connection === null) throw convexError({
|
|
189
|
+
code: "INVALID_PARAMETERS",
|
|
190
|
+
message: connectionNotFoundError
|
|
191
|
+
});
|
|
192
|
+
const domains = await listConnectionDomains(ctx, config.component.public, connectionId);
|
|
193
|
+
const primaryDomain = domains.find((domain) => domain.isPrimary) ?? null;
|
|
194
|
+
const verifiedDomains = domains.filter((domain) => domain.verifiedAt !== void 0);
|
|
195
|
+
const pendingChallenges = (await Promise.all(domains.map(async (domain) => {
|
|
196
|
+
const verification = await getConnectionDomainVerification(ctx, config.component.public, domain._id);
|
|
197
|
+
if (!verification || verification.expiresAt < Date.now()) return null;
|
|
198
|
+
return {
|
|
199
|
+
domain: domain.domain,
|
|
200
|
+
recordName: verification.recordName,
|
|
201
|
+
expiresAt: verification.expiresAt
|
|
202
|
+
};
|
|
203
|
+
}))).filter((challenge) => challenge !== null);
|
|
204
|
+
const warnings = [];
|
|
205
|
+
const nextSteps = [];
|
|
206
|
+
if (domains.length === 0) {
|
|
207
|
+
warnings.push("No domains configured.");
|
|
208
|
+
nextSteps.push("Attach at least one domain to this connection.");
|
|
209
|
+
}
|
|
210
|
+
if (primaryDomain === null && domains.length > 0) {
|
|
211
|
+
warnings.push("No primary domain configured.");
|
|
212
|
+
nextSteps.push("Mark one attached domain as the primary domain.");
|
|
213
|
+
}
|
|
214
|
+
if (verifiedDomains.length === 0 && domains.length > 0) {
|
|
215
|
+
warnings.push("No verified domains yet.");
|
|
216
|
+
nextSteps.push("Request a TXT challenge and confirm verification for at least one domain.");
|
|
217
|
+
}
|
|
218
|
+
if (primaryDomain !== null && primaryDomain.verifiedAt === void 0 && domains.length > 0) nextSteps.push(`Verify the primary domain ${primaryDomain.domain} to establish trusted ownership.`);
|
|
219
|
+
if (pendingChallenges.length > 0) nextSteps.push("If DNS is already updated, confirm the pending TXT challenge to complete verification.");
|
|
220
|
+
const primaryDomainVerified = primaryDomain?.verifiedAt !== void 0;
|
|
221
|
+
return {
|
|
222
|
+
connectionId,
|
|
223
|
+
ready: connection.status === "active" && domains.length > 0 && primaryDomain !== null && verifiedDomains.length > 0,
|
|
224
|
+
primaryDomain: primaryDomain === null ? null : {
|
|
225
|
+
domainId: primaryDomain._id,
|
|
226
|
+
domain: primaryDomain.domain,
|
|
227
|
+
isPrimary: true,
|
|
228
|
+
verified: primaryDomain.verifiedAt !== void 0,
|
|
229
|
+
verifiedAt: primaryDomain.verifiedAt ?? null
|
|
230
|
+
},
|
|
231
|
+
trustedDomains: verifiedDomains.map((domain) => ({
|
|
232
|
+
domainId: domain._id,
|
|
233
|
+
domain: domain.domain,
|
|
234
|
+
isPrimary: Boolean(domain.isPrimary),
|
|
235
|
+
verified: true,
|
|
236
|
+
verifiedAt: domain.verifiedAt ?? null
|
|
237
|
+
})),
|
|
238
|
+
pendingChallenges,
|
|
239
|
+
trust: {
|
|
240
|
+
domainDiscoveryReady: verifiedDomains.length > 0,
|
|
241
|
+
primaryDomainVerified,
|
|
242
|
+
automaticLinkingEligible: primaryDomainVerified && connection.status === "active" && verifiedDomains.length > 0
|
|
243
|
+
},
|
|
244
|
+
warnings,
|
|
245
|
+
nextSteps
|
|
246
|
+
};
|
|
247
|
+
},
|
|
248
|
+
remove: async (ctx, domainId) => {
|
|
249
|
+
await deleteConnectionDomain(ctx, config.component.public, domainId);
|
|
250
|
+
},
|
|
251
|
+
verification: {
|
|
252
|
+
request: async (ctx, args) => {
|
|
253
|
+
const connection = await loadConnectionOrThrow(ctx, args.connectionId);
|
|
254
|
+
const normalizedDomain = normalizeDomain(args.domain);
|
|
255
|
+
const domain = (await listConnectionDomains(ctx, config.component.public, connection._id)).find((entry) => entry.domain === normalizedDomain);
|
|
256
|
+
if (!domain) throw convexError({
|
|
257
|
+
code: "INVALID_PARAMETERS",
|
|
258
|
+
message: "Domain is not attached to this connection."
|
|
259
|
+
});
|
|
260
|
+
const requestedAt = Date.now();
|
|
261
|
+
const expiresAt = requestedAt + GROUP_CONNECTION_DOMAIN_VERIFICATION_TTL_MS;
|
|
262
|
+
const token = generateRandomString(32, INVITE_TOKEN_ALPHABET);
|
|
263
|
+
const tokenHash = await sha256(token);
|
|
264
|
+
const recordName = getDomainVerificationRecordName(normalizedDomain);
|
|
265
|
+
await upsertConnectionDomainVerification(ctx, config.component.public, {
|
|
266
|
+
connectionId: connection._id,
|
|
267
|
+
groupId: connection.groupId,
|
|
268
|
+
domainId: domain._id,
|
|
269
|
+
domain: normalizedDomain,
|
|
270
|
+
recordName,
|
|
271
|
+
token,
|
|
272
|
+
tokenHash,
|
|
273
|
+
requestedAt,
|
|
274
|
+
expiresAt
|
|
275
|
+
});
|
|
276
|
+
await recordGroupAuditEvent(ctx, {
|
|
277
|
+
connectionId: connection._id,
|
|
278
|
+
groupId: connection.groupId,
|
|
279
|
+
eventType: "group.sso.domain.verification_requested",
|
|
280
|
+
actorType: "system",
|
|
281
|
+
subjectType: "group_connection_domain",
|
|
282
|
+
subjectId: domain._id,
|
|
283
|
+
ok: true,
|
|
284
|
+
metadata: {
|
|
285
|
+
domain: normalizedDomain,
|
|
286
|
+
recordName,
|
|
287
|
+
expiresAt
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
return {
|
|
291
|
+
connectionId: connection._id,
|
|
292
|
+
domain: normalizedDomain,
|
|
293
|
+
requestedAt,
|
|
294
|
+
expiresAt,
|
|
295
|
+
challenge: {
|
|
296
|
+
recordType: "TXT",
|
|
297
|
+
recordName,
|
|
298
|
+
recordValue: token
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
},
|
|
302
|
+
confirm: async (ctx, args) => {
|
|
303
|
+
const connection = await loadConnectionOrThrow(ctx, args.connectionId);
|
|
304
|
+
const normalizedDomain = normalizeDomain(args.domain);
|
|
305
|
+
const domain = (await listConnectionDomains(ctx, config.component.public, connection._id)).find((entry) => entry.domain === normalizedDomain);
|
|
306
|
+
if (!domain) throw convexError({
|
|
307
|
+
code: "INVALID_PARAMETERS",
|
|
308
|
+
message: "Domain is not attached to this connection."
|
|
309
|
+
});
|
|
310
|
+
if (domain.verifiedAt !== void 0) return {
|
|
311
|
+
connectionId: connection._id,
|
|
312
|
+
domain: normalizedDomain,
|
|
313
|
+
verifiedAt: domain.verifiedAt,
|
|
314
|
+
checks: [{
|
|
315
|
+
name: "domain_verified",
|
|
316
|
+
ok: true,
|
|
317
|
+
message: "Domain is already verified."
|
|
318
|
+
}]
|
|
319
|
+
};
|
|
320
|
+
const verification = await getConnectionDomainVerification(ctx, config.component.public, domain._id);
|
|
321
|
+
const checks = [];
|
|
322
|
+
if (!verification) {
|
|
323
|
+
checks.push({
|
|
324
|
+
name: "verification_requested",
|
|
325
|
+
ok: false,
|
|
326
|
+
message: "No active domain verification challenge exists."
|
|
327
|
+
});
|
|
328
|
+
return {
|
|
329
|
+
connectionId: connection._id,
|
|
330
|
+
domain: normalizedDomain,
|
|
331
|
+
checks
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
checks.push({
|
|
335
|
+
name: "verification_requested",
|
|
336
|
+
ok: true
|
|
337
|
+
});
|
|
338
|
+
if (verification.expiresAt < Date.now()) {
|
|
339
|
+
await deleteConnectionDomainVerification(ctx, config.component.public, domain._id);
|
|
340
|
+
checks.push({
|
|
341
|
+
name: "challenge_active",
|
|
342
|
+
ok: false,
|
|
343
|
+
message: "The verification challenge expired. Request a new one."
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
connectionId: connection._id,
|
|
347
|
+
domain: normalizedDomain,
|
|
348
|
+
checks
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
checks.push({
|
|
352
|
+
name: "challenge_active",
|
|
353
|
+
ok: true
|
|
354
|
+
});
|
|
355
|
+
let txtValues;
|
|
356
|
+
try {
|
|
357
|
+
txtValues = await resolveTxtValues(verification.recordName);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
throw convexError({
|
|
360
|
+
code: "INTERNAL_ERROR",
|
|
361
|
+
message: error instanceof Error ? error.message : "Failed to resolve DNS TXT records."
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
checks.push({
|
|
365
|
+
name: "dns_record_present",
|
|
366
|
+
ok: txtValues.length > 0,
|
|
367
|
+
message: txtValues.length > 0 ? void 0 : `No TXT records found at ${verification.recordName}.`
|
|
368
|
+
});
|
|
369
|
+
const matches = txtValues.includes(verification.token);
|
|
370
|
+
checks.push({
|
|
371
|
+
name: "dns_record_matches",
|
|
372
|
+
ok: matches,
|
|
373
|
+
message: matches ? void 0 : `TXT record at ${verification.recordName} does not match the expected value.`
|
|
374
|
+
});
|
|
375
|
+
if (!checks.every((check) => check.ok)) return {
|
|
376
|
+
connectionId: connection._id,
|
|
377
|
+
domain: normalizedDomain,
|
|
378
|
+
checks
|
|
379
|
+
};
|
|
380
|
+
const verifiedAt = Date.now();
|
|
381
|
+
await verifyConnectionDomain(ctx, config.component.public, {
|
|
382
|
+
domainId: domain._id,
|
|
383
|
+
verifiedAt
|
|
384
|
+
});
|
|
385
|
+
await recordGroupAuditEvent(ctx, {
|
|
386
|
+
connectionId: connection._id,
|
|
387
|
+
groupId: connection.groupId,
|
|
388
|
+
eventType: "group.sso.domain.verified",
|
|
389
|
+
actorType: "system",
|
|
390
|
+
subjectType: "group_connection_domain",
|
|
391
|
+
subjectId: domain._id,
|
|
392
|
+
ok: true,
|
|
393
|
+
metadata: {
|
|
394
|
+
domain: normalizedDomain,
|
|
395
|
+
verifiedAt
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
return {
|
|
399
|
+
connectionId: connection._id,
|
|
400
|
+
domain: normalizedDomain,
|
|
401
|
+
verifiedAt,
|
|
402
|
+
checks
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
saml: {
|
|
408
|
+
configure: async (ctx, data) => {
|
|
409
|
+
let connection;
|
|
410
|
+
try {
|
|
411
|
+
connection = await getGroupConnection(ctx, config.component.public, data.connectionId);
|
|
412
|
+
} catch {
|
|
413
|
+
throw convexError({
|
|
414
|
+
code: "INTERNAL_ERROR",
|
|
415
|
+
message: "Failed to load connection."
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
if (connection === null) throw convexError({
|
|
419
|
+
code: "INVALID_PARAMETERS",
|
|
420
|
+
message: connectionNotFoundError
|
|
421
|
+
});
|
|
422
|
+
if (connection.protocol !== "saml") throw convexError({
|
|
423
|
+
code: "INVALID_PARAMETERS",
|
|
424
|
+
message: "This connection is not a SAML connection."
|
|
425
|
+
});
|
|
426
|
+
const metadataUrl = typeof data.metadata.url === "string" && data.metadata.url.length > 0 ? data.metadata.url : void 0;
|
|
427
|
+
let metadataXml;
|
|
428
|
+
if (metadataUrl) try {
|
|
429
|
+
metadataXml = await retryWithBackoff(async () => {
|
|
430
|
+
const response = await fetch(metadataUrl, { signal: AbortSignal.timeout(1e4) });
|
|
431
|
+
if (!response.ok) throw new Error(`Failed to fetch SAML metadata: ${response.status}`);
|
|
432
|
+
return await response.text();
|
|
433
|
+
});
|
|
434
|
+
} catch (error) {
|
|
435
|
+
throw convexError({
|
|
436
|
+
code: "INVALID_PARAMETERS",
|
|
437
|
+
message: error instanceof Error ? error.message : "Failed to fetch SAML metadata"
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
else if (data.metadata.xml) metadataXml = data.metadata.xml;
|
|
441
|
+
else throw convexError({
|
|
442
|
+
code: "INVALID_PARAMETERS",
|
|
443
|
+
message: "SAML registration requires metadataXml or metadataUrl."
|
|
444
|
+
});
|
|
445
|
+
let parsed;
|
|
446
|
+
try {
|
|
447
|
+
parsed = parseSamlIdpMetadataChecked({
|
|
448
|
+
metadataXml,
|
|
449
|
+
config: { protocols: { saml: { security: data.security } } }
|
|
450
|
+
});
|
|
451
|
+
} catch (error) {
|
|
452
|
+
throw convexError({
|
|
453
|
+
code: "INVALID_PARAMETERS",
|
|
454
|
+
message: error instanceof Error ? `Failed to parse SAML metadata: ${error.message}` : "Failed to parse SAML metadata."
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
log("DEBUG", "[group-sso] saml:configure:parsed", {
|
|
458
|
+
connectionId: data.connectionId,
|
|
459
|
+
metadataUrl,
|
|
460
|
+
entityId: parsed.entityId,
|
|
461
|
+
issuer: parsed.issuer
|
|
462
|
+
});
|
|
463
|
+
const baseConfig = upsertProtocolConfig(connection.config, "saml", {
|
|
464
|
+
enabled: true,
|
|
465
|
+
idp: {
|
|
466
|
+
metadataUrl,
|
|
467
|
+
metadataXml,
|
|
468
|
+
...parsed
|
|
469
|
+
},
|
|
470
|
+
serviceProvider: data.serviceProvider,
|
|
471
|
+
request: {
|
|
472
|
+
signAuthnRequests: data.request?.signAuthnRequests ?? parsed.wantsSignedAuthnRequests,
|
|
473
|
+
nameIdFormat: data.request?.nameIdFormat,
|
|
474
|
+
forceAuthn: data.request?.forceAuthn,
|
|
475
|
+
authnContextClassRefs: data.request?.authnContextClassRefs
|
|
476
|
+
},
|
|
477
|
+
profile: {
|
|
478
|
+
mapping: data.profile?.mapping,
|
|
479
|
+
extraFields: data.profile?.extraFields
|
|
480
|
+
},
|
|
481
|
+
security: data.security
|
|
482
|
+
});
|
|
483
|
+
const normalizedDomains = data.domains?.map(normalizeDomain);
|
|
484
|
+
const nextConfig = normalizedDomains ? {
|
|
485
|
+
...baseConfig,
|
|
486
|
+
domains: normalizedDomains
|
|
487
|
+
} : baseConfig;
|
|
488
|
+
const nextSamlConfig = nextConfig.protocols?.saml ?? void 0;
|
|
489
|
+
log("DEBUG", "[group-sso] saml:configure:nextConfig", {
|
|
490
|
+
connectionId: data.connectionId,
|
|
491
|
+
entityId: nextSamlConfig?.idp?.entityId ?? null,
|
|
492
|
+
issuer: nextSamlConfig?.idp?.issuer ?? null,
|
|
493
|
+
metadataUrl: nextSamlConfig?.idp?.metadataUrl ?? null,
|
|
494
|
+
hasMetadataXml: typeof nextSamlConfig?.idp?.metadataXml === "string"
|
|
495
|
+
});
|
|
496
|
+
try {
|
|
497
|
+
await updateGroupConnection(ctx, config.component.public, {
|
|
498
|
+
connectionId: connection._id,
|
|
499
|
+
data: {
|
|
500
|
+
status: "active",
|
|
501
|
+
config: nextConfig
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
} catch {
|
|
505
|
+
throw convexError({
|
|
506
|
+
code: "INTERNAL_ERROR",
|
|
507
|
+
message: "Failed to persist SAML registration."
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
if (normalizedDomains) for (const [index, domain] of normalizedDomains.entries()) try {
|
|
511
|
+
await addConnectionDomain(ctx, config.component.public, {
|
|
512
|
+
connectionId: connection._id,
|
|
513
|
+
groupId: connection.groupId,
|
|
514
|
+
domain,
|
|
515
|
+
isPrimary: index === 0
|
|
516
|
+
});
|
|
517
|
+
} catch {
|
|
518
|
+
throw convexError({
|
|
519
|
+
code: "INTERNAL_ERROR",
|
|
520
|
+
message: "Failed to persist connection domain."
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
await recordGroupAuditEvent(ctx, {
|
|
525
|
+
connectionId: connection._id,
|
|
526
|
+
groupId: connection.groupId,
|
|
527
|
+
eventType: "group.sso.saml.registered",
|
|
528
|
+
actorType: "system",
|
|
529
|
+
subjectType: "group_connection_saml",
|
|
530
|
+
subjectId: connection._id,
|
|
531
|
+
ok: true,
|
|
532
|
+
metadata: {
|
|
533
|
+
metadataUrl,
|
|
534
|
+
domains: normalizedDomains
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
} catch {
|
|
538
|
+
throw convexError({
|
|
539
|
+
code: "INTERNAL_ERROR",
|
|
540
|
+
message: "Failed to record SAML registration audit event."
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
connectionId: connection._id,
|
|
545
|
+
groupId: connection.groupId
|
|
546
|
+
};
|
|
547
|
+
},
|
|
548
|
+
refresh: async (ctx, data) => {
|
|
549
|
+
let connection;
|
|
550
|
+
try {
|
|
551
|
+
connection = await getGroupConnection(ctx, config.component.public, data.connectionId);
|
|
552
|
+
} catch {
|
|
553
|
+
throw convexError({
|
|
554
|
+
code: "INTERNAL_ERROR",
|
|
555
|
+
message: "Failed to load connection."
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
if (connection === null) throw convexError({
|
|
559
|
+
code: "INVALID_PARAMETERS",
|
|
560
|
+
message: connectionNotFoundError
|
|
561
|
+
});
|
|
562
|
+
const samlConfig = connection.config?.protocols?.saml;
|
|
563
|
+
if (connection.protocol !== "saml") throw convexError({
|
|
564
|
+
code: "INVALID_PARAMETERS",
|
|
565
|
+
message: "This connection is not a SAML connection."
|
|
566
|
+
});
|
|
567
|
+
if (typeof samlConfig?.idp?.metadataUrl !== "string") throw convexError({
|
|
568
|
+
code: "INVALID_PARAMETERS",
|
|
569
|
+
message: "SAML metadataUrl is not configured."
|
|
570
|
+
});
|
|
571
|
+
const metadataUrl = samlConfig.idp.metadataUrl;
|
|
572
|
+
let metadataXml;
|
|
573
|
+
try {
|
|
574
|
+
metadataXml = await retryWithBackoff(async () => {
|
|
575
|
+
const response = await fetch(metadataUrl, { signal: AbortSignal.timeout(1e4) });
|
|
576
|
+
if (!response.ok) throw new Error(`Failed to fetch SAML metadata: ${response.status}`);
|
|
577
|
+
return await response.text();
|
|
578
|
+
});
|
|
579
|
+
} catch (error) {
|
|
580
|
+
throw convexError({
|
|
581
|
+
code: "INVALID_PARAMETERS",
|
|
582
|
+
message: error instanceof Error ? error.message : "Failed to fetch SAML metadata"
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
let parsed;
|
|
586
|
+
try {
|
|
587
|
+
parsed = parseSamlIdpMetadataChecked({
|
|
588
|
+
metadataXml,
|
|
589
|
+
config: connection.config
|
|
590
|
+
});
|
|
591
|
+
} catch (error) {
|
|
592
|
+
throw convexError({
|
|
593
|
+
code: "INVALID_PARAMETERS",
|
|
594
|
+
message: error instanceof Error ? `Failed to parse SAML metadata: ${error.message}` : "Failed to parse SAML metadata."
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
const nextConfig = upsertProtocolConfig(connection.config, "saml", {
|
|
598
|
+
enabled: true,
|
|
599
|
+
idp: {
|
|
600
|
+
metadataUrl,
|
|
601
|
+
metadataXml,
|
|
602
|
+
...parsed
|
|
603
|
+
},
|
|
604
|
+
serviceProvider: samlConfig.serviceProvider,
|
|
605
|
+
request: samlConfig.request,
|
|
606
|
+
profile: samlConfig.profile,
|
|
607
|
+
security: samlConfig.security
|
|
608
|
+
});
|
|
609
|
+
try {
|
|
610
|
+
await updateGroupConnection(ctx, config.component.public, {
|
|
611
|
+
connectionId: connection._id,
|
|
612
|
+
data: {
|
|
613
|
+
status: connection.status,
|
|
614
|
+
config: nextConfig
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
} catch {
|
|
618
|
+
throw convexError({
|
|
619
|
+
code: "INTERNAL_ERROR",
|
|
620
|
+
message: "Failed to persist refreshed SAML metadata."
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
try {
|
|
624
|
+
await recordGroupAuditEvent(ctx, {
|
|
625
|
+
connectionId: connection._id,
|
|
626
|
+
groupId: connection.groupId,
|
|
627
|
+
eventType: "group.sso.saml.refreshed",
|
|
628
|
+
actorType: "system",
|
|
629
|
+
subjectType: "group_connection_saml",
|
|
630
|
+
subjectId: connection._id,
|
|
631
|
+
ok: true,
|
|
632
|
+
metadata: { metadataUrl }
|
|
633
|
+
});
|
|
634
|
+
} catch {
|
|
635
|
+
throw convexError({
|
|
636
|
+
code: "INTERNAL_ERROR",
|
|
637
|
+
message: "Failed to record SAML refresh audit event."
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
connectionId: connection._id,
|
|
642
|
+
groupId: connection.groupId
|
|
643
|
+
};
|
|
644
|
+
},
|
|
645
|
+
get: async (ctx, connectionId) => {
|
|
646
|
+
let connection;
|
|
647
|
+
try {
|
|
648
|
+
connection = await getGroupConnection(ctx, config.component.public, connectionId);
|
|
649
|
+
} catch {
|
|
650
|
+
throw convexError({
|
|
651
|
+
code: "INTERNAL_ERROR",
|
|
652
|
+
message: "Failed to load connection."
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
if (connection === null) throw convexError({
|
|
656
|
+
code: "INVALID_PARAMETERS",
|
|
657
|
+
message: connectionNotFoundError
|
|
658
|
+
});
|
|
659
|
+
return getSamlConfig(connection.config);
|
|
660
|
+
},
|
|
661
|
+
status: (ctx, connectionId) => {
|
|
662
|
+
return getGroupConnection(ctx, config.component.public, connectionId).then((connection) => {
|
|
663
|
+
if (!connection) throw convexError({
|
|
664
|
+
code: "INVALID_PARAMETERS",
|
|
665
|
+
message: connectionNotFoundError
|
|
666
|
+
});
|
|
667
|
+
const currentConfig = getSamlConfig(connection.config);
|
|
668
|
+
const configured = currentConfig.enabled === true;
|
|
669
|
+
return {
|
|
670
|
+
connectionId,
|
|
671
|
+
configured,
|
|
672
|
+
ready: configured && typeof currentConfig.idp?.entityId === "string",
|
|
673
|
+
config: currentConfig,
|
|
674
|
+
checks: [{
|
|
675
|
+
name: "saml_configured",
|
|
676
|
+
ok: configured,
|
|
677
|
+
message: configured ? void 0 : "SAML is not configured."
|
|
678
|
+
}]
|
|
679
|
+
};
|
|
680
|
+
});
|
|
681
|
+
},
|
|
682
|
+
metadata: async (ctx, opts) => {
|
|
683
|
+
const connection = await getGroupConnection(ctx, config.component.public, opts.connectionId);
|
|
684
|
+
if (!connection) throw convexError({
|
|
685
|
+
code: "INVALID_PARAMETERS",
|
|
686
|
+
message: "Connection not found."
|
|
687
|
+
});
|
|
688
|
+
return createServiceProviderMetadata(getSamlServiceProviderOptions({
|
|
689
|
+
rootUrl: requireEnv("CONVEX_SITE_URL"),
|
|
690
|
+
source: {
|
|
691
|
+
kind: "connection",
|
|
692
|
+
id: connection._id
|
|
693
|
+
},
|
|
694
|
+
config: connection.config,
|
|
695
|
+
overrides: {
|
|
696
|
+
entityId: opts.entityId,
|
|
697
|
+
acsUrl: opts.acsUrl,
|
|
698
|
+
sloUrl: opts.sloUrl
|
|
699
|
+
}
|
|
700
|
+
}));
|
|
701
|
+
},
|
|
702
|
+
validate: async (ctx, connectionId) => {
|
|
703
|
+
const checks = [];
|
|
704
|
+
const connection = await getGroupConnection(ctx, config.component.public, connectionId);
|
|
705
|
+
if (!connection) return {
|
|
706
|
+
ok: false,
|
|
707
|
+
connectionId,
|
|
708
|
+
checks: [{
|
|
709
|
+
name: "group_connection_exists",
|
|
710
|
+
ok: false,
|
|
711
|
+
message: "Connection not found."
|
|
712
|
+
}]
|
|
713
|
+
};
|
|
714
|
+
const samlConfig = connection.config?.protocols?.saml;
|
|
715
|
+
const samlConfigured = samlConfig?.enabled === true && typeof samlConfig?.idp?.metadataXml === "string";
|
|
716
|
+
checks.push({
|
|
717
|
+
name: "saml_configured",
|
|
718
|
+
ok: samlConfigured,
|
|
719
|
+
message: samlConfigured ? void 0 : "SAML is not configured."
|
|
720
|
+
});
|
|
721
|
+
const hasIdpMetadata = typeof samlConfig?.idp?.metadataXml === "string" && samlConfig.idp.metadataXml.length > 0;
|
|
722
|
+
checks.push({
|
|
723
|
+
name: "idp_metadata_present",
|
|
724
|
+
ok: hasIdpMetadata,
|
|
725
|
+
message: hasIdpMetadata ? void 0 : "IdP metadata XML is missing."
|
|
726
|
+
});
|
|
727
|
+
const reparsedIdp = hasIdpMetadata && typeof samlConfig?.idp?.metadataXml === "string" ? (() => {
|
|
728
|
+
try {
|
|
729
|
+
return parseSamlIdpMetadataChecked({
|
|
730
|
+
metadataXml: samlConfig.idp.metadataXml,
|
|
731
|
+
config: connection.config
|
|
732
|
+
});
|
|
733
|
+
} catch {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
})() : null;
|
|
737
|
+
log("DEBUG", "[group-sso] saml:validate:idp", {
|
|
738
|
+
connectionId,
|
|
739
|
+
entityId: samlConfig?.idp?.entityId ?? null,
|
|
740
|
+
issuer: samlConfig?.idp?.issuer ?? null,
|
|
741
|
+
metadataUrl: samlConfig?.idp?.metadataUrl ?? null,
|
|
742
|
+
reparsedEntityId: reparsedIdp?.entityId ?? null,
|
|
743
|
+
reparsedIssuer: reparsedIdp?.issuer ?? null
|
|
744
|
+
});
|
|
745
|
+
const hasEntityId = typeof samlConfig?.idp?.entityId === "string" && samlConfig.idp.entityId.length > 0 || typeof samlConfig?.idp?.issuer === "string" && samlConfig.idp.issuer.length > 0 || typeof reparsedIdp?.entityId === "string" && reparsedIdp.entityId.length > 0 || typeof reparsedIdp?.issuer === "string" && reparsedIdp.issuer.length > 0;
|
|
746
|
+
checks.push({
|
|
747
|
+
name: "idp_entity_id",
|
|
748
|
+
ok: hasEntityId,
|
|
749
|
+
message: hasEntityId ? void 0 : "IdP entityId could not be parsed from metadata."
|
|
750
|
+
});
|
|
751
|
+
let spMetadataOk = false;
|
|
752
|
+
let spMetadataMessage;
|
|
753
|
+
if (samlConfigured) try {
|
|
754
|
+
createServiceProviderMetadata(getSamlServiceProviderOptions({
|
|
755
|
+
rootUrl: requireEnv("CONVEX_SITE_URL"),
|
|
756
|
+
source: {
|
|
757
|
+
kind: "connection",
|
|
758
|
+
id: connection._id
|
|
759
|
+
},
|
|
760
|
+
config: connection.config,
|
|
761
|
+
overrides: {}
|
|
762
|
+
}));
|
|
763
|
+
spMetadataOk = true;
|
|
764
|
+
} catch (e) {
|
|
765
|
+
spMetadataMessage = e instanceof Error ? e.message : "SP metadata generation failed.";
|
|
766
|
+
}
|
|
767
|
+
else spMetadataMessage = "Skipped — SAML not configured.";
|
|
768
|
+
checks.push({
|
|
769
|
+
name: "sp_metadata_generates",
|
|
770
|
+
ok: spMetadataOk,
|
|
771
|
+
message: spMetadataMessage
|
|
772
|
+
});
|
|
773
|
+
const requiresSignedAssertions = samlConfig?.security?.requireSignedAssertions === true;
|
|
774
|
+
const hasSigningCert = reparsedIdp?.signingCert !== null;
|
|
775
|
+
checks.push({
|
|
776
|
+
name: "signed_assertions_compatible",
|
|
777
|
+
ok: !requiresSignedAssertions || hasSigningCert,
|
|
778
|
+
message: !requiresSignedAssertions || hasSigningCert ? void 0 : "Signed assertions are required but the IdP metadata has no signing certificate."
|
|
779
|
+
});
|
|
780
|
+
const signAuthnRequests = samlConfig?.request?.signAuthnRequests === true;
|
|
781
|
+
const hasSpPrivateKey = typeof samlConfig?.serviceProvider?.privateKey === "string";
|
|
782
|
+
checks.push({
|
|
783
|
+
name: "authn_request_signing_compatible",
|
|
784
|
+
ok: !signAuthnRequests || hasSpPrivateKey,
|
|
785
|
+
message: !signAuthnRequests || hasSpPrivateKey ? void 0 : "signAuthnRequests is enabled but no SP privateKey is configured."
|
|
786
|
+
});
|
|
787
|
+
checks.push({
|
|
788
|
+
name: "timestamp_validation_configured",
|
|
789
|
+
ok: true,
|
|
790
|
+
message: samlConfig?.security?.requireTimestamps === true ? `Timestamp validation enabled with clock skew ${samlConfig.security.clockSkewSeconds ?? 300} seconds.` : "Timestamp validation uses compatibility defaults."
|
|
791
|
+
});
|
|
792
|
+
return {
|
|
793
|
+
ok: checks.every((c) => c.ok),
|
|
794
|
+
connectionId: connection._id,
|
|
795
|
+
checks
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
policy,
|
|
800
|
+
oidc: {
|
|
801
|
+
configure: async (ctx, data) => {
|
|
802
|
+
if (data.discovery.issuer === void 0 && data.discovery.discoveryUrl === void 0) throw convexError({
|
|
803
|
+
code: "INVALID_PARAMETERS",
|
|
804
|
+
message: "OIDC registration requires issuer or discoveryUrl."
|
|
805
|
+
});
|
|
806
|
+
let connection;
|
|
807
|
+
try {
|
|
808
|
+
connection = await getGroupConnection(ctx, config.component.public, data.connectionId);
|
|
809
|
+
} catch {
|
|
810
|
+
throw convexError({
|
|
811
|
+
code: "INTERNAL_ERROR",
|
|
812
|
+
message: "Failed to load connection."
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
if (connection === null) throw convexError({
|
|
816
|
+
code: "INVALID_PARAMETERS",
|
|
817
|
+
message: connectionNotFoundError
|
|
818
|
+
});
|
|
819
|
+
if (connection.protocol !== "oidc") throw convexError({
|
|
820
|
+
code: "INVALID_PARAMETERS",
|
|
821
|
+
message: "This connection is not an OIDC connection."
|
|
822
|
+
});
|
|
823
|
+
const nextConfig = upsertProtocolConfig(connection.config, "oidc", {
|
|
824
|
+
enabled: true,
|
|
825
|
+
discovery: {
|
|
826
|
+
issuer: data.discovery.issuer,
|
|
827
|
+
discoveryUrl: data.discovery.discoveryUrl,
|
|
828
|
+
jwksUri: data.discovery.jwksUri,
|
|
829
|
+
audience: data.discovery.audience
|
|
830
|
+
},
|
|
831
|
+
client: {
|
|
832
|
+
id: data.client.id,
|
|
833
|
+
authMethod: data.client.authMethod
|
|
834
|
+
},
|
|
835
|
+
request: {
|
|
836
|
+
scopes: data.request?.scopes ?? [
|
|
837
|
+
"openid",
|
|
838
|
+
"profile",
|
|
839
|
+
"email"
|
|
840
|
+
],
|
|
841
|
+
loginHint: data.request?.loginHint,
|
|
842
|
+
authorizationParams: data.request?.authorizationParams
|
|
843
|
+
},
|
|
844
|
+
security: {
|
|
845
|
+
clockToleranceSeconds: data.security?.clockToleranceSeconds,
|
|
846
|
+
strictIssuer: data.security?.strictIssuer
|
|
847
|
+
},
|
|
848
|
+
profile: {
|
|
849
|
+
mapping: data.profile?.mapping,
|
|
850
|
+
extraFields: data.profile?.extraFields
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
try {
|
|
854
|
+
await updateGroupConnection(ctx, config.component.public, {
|
|
855
|
+
connectionId: data.connectionId,
|
|
856
|
+
data: { config: nextConfig }
|
|
857
|
+
});
|
|
858
|
+
} catch {
|
|
859
|
+
throw convexError({
|
|
860
|
+
code: "INTERNAL_ERROR",
|
|
861
|
+
message: "Failed to persist OIDC registration."
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
if (data.client.secret !== void 0) {
|
|
865
|
+
let ciphertext;
|
|
866
|
+
try {
|
|
867
|
+
ciphertext = await encryptSecret(data.client.secret);
|
|
868
|
+
} catch {
|
|
869
|
+
throw convexError({
|
|
870
|
+
code: "INTERNAL_ERROR",
|
|
871
|
+
message: "Failed to encrypt OIDC client secret."
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
try {
|
|
875
|
+
await upsertGroupConnectionSecret(ctx, config.component.public, {
|
|
876
|
+
connectionId: data.connectionId,
|
|
877
|
+
groupId: connection.groupId,
|
|
878
|
+
kind: GROUP_CONNECTION_OIDC_CLIENT_SECRET_KIND,
|
|
879
|
+
ciphertext,
|
|
880
|
+
updatedAt: Date.now()
|
|
881
|
+
});
|
|
882
|
+
} catch {
|
|
883
|
+
throw convexError({
|
|
884
|
+
code: "INTERNAL_ERROR",
|
|
885
|
+
message: "Failed to persist OIDC client secret."
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
try {
|
|
890
|
+
await recordGroupAuditEvent(ctx, {
|
|
891
|
+
connectionId: data.connectionId,
|
|
892
|
+
groupId: connection.groupId,
|
|
893
|
+
eventType: "group.sso.oidc.registered",
|
|
894
|
+
actorType: "system",
|
|
895
|
+
subjectType: "group_connection_oidc",
|
|
896
|
+
subjectId: data.connectionId,
|
|
897
|
+
ok: true,
|
|
898
|
+
metadata: {
|
|
899
|
+
issuer: data.discovery.issuer,
|
|
900
|
+
discoveryUrl: data.discovery.discoveryUrl,
|
|
901
|
+
jwksUri: data.discovery.jwksUri,
|
|
902
|
+
audience: data.discovery.audience,
|
|
903
|
+
tokenEndpointAuthMethod: data.client.authMethod
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
} catch {
|
|
907
|
+
throw convexError({
|
|
908
|
+
code: "INTERNAL_ERROR",
|
|
909
|
+
message: "Failed to record OIDC registration audit event."
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
let secret;
|
|
913
|
+
try {
|
|
914
|
+
secret = await getGroupConnectionSecret(ctx, data.connectionId, GROUP_CONNECTION_OIDC_CLIENT_SECRET_KIND);
|
|
915
|
+
} catch {
|
|
916
|
+
throw convexError({
|
|
917
|
+
code: "INTERNAL_ERROR",
|
|
918
|
+
message: "Failed to load OIDC secret metadata."
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
return withOidcSecretState(getPublicOidcConfig(nextConfig), secret !== null);
|
|
922
|
+
},
|
|
923
|
+
get: async (ctx, connectionId) => {
|
|
924
|
+
let connection;
|
|
925
|
+
try {
|
|
926
|
+
connection = await getGroupConnection(ctx, config.component.public, connectionId);
|
|
927
|
+
} catch {
|
|
928
|
+
throw convexError({
|
|
929
|
+
code: "INTERNAL_ERROR",
|
|
930
|
+
message: "Failed to load connection."
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
if (connection === null) throw convexError({
|
|
934
|
+
code: "INVALID_PARAMETERS",
|
|
935
|
+
message: connectionNotFoundError
|
|
936
|
+
});
|
|
937
|
+
let secret;
|
|
938
|
+
try {
|
|
939
|
+
secret = await getGroupConnectionSecret(ctx, connection._id, GROUP_CONNECTION_OIDC_CLIENT_SECRET_KIND);
|
|
940
|
+
} catch {
|
|
941
|
+
throw convexError({
|
|
942
|
+
code: "INTERNAL_ERROR",
|
|
943
|
+
message: "Failed to load OIDC secret metadata."
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
return withOidcSecretState(getPublicOidcConfig(connection.config), secret !== null);
|
|
947
|
+
},
|
|
948
|
+
status: (ctx, connectionId) => {
|
|
949
|
+
return Promise.all([getGroupConnection(ctx, config.component.public, connectionId), getGroupConnectionSecret(ctx, connectionId, GROUP_CONNECTION_OIDC_CLIENT_SECRET_KIND)]).then(([connection, secret]) => {
|
|
950
|
+
if (!connection) throw convexError({
|
|
951
|
+
code: "INVALID_PARAMETERS",
|
|
952
|
+
message: connectionNotFoundError
|
|
953
|
+
});
|
|
954
|
+
const currentConfig = getPublicOidcConfig(connection.config);
|
|
955
|
+
const oidcConfig = getOidcConfig(connection.config);
|
|
956
|
+
const configured = currentConfig.enabled === true && typeof oidcConfig.client?.id === "string" && (typeof oidcConfig.discovery?.issuer === "string" || typeof oidcConfig.discovery?.discoveryUrl === "string");
|
|
957
|
+
return {
|
|
958
|
+
connectionId,
|
|
959
|
+
configured,
|
|
960
|
+
ready: configured && secret !== null,
|
|
961
|
+
config: withOidcSecretState(currentConfig, secret !== null),
|
|
962
|
+
checks: [{
|
|
963
|
+
name: "oidc_configured",
|
|
964
|
+
ok: configured,
|
|
965
|
+
message: configured ? void 0 : "OIDC is not configured."
|
|
966
|
+
}, {
|
|
967
|
+
name: "client_secret_stored",
|
|
968
|
+
ok: secret !== null,
|
|
969
|
+
message: secret !== null ? void 0 : "OIDC client secret is missing."
|
|
970
|
+
}]
|
|
971
|
+
};
|
|
972
|
+
});
|
|
973
|
+
},
|
|
974
|
+
signIn: async (ctx, data) => {
|
|
975
|
+
log("DEBUG", "[group-sso] resolver:start", {
|
|
976
|
+
connectionId: data.connectionId,
|
|
977
|
+
email: data.email,
|
|
978
|
+
domain: data.domain,
|
|
979
|
+
redirectTo: data.redirectTo
|
|
980
|
+
});
|
|
981
|
+
let connection;
|
|
982
|
+
if (data.connectionId !== void 0) {
|
|
983
|
+
try {
|
|
984
|
+
connection = await getGroupConnection(ctx, config.component.public, data.connectionId);
|
|
985
|
+
} catch {
|
|
986
|
+
throw convexError({
|
|
987
|
+
code: "INTERNAL_ERROR",
|
|
988
|
+
message: "Failed to load connection."
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
if (connection === null) throw convexError({
|
|
992
|
+
code: "INVALID_PARAMETERS",
|
|
993
|
+
message: connectionNotFoundError
|
|
994
|
+
});
|
|
995
|
+
} else if (data.domain !== void 0 || data.email !== void 0) {
|
|
996
|
+
let result;
|
|
997
|
+
try {
|
|
998
|
+
result = await getGroupConnectionByDomain(ctx, config.component.public, normalizeDomain(data.domain ?? String(data.email).split("@").pop() ?? ""));
|
|
999
|
+
} catch {
|
|
1000
|
+
throw convexError({
|
|
1001
|
+
code: "INTERNAL_ERROR",
|
|
1002
|
+
message: "Failed to resolve connection by domain."
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
log("DEBUG", "[group-sso] resolver:domainLookup", result);
|
|
1006
|
+
if (result?.connection && result.domain?.verifiedAt !== void 0) connection = result.connection;
|
|
1007
|
+
else throw convexError({
|
|
1008
|
+
code: "INVALID_PARAMETERS",
|
|
1009
|
+
message: "No group connection matched the provided input."
|
|
1010
|
+
});
|
|
1011
|
+
} else throw convexError({
|
|
1012
|
+
code: "INVALID_PARAMETERS",
|
|
1013
|
+
message: "No group connection matched the provided input."
|
|
1014
|
+
});
|
|
1015
|
+
if (connection.status !== "active") throw convexError({
|
|
1016
|
+
code: "INVALID_PARAMETERS",
|
|
1017
|
+
message: "Group connection is not active."
|
|
1018
|
+
});
|
|
1019
|
+
const protocol = resolveGroupConnectionProtocol(connection);
|
|
1020
|
+
log("DEBUG", "[group-sso] resolver:connection", {
|
|
1021
|
+
connectionId: connection._id,
|
|
1022
|
+
status: connection.status,
|
|
1023
|
+
protocol
|
|
1024
|
+
});
|
|
1025
|
+
const { signInPath, callbackPath, providerId } = protocol === "oidc" ? (() => {
|
|
1026
|
+
const urls = getGroupOidcUrls({
|
|
1027
|
+
rootUrl: requireEnv("CONVEX_SITE_URL"),
|
|
1028
|
+
connectionId: connection._id,
|
|
1029
|
+
sharedRedirectURI: deps.sharedOidcRedirectURI
|
|
1030
|
+
});
|
|
1031
|
+
return {
|
|
1032
|
+
signInPath: urls.signInUrl,
|
|
1033
|
+
callbackPath: urls.callbackUrl,
|
|
1034
|
+
providerId: groupOidcProviderId(connection._id)
|
|
1035
|
+
};
|
|
1036
|
+
})() : (() => {
|
|
1037
|
+
const urls = getGroupSamlUrls({
|
|
1038
|
+
rootUrl: requireEnv("CONVEX_SITE_URL"),
|
|
1039
|
+
source: {
|
|
1040
|
+
kind: "connection",
|
|
1041
|
+
id: connection._id
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
return {
|
|
1045
|
+
signInPath: `${requireEnv("CONVEX_SITE_URL")}/api/auth/connections/${connection._id}/saml/signin`,
|
|
1046
|
+
callbackPath: urls.acsUrl,
|
|
1047
|
+
providerId: groupSamlProviderId(connection._id)
|
|
1048
|
+
};
|
|
1049
|
+
})();
|
|
1050
|
+
log("DEBUG", "[group-sso] resolver:paths", {
|
|
1051
|
+
connectionId: connection._id,
|
|
1052
|
+
signInPath,
|
|
1053
|
+
callbackPath
|
|
1054
|
+
});
|
|
1055
|
+
return {
|
|
1056
|
+
connectionId: connection._id,
|
|
1057
|
+
protocol,
|
|
1058
|
+
providerId,
|
|
1059
|
+
signInPath: protocol === "oidc" && typeof data.loginHint === "string" ? (() => {
|
|
1060
|
+
const signInUrl = new URL(signInPath);
|
|
1061
|
+
signInUrl.searchParams.set("loginHint", data.loginHint);
|
|
1062
|
+
return signInUrl.toString();
|
|
1063
|
+
})() : signInPath,
|
|
1064
|
+
callbackPath,
|
|
1065
|
+
redirectTo: data.redirectTo
|
|
1066
|
+
};
|
|
1067
|
+
},
|
|
1068
|
+
validate: async (ctx, connectionId) => {
|
|
1069
|
+
const checks = [];
|
|
1070
|
+
const connection = await getGroupConnection(ctx, config.component.public, connectionId);
|
|
1071
|
+
if (!connection) return {
|
|
1072
|
+
ok: false,
|
|
1073
|
+
connectionId,
|
|
1074
|
+
checks: [{
|
|
1075
|
+
name: "group_connection_exists",
|
|
1076
|
+
ok: false,
|
|
1077
|
+
message: "Connection not found."
|
|
1078
|
+
}]
|
|
1079
|
+
};
|
|
1080
|
+
const oidc = getOidcConfig(connection.config);
|
|
1081
|
+
const secret = await getGroupConnectionSecret(ctx, connection._id, GROUP_CONNECTION_OIDC_CLIENT_SECRET_KIND);
|
|
1082
|
+
const oidcConfigured = oidc.enabled === true && typeof oidc.client?.id === "string" && oidc.client.id.length > 0;
|
|
1083
|
+
checks.push({
|
|
1084
|
+
name: "oidc_configured",
|
|
1085
|
+
ok: oidcConfigured,
|
|
1086
|
+
message: oidcConfigured ? void 0 : "OIDC is not configured."
|
|
1087
|
+
});
|
|
1088
|
+
const hasClientId = typeof oidc.client?.id === "string" && oidc.client.id.length > 0;
|
|
1089
|
+
checks.push({
|
|
1090
|
+
name: "client_id_present",
|
|
1091
|
+
ok: hasClientId,
|
|
1092
|
+
message: hasClientId ? void 0 : "clientId is missing."
|
|
1093
|
+
});
|
|
1094
|
+
checks.push({
|
|
1095
|
+
name: "client_secret_stored",
|
|
1096
|
+
ok: secret !== null,
|
|
1097
|
+
message: secret !== null ? void 0 : "OIDC client secret is missing."
|
|
1098
|
+
});
|
|
1099
|
+
const discoveryConfig = typeof oidc.discovery === "object" && oidc.discovery !== null ? oidc.discovery : {};
|
|
1100
|
+
const clientConfig = typeof oidc.client === "object" && oidc.client !== null ? oidc.client : {};
|
|
1101
|
+
const discoveryTarget = discoveryConfig.discoveryUrl ?? discoveryConfig.issuer;
|
|
1102
|
+
const hasDiscovery = typeof discoveryTarget === "string" && discoveryTarget.length > 0;
|
|
1103
|
+
checks.push({
|
|
1104
|
+
name: "issuer_or_discovery_url_present",
|
|
1105
|
+
ok: hasDiscovery,
|
|
1106
|
+
message: hasDiscovery ? void 0 : "issuer or discoveryUrl is missing."
|
|
1107
|
+
});
|
|
1108
|
+
let discoveryOk = false;
|
|
1109
|
+
let discoveryMessage;
|
|
1110
|
+
if (hasDiscovery) {
|
|
1111
|
+
const discoveryUrl = typeof discoveryConfig.discoveryUrl === "string" && discoveryConfig.discoveryUrl.length > 0 ? discoveryConfig.discoveryUrl : `${String(discoveryConfig.issuer)}/.well-known/openid-configuration`;
|
|
1112
|
+
try {
|
|
1113
|
+
const res = await fetch(discoveryUrl, {
|
|
1114
|
+
headers: { Accept: "application/json" },
|
|
1115
|
+
signal: AbortSignal.timeout(8e3)
|
|
1116
|
+
});
|
|
1117
|
+
if (!res.ok) discoveryMessage = `Discovery endpoint returned ${res.status}.`;
|
|
1118
|
+
else {
|
|
1119
|
+
const json = await res.json();
|
|
1120
|
+
if (typeof json.issuer !== "string") discoveryMessage = "Discovery document is missing issuer field.";
|
|
1121
|
+
else if (typeof json.authorization_endpoint !== "string") discoveryMessage = "Discovery document is missing authorization_endpoint.";
|
|
1122
|
+
else if (typeof json.token_endpoint !== "string") discoveryMessage = "Discovery document is missing token_endpoint.";
|
|
1123
|
+
else if (discoveryConfig.jwksUri === void 0 && typeof json.jwks_uri !== "string") discoveryMessage = "Discovery document is missing jwks_uri and no jwksUri override is configured.";
|
|
1124
|
+
else discoveryOk = true;
|
|
1125
|
+
}
|
|
1126
|
+
} catch (e) {
|
|
1127
|
+
discoveryMessage = e instanceof Error ? `Discovery fetch failed: ${e.message}` : "Discovery fetch failed.";
|
|
1128
|
+
}
|
|
1129
|
+
} else discoveryMessage = "Skipped — issuer or discoveryUrl not set.";
|
|
1130
|
+
checks.push({
|
|
1131
|
+
name: "discovery_reachable",
|
|
1132
|
+
ok: discoveryOk,
|
|
1133
|
+
message: discoveryMessage
|
|
1134
|
+
});
|
|
1135
|
+
const hasValidTokenAuthMethod = clientConfig.authMethod === void 0 || clientConfig.authMethod === "client_secret_post" || clientConfig.authMethod === "client_secret_basic";
|
|
1136
|
+
checks.push({
|
|
1137
|
+
name: "token_endpoint_auth_method_supported",
|
|
1138
|
+
ok: hasValidTokenAuthMethod,
|
|
1139
|
+
message: hasValidTokenAuthMethod ? void 0 : "tokenEndpointAuthMethod must be client_secret_post or client_secret_basic."
|
|
1140
|
+
});
|
|
1141
|
+
const hasJwksUri = discoveryConfig.jwksUri === void 0 || typeof discoveryConfig.jwksUri === "string" && discoveryConfig.jwksUri.length > 0;
|
|
1142
|
+
checks.push({
|
|
1143
|
+
name: "jwks_uri_present",
|
|
1144
|
+
ok: hasJwksUri,
|
|
1145
|
+
message: hasJwksUri ? void 0 : "jwksUri is empty."
|
|
1146
|
+
});
|
|
1147
|
+
const hasAudience = discoveryConfig.audience === void 0 || typeof discoveryConfig.audience === "string" || Array.isArray(discoveryConfig.audience) && discoveryConfig.audience.every((value) => typeof value === "string");
|
|
1148
|
+
checks.push({
|
|
1149
|
+
name: "audience_valid",
|
|
1150
|
+
ok: hasAudience,
|
|
1151
|
+
message: hasAudience ? void 0 : "audience must be a string or string array."
|
|
1152
|
+
});
|
|
1153
|
+
return {
|
|
1154
|
+
ok: checks.every((c) => c.ok),
|
|
1155
|
+
connectionId: connection._id,
|
|
1156
|
+
checks
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
},
|
|
1160
|
+
scim,
|
|
1161
|
+
audit: {
|
|
1162
|
+
record: async (ctx, data) => {
|
|
1163
|
+
return await recordGroupAuditEvent(ctx, data);
|
|
1164
|
+
},
|
|
1165
|
+
list: async (ctx, data) => {
|
|
1166
|
+
return await listAuditEvents(ctx, config.component.public, data);
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
webhook
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
//#endregion
|
|
1174
|
+
export { createGroupConnectionDomain };
|
|
1175
|
+
//# sourceMappingURL=domain.js.map
|