@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
package/src/client/index.ts
DELETED
|
@@ -1,1124 +0,0 @@
|
|
|
1
|
-
import { Fx } from "@robelest/fx";
|
|
2
|
-
import { ConvexHttpClient } from "convex/browser";
|
|
3
|
-
import { ConvexError, Value } from "convex/values";
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
AuthApiRefs,
|
|
7
|
-
AuthClient,
|
|
8
|
-
AuthFlowContext,
|
|
9
|
-
AuthHandshakeErrorCode,
|
|
10
|
-
AuthSession,
|
|
11
|
-
AuthState,
|
|
12
|
-
ClientOptions,
|
|
13
|
-
ConvexTransport,
|
|
14
|
-
DeviceClient,
|
|
15
|
-
DeviceCodeResult,
|
|
16
|
-
HandshakeWaiter,
|
|
17
|
-
PasskeyClient,
|
|
18
|
-
PendingInvite,
|
|
19
|
-
SignInActionResult,
|
|
20
|
-
SignInResult,
|
|
21
|
-
Storage,
|
|
22
|
-
TotpClient,
|
|
23
|
-
} from "./core/types";
|
|
24
|
-
import { createDeviceClient } from "./factors/device";
|
|
25
|
-
import { createPasskeyClient } from "./factors/passkey";
|
|
26
|
-
import { createTotpClient } from "./factors/totp";
|
|
27
|
-
import { browserMutex, getStorageListenerRegistry } from "./runtime/browser";
|
|
28
|
-
import { createInviteManager } from "./runtime/invite";
|
|
29
|
-
import {
|
|
30
|
-
createProxyHelpers,
|
|
31
|
-
isRetriableProxyRefreshError,
|
|
32
|
-
isTransientNetworkError,
|
|
33
|
-
} from "./runtime/proxy";
|
|
34
|
-
import { createStorageHelpers } from "./runtime/storage";
|
|
35
|
-
|
|
36
|
-
export type {
|
|
37
|
-
AuthApiRefs,
|
|
38
|
-
AuthClient,
|
|
39
|
-
AuthState,
|
|
40
|
-
ClientOptions,
|
|
41
|
-
DeviceClient,
|
|
42
|
-
DeviceCodeResult,
|
|
43
|
-
PasskeyClient,
|
|
44
|
-
PendingInvite,
|
|
45
|
-
SignInResult,
|
|
46
|
-
Storage,
|
|
47
|
-
TotpClient,
|
|
48
|
-
} from "./core/types";
|
|
49
|
-
|
|
50
|
-
const VERIFIER_STORAGE_KEY = "__convexAuthOAuthVerifier";
|
|
51
|
-
const JWT_STORAGE_KEY = "__convexAuthJWT";
|
|
52
|
-
const REFRESH_TOKEN_STORAGE_KEY = "__convexAuthRefreshToken";
|
|
53
|
-
const INVITE_TOKEN_KEY = "__convexAuthPendingInvite";
|
|
54
|
-
const INVITE_EMAIL_KEY = "__convexAuthPendingInviteEmail";
|
|
55
|
-
|
|
56
|
-
const RETRY_BASE_MS = 500;
|
|
57
|
-
const RETRY_MAX_RETRIES = 2;
|
|
58
|
-
const AUTH_HANDSHAKE_TIMEOUT_MS = 5000;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Resolve the Convex deployment URL from the client.
|
|
62
|
-
*
|
|
63
|
-
* `ConvexReactClient` exposes `.url` directly.
|
|
64
|
-
* `ConvexClient` exposes `.client.url` via `BaseConvexClient`.
|
|
65
|
-
*/
|
|
66
|
-
function resolveUrl(convex: ConvexTransport, explicit?: string): string {
|
|
67
|
-
if (explicit) return explicit;
|
|
68
|
-
const c = convex as any;
|
|
69
|
-
const url: unknown = c.url ?? c.client?.url;
|
|
70
|
-
if (typeof url === "string") return url;
|
|
71
|
-
throw new Error(
|
|
72
|
-
"Could not determine Convex deployment URL. Pass `url` explicitly.",
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Create a framework-agnostic auth client.
|
|
78
|
-
*
|
|
79
|
-
* Returns an object with `signIn`, `signOut`, `onChange`, `state`,
|
|
80
|
-
* `passkey`, and `totp` — everything needed for client-side auth.
|
|
81
|
-
*
|
|
82
|
-
* ### SPA mode (default)
|
|
83
|
-
*
|
|
84
|
-
* ```ts
|
|
85
|
-
* import { ConvexClient } from 'convex/browser';
|
|
86
|
-
* import { client } from '@robelest/convex-auth/client';
|
|
87
|
-
* import { api } from '../convex/_generated/api';
|
|
88
|
-
*
|
|
89
|
-
* const convex = new ConvexClient(CONVEX_URL);
|
|
90
|
-
* const auth = client({ convex, api: api.auth });
|
|
91
|
-
* ```
|
|
92
|
-
*
|
|
93
|
-
* ### SSR / proxy mode
|
|
94
|
-
*
|
|
95
|
-
* ```ts
|
|
96
|
-
* const auth = client({
|
|
97
|
-
* convex,
|
|
98
|
-
* proxyPath: '/api/auth',
|
|
99
|
-
* tokenSeed: tokenFromServer, // JWT read from httpOnly cookie during SSR
|
|
100
|
-
* });
|
|
101
|
-
* ```
|
|
102
|
-
*
|
|
103
|
-
* In proxy mode all auth operations go through the proxy URL.
|
|
104
|
-
* Tokens are stored in httpOnly cookies server-side — the client
|
|
105
|
-
* holds the JWT in memory only.
|
|
106
|
-
*
|
|
107
|
-
* @param options - Client configuration. See {@link ClientOptions}.
|
|
108
|
-
* @typeParam Api - An AuthApiRefs type determining which factor helpers are available.
|
|
109
|
-
* @returns Auth client with conditional `passkey`, `totp`, and `device` helpers.
|
|
110
|
-
* @throws {Error} When the Convex deployment URL cannot be determined and `url` is not passed explicitly.
|
|
111
|
-
* @throws {Error} When `proxyPath` is not set and the `api` option is missing.
|
|
112
|
-
*/
|
|
113
|
-
export function client<
|
|
114
|
-
Api extends AuthApiRefs<boolean, boolean, boolean> = AuthApiRefs,
|
|
115
|
-
>(options: ClientOptions<Api>): AuthClient<Api> {
|
|
116
|
-
const { convex, proxyPath, api: apiRefs } = options;
|
|
117
|
-
const proxy = proxyPath;
|
|
118
|
-
|
|
119
|
-
function requireApiRefs() {
|
|
120
|
-
if (!apiRefs) {
|
|
121
|
-
throw new Error(
|
|
122
|
-
"The `api` option is required when `proxyPath` is not set. " +
|
|
123
|
-
"Pass { api: api.auth }.",
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
return apiRefs;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// In proxy mode, default storage to null (cookies handle persistence).
|
|
130
|
-
const storage =
|
|
131
|
-
options.storage !== undefined
|
|
132
|
-
? options.storage
|
|
133
|
-
: proxy
|
|
134
|
-
? null
|
|
135
|
-
: typeof window === "undefined"
|
|
136
|
-
? null
|
|
137
|
-
: window.localStorage;
|
|
138
|
-
|
|
139
|
-
const replaceUrl =
|
|
140
|
-
options.replaceUrl ??
|
|
141
|
-
((url: string) => {
|
|
142
|
-
if (typeof window !== "undefined") {
|
|
143
|
-
window.history.replaceState({}, "", url);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// ---------------------------------------------------------------------------
|
|
148
|
-
// Location — SSR-safe URL reading
|
|
149
|
-
// ---------------------------------------------------------------------------
|
|
150
|
-
|
|
151
|
-
function getLocation(): URL | null {
|
|
152
|
-
if (typeof options.location === "function") return options.location();
|
|
153
|
-
if (options.location instanceof URL) return options.location;
|
|
154
|
-
if (typeof window !== "undefined") return new URL(window.location.href);
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* SSR-safe URL parameter reader.
|
|
160
|
-
*
|
|
161
|
-
* Uses the `location` option if provided, otherwise falls back to
|
|
162
|
-
* `window.location` (returns `null` during SSR where `window` is unavailable).
|
|
163
|
-
*
|
|
164
|
-
* @param name - The query parameter name.
|
|
165
|
-
* @returns The parameter value, or `null` if not present or in SSR.
|
|
166
|
-
*
|
|
167
|
-
* @example
|
|
168
|
-
* ```ts
|
|
169
|
-
* const workspaceId = auth.param("workspace");
|
|
170
|
-
* const tab = auth.param("tab") ?? "issues";
|
|
171
|
-
* ```
|
|
172
|
-
*/
|
|
173
|
-
function param(name: string): string | null {
|
|
174
|
-
const loc = getLocation();
|
|
175
|
-
return loc?.searchParams.get(name) ?? null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function cleanUrlParams(params: string[]) {
|
|
179
|
-
const loc = getLocation();
|
|
180
|
-
if (!loc) return;
|
|
181
|
-
const searchParams = new URLSearchParams(loc.search);
|
|
182
|
-
let changed = false;
|
|
183
|
-
for (const p of params) {
|
|
184
|
-
if (searchParams.has(p)) {
|
|
185
|
-
searchParams.delete(p);
|
|
186
|
-
changed = true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (changed) {
|
|
190
|
-
const next = searchParams.toString()
|
|
191
|
-
? `${loc.pathname}?${searchParams}`
|
|
192
|
-
: loc.pathname;
|
|
193
|
-
void replaceUrl(next);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const url = proxy ? undefined : resolveUrl(convex, options.url);
|
|
198
|
-
const escapedNamespace = proxy
|
|
199
|
-
? proxy.replace(/[^a-zA-Z0-9]/g, "")
|
|
200
|
-
: url!.replace(/[^a-zA-Z0-9]/g, "");
|
|
201
|
-
const key = (name: string) => `${name}_${escapedNamespace}`;
|
|
202
|
-
const {
|
|
203
|
-
get: storageGet,
|
|
204
|
-
set: storageSet,
|
|
205
|
-
remove: storageRemove,
|
|
206
|
-
} = createStorageHelpers({ storage, key });
|
|
207
|
-
const { isAbsoluteUrl, proxyFetch, resolveProxyUrl } = createProxyHelpers({
|
|
208
|
-
proxy,
|
|
209
|
-
});
|
|
210
|
-
const subscribers = new Set<() => void>();
|
|
211
|
-
let disposeStorageListener: (() => void) | null = null;
|
|
212
|
-
|
|
213
|
-
// Unauthenticated HTTP client for code verification & OAuth exchange.
|
|
214
|
-
// Only needed in SPA mode — proxy mode routes everything through the proxy.
|
|
215
|
-
const httpClient = proxy ? null : new ConvexHttpClient(url!);
|
|
216
|
-
|
|
217
|
-
// ---------------------------------------------------------------------------
|
|
218
|
-
// State
|
|
219
|
-
// ---------------------------------------------------------------------------
|
|
220
|
-
|
|
221
|
-
// If a server-provided token was supplied (SSR hydration), treat it as
|
|
222
|
-
// immediately authenticated to avoid a handshake-only loading screen.
|
|
223
|
-
const serverToken =
|
|
224
|
-
typeof options.tokenSeed === "string" && options.tokenSeed.trim().length > 0
|
|
225
|
-
? options.tokenSeed
|
|
226
|
-
: null;
|
|
227
|
-
const hasServerToken = serverToken !== null;
|
|
228
|
-
|
|
229
|
-
let token: string | null = serverToken;
|
|
230
|
-
let isLoading = !hasServerToken;
|
|
231
|
-
let authConfirmed = hasServerToken;
|
|
232
|
-
let handshakePending = false;
|
|
233
|
-
let authEpoch = 0;
|
|
234
|
-
let destroyed = false;
|
|
235
|
-
const handshakeWaiters = new Set<HandshakeWaiter>();
|
|
236
|
-
let snapshot: AuthState = {
|
|
237
|
-
phase: hasServerToken
|
|
238
|
-
? "authenticated"
|
|
239
|
-
: isLoading
|
|
240
|
-
? "loading"
|
|
241
|
-
: "unauthenticated",
|
|
242
|
-
isLoading,
|
|
243
|
-
isAuthenticated: hasServerToken,
|
|
244
|
-
token,
|
|
245
|
-
};
|
|
246
|
-
let handlingCodeFlow = false;
|
|
247
|
-
|
|
248
|
-
const HANDSHAKE_ERROR_MESSAGES: Record<AuthHandshakeErrorCode, string> = {
|
|
249
|
-
AUTH_HANDSHAKE_TIMEOUT:
|
|
250
|
-
"Sign-in succeeded but authentication confirmation timed out.",
|
|
251
|
-
AUTH_HANDSHAKE_REJECTED:
|
|
252
|
-
"Authentication was rejected while confirming the session.",
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const createHandshakeError = (
|
|
256
|
-
code: AuthHandshakeErrorCode,
|
|
257
|
-
context: Record<string, unknown>,
|
|
258
|
-
) => {
|
|
259
|
-
return new ConvexError({
|
|
260
|
-
code,
|
|
261
|
-
message: HANDSHAKE_ERROR_MESSAGES[code],
|
|
262
|
-
...context,
|
|
263
|
-
} as Value);
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
const settleHandshakeWaiters = (
|
|
267
|
-
epoch: number,
|
|
268
|
-
outcome:
|
|
269
|
-
| { type: "resolve" }
|
|
270
|
-
| { type: "reject"; error: ConvexError<Value> },
|
|
271
|
-
) => {
|
|
272
|
-
for (const waiter of Array.from(handshakeWaiters)) {
|
|
273
|
-
if (waiter.epoch !== epoch) {
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
clearTimeout(waiter.timeoutId);
|
|
277
|
-
handshakeWaiters.delete(waiter);
|
|
278
|
-
if (outcome.type === "resolve") {
|
|
279
|
-
waiter.resolve();
|
|
280
|
-
} else {
|
|
281
|
-
waiter.reject(outcome.error);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const rejectObsoleteHandshakeWaiters = (activeEpoch: number) => {
|
|
287
|
-
for (const waiter of Array.from(handshakeWaiters)) {
|
|
288
|
-
if (waiter.epoch >= activeEpoch) {
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
clearTimeout(waiter.timeoutId);
|
|
292
|
-
handshakeWaiters.delete(waiter);
|
|
293
|
-
waiter.reject(
|
|
294
|
-
createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
|
|
295
|
-
...waiter.context,
|
|
296
|
-
reason: "token_changed",
|
|
297
|
-
}),
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const waitForAuthHandshake = async (context: AuthFlowContext) => {
|
|
303
|
-
if (token === null) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
if (authConfirmed && !handshakePending) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (!handshakePending) {
|
|
310
|
-
throw createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
|
|
311
|
-
...context,
|
|
312
|
-
reason: "auth_rejected",
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const epoch = authEpoch;
|
|
317
|
-
await new Promise<void>((resolve, reject) => {
|
|
318
|
-
const waiterRef: { current: HandshakeWaiter | null } = { current: null };
|
|
319
|
-
const timeoutId = setTimeout(() => {
|
|
320
|
-
if (waiterRef.current !== null) {
|
|
321
|
-
handshakeWaiters.delete(waiterRef.current);
|
|
322
|
-
}
|
|
323
|
-
reject(
|
|
324
|
-
createHandshakeError("AUTH_HANDSHAKE_TIMEOUT", {
|
|
325
|
-
...context,
|
|
326
|
-
timeoutMs: AUTH_HANDSHAKE_TIMEOUT_MS,
|
|
327
|
-
}),
|
|
328
|
-
);
|
|
329
|
-
}, AUTH_HANDSHAKE_TIMEOUT_MS);
|
|
330
|
-
|
|
331
|
-
const waiter: HandshakeWaiter = {
|
|
332
|
-
epoch,
|
|
333
|
-
context,
|
|
334
|
-
resolve,
|
|
335
|
-
reject,
|
|
336
|
-
timeoutId,
|
|
337
|
-
};
|
|
338
|
-
waiterRef.current = waiter;
|
|
339
|
-
handshakeWaiters.add(waiter);
|
|
340
|
-
});
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
const handleConvexAuthChange = (isAuthenticated: boolean) => {
|
|
344
|
-
if (destroyed) {
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (isAuthenticated) {
|
|
349
|
-
authConfirmed = true;
|
|
350
|
-
handshakePending = false;
|
|
351
|
-
settleHandshakeWaiters(authEpoch, { type: "resolve" });
|
|
352
|
-
} else {
|
|
353
|
-
authConfirmed = false;
|
|
354
|
-
// Do not reject immediately while a handshake is pending.
|
|
355
|
-
// Convex can transiently emit `false` while reauth is still in flight,
|
|
356
|
-
// and a subsequent `true` confirms the same session.
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (updateSnapshot()) {
|
|
360
|
-
notify();
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
const notify = () => {
|
|
365
|
-
for (const cb of subscribers) cb();
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
const updateSnapshot = () => {
|
|
369
|
-
const phaseDispatch = {
|
|
370
|
-
tag:
|
|
371
|
-
token !== null && handshakePending
|
|
372
|
-
? "handshake"
|
|
373
|
-
: isLoading
|
|
374
|
-
? "loading"
|
|
375
|
-
: token !== null && authConfirmed
|
|
376
|
-
? "authenticated"
|
|
377
|
-
: "unauthenticated",
|
|
378
|
-
} as const;
|
|
379
|
-
|
|
380
|
-
const phase = {
|
|
381
|
-
handshake: "handshake",
|
|
382
|
-
loading: "loading",
|
|
383
|
-
authenticated: "authenticated",
|
|
384
|
-
unauthenticated: "unauthenticated",
|
|
385
|
-
}[phaseDispatch.tag] as AuthState["phase"];
|
|
386
|
-
|
|
387
|
-
const next: AuthState = {
|
|
388
|
-
phase,
|
|
389
|
-
isLoading: phase === "loading" || phase === "handshake",
|
|
390
|
-
isAuthenticated: phase === "authenticated",
|
|
391
|
-
token,
|
|
392
|
-
};
|
|
393
|
-
if (
|
|
394
|
-
snapshot.phase === next.phase &&
|
|
395
|
-
snapshot.isLoading === next.isLoading &&
|
|
396
|
-
snapshot.isAuthenticated === next.isAuthenticated &&
|
|
397
|
-
snapshot.token === next.token
|
|
398
|
-
) {
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
snapshot = next;
|
|
402
|
-
return true;
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
const finalizeLoadingState = () => {
|
|
406
|
-
if (!isLoading) {
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
isLoading = false;
|
|
410
|
-
if (updateSnapshot()) {
|
|
411
|
-
notify();
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
const inviteManager = createInviteManager({
|
|
416
|
-
param,
|
|
417
|
-
storageGet,
|
|
418
|
-
storageSet,
|
|
419
|
-
storageRemove,
|
|
420
|
-
cleanUrlParams,
|
|
421
|
-
tokenKey: INVITE_TOKEN_KEY,
|
|
422
|
-
emailKey: INVITE_EMAIL_KEY,
|
|
423
|
-
});
|
|
424
|
-
const getPendingInvite = () => inviteManager.getPendingInvite();
|
|
425
|
-
const persistInvite = () => inviteManager.persistInvite();
|
|
426
|
-
const acceptInvite = () => inviteManager.acceptInvite();
|
|
427
|
-
|
|
428
|
-
// ---------------------------------------------------------------------------
|
|
429
|
-
// Token management
|
|
430
|
-
// ---------------------------------------------------------------------------
|
|
431
|
-
|
|
432
|
-
const bindConvexAuth = () => {
|
|
433
|
-
convex.setAuth(fetchAccessToken, handleConvexAuthChange);
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
const setToken = async (
|
|
437
|
-
args:
|
|
438
|
-
| {
|
|
439
|
-
shouldStore: true;
|
|
440
|
-
tokens: AuthSession | null;
|
|
441
|
-
requireHandshake?: boolean;
|
|
442
|
-
resyncConvexAuth?: boolean;
|
|
443
|
-
}
|
|
444
|
-
| {
|
|
445
|
-
shouldStore: false;
|
|
446
|
-
tokens: { token: string } | null;
|
|
447
|
-
requireHandshake?: boolean;
|
|
448
|
-
resyncConvexAuth?: boolean;
|
|
449
|
-
},
|
|
450
|
-
) => {
|
|
451
|
-
const previousToken = token;
|
|
452
|
-
|
|
453
|
-
if (args.tokens === null) {
|
|
454
|
-
token = null;
|
|
455
|
-
if (args.shouldStore) {
|
|
456
|
-
await storageRemove(JWT_STORAGE_KEY);
|
|
457
|
-
await storageRemove(REFRESH_TOKEN_STORAGE_KEY);
|
|
458
|
-
}
|
|
459
|
-
} else {
|
|
460
|
-
token = args.tokens.token;
|
|
461
|
-
if (args.shouldStore && "refreshToken" in args.tokens) {
|
|
462
|
-
await storageSet(JWT_STORAGE_KEY, args.tokens.token);
|
|
463
|
-
await storageSet(REFRESH_TOKEN_STORAGE_KEY, args.tokens.refreshToken);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (token !== previousToken) {
|
|
468
|
-
authEpoch += 1;
|
|
469
|
-
rejectObsoleteHandshakeWaiters(authEpoch);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (token === null) {
|
|
473
|
-
authConfirmed = false;
|
|
474
|
-
handshakePending = false;
|
|
475
|
-
settleHandshakeWaiters(authEpoch, {
|
|
476
|
-
type: "reject",
|
|
477
|
-
error: createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
|
|
478
|
-
reason: "token_cleared",
|
|
479
|
-
}),
|
|
480
|
-
});
|
|
481
|
-
} else {
|
|
482
|
-
const shouldEnterHandshake =
|
|
483
|
-
args.requireHandshake === true || !authConfirmed;
|
|
484
|
-
if (shouldEnterHandshake) {
|
|
485
|
-
authConfirmed = false;
|
|
486
|
-
handshakePending = true;
|
|
487
|
-
} else {
|
|
488
|
-
handshakePending = false;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const hadPendingLoad = isLoading;
|
|
493
|
-
isLoading = false;
|
|
494
|
-
const changed = updateSnapshot();
|
|
495
|
-
if (args.resyncConvexAuth !== false) {
|
|
496
|
-
bindConvexAuth();
|
|
497
|
-
}
|
|
498
|
-
if (hadPendingLoad || changed) {
|
|
499
|
-
notify();
|
|
500
|
-
}
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
const setTokenAndMaybeWait = async (
|
|
504
|
-
args:
|
|
505
|
-
| {
|
|
506
|
-
shouldStore: true;
|
|
507
|
-
tokens: AuthSession | null;
|
|
508
|
-
waitForHandshake: boolean;
|
|
509
|
-
context: AuthFlowContext;
|
|
510
|
-
}
|
|
511
|
-
| {
|
|
512
|
-
shouldStore: false;
|
|
513
|
-
tokens: { token: string } | null;
|
|
514
|
-
waitForHandshake: boolean;
|
|
515
|
-
context: AuthFlowContext;
|
|
516
|
-
},
|
|
517
|
-
): Promise<boolean> => {
|
|
518
|
-
const { waitForHandshake, context, ...tokenArgs } = args;
|
|
519
|
-
await setToken({
|
|
520
|
-
...(tokenArgs as
|
|
521
|
-
| { shouldStore: true; tokens: AuthSession | null }
|
|
522
|
-
| { shouldStore: false; tokens: { token: string } | null }),
|
|
523
|
-
requireHandshake: waitForHandshake,
|
|
524
|
-
});
|
|
525
|
-
if (tokenArgs.tokens === null) {
|
|
526
|
-
return false;
|
|
527
|
-
}
|
|
528
|
-
if (waitForHandshake) {
|
|
529
|
-
await waitForAuthHandshake(context);
|
|
530
|
-
}
|
|
531
|
-
return true;
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
// ---------------------------------------------------------------------------
|
|
535
|
-
// Code verification with retries (SPA mode only)
|
|
536
|
-
// ---------------------------------------------------------------------------
|
|
537
|
-
|
|
538
|
-
const verifyCode = async (
|
|
539
|
-
args: { code: string; verifier?: string } | { refreshToken: string },
|
|
540
|
-
) => {
|
|
541
|
-
const verifyCodeRetryPolicy = Fx.retry.while(
|
|
542
|
-
Fx.retry.compose(
|
|
543
|
-
Fx.retry.jittered(Fx.retry.exponential(RETRY_BASE_MS)),
|
|
544
|
-
Fx.retry.recurs(RETRY_MAX_RETRIES),
|
|
545
|
-
),
|
|
546
|
-
(meta) => isTransientNetworkError(meta.input),
|
|
547
|
-
);
|
|
548
|
-
|
|
549
|
-
return Fx.run(
|
|
550
|
-
Fx.from({
|
|
551
|
-
ok: () =>
|
|
552
|
-
httpClient!.action(
|
|
553
|
-
requireApiRefs().signIn,
|
|
554
|
-
"code" in args
|
|
555
|
-
? { params: { code: args.code }, verifier: args.verifier }
|
|
556
|
-
: args,
|
|
557
|
-
),
|
|
558
|
-
err: (e) => e,
|
|
559
|
-
}).pipe(
|
|
560
|
-
Fx.retry(verifyCodeRetryPolicy),
|
|
561
|
-
Fx.recover((e) => Fx.fatal(e)),
|
|
562
|
-
),
|
|
563
|
-
);
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
const verifyCodeAndSetToken = async (
|
|
567
|
-
args: { code: string; verifier?: string } | { refreshToken: string },
|
|
568
|
-
opts?: { resyncConvexAuth?: boolean },
|
|
569
|
-
) => {
|
|
570
|
-
const { tokens } = await verifyCode(args);
|
|
571
|
-
await setToken({
|
|
572
|
-
shouldStore: true,
|
|
573
|
-
tokens: (tokens as AuthSession | null) ?? null,
|
|
574
|
-
resyncConvexAuth: opts?.resyncConvexAuth,
|
|
575
|
-
});
|
|
576
|
-
return tokens !== null;
|
|
577
|
-
};
|
|
578
|
-
|
|
579
|
-
const normalizeDeviceCodeResult = (device_code: any): DeviceCodeResult => {
|
|
580
|
-
return {
|
|
581
|
-
deviceCode: device_code.deviceCode,
|
|
582
|
-
userCode: device_code.userCode,
|
|
583
|
-
verificationUri:
|
|
584
|
-
device_code.verification_uri ?? device_code.verificationUri,
|
|
585
|
-
verificationUriComplete:
|
|
586
|
-
device_code.verification_uri_complete ??
|
|
587
|
-
device_code.verificationUriComplete,
|
|
588
|
-
expiresIn: device_code.expiresIn,
|
|
589
|
-
interval: device_code.interval,
|
|
590
|
-
};
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
// ---------------------------------------------------------------------------
|
|
594
|
-
// signIn
|
|
595
|
-
// ---------------------------------------------------------------------------
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Sign in with a provider.
|
|
599
|
-
*
|
|
600
|
-
* @param provider - Provider ID (e.g. `"email"`, `"password"`, `"google"`).
|
|
601
|
-
* Omit when exchanging an OAuth code (the code carries the provider info).
|
|
602
|
-
* @param args - Provider-specific arguments. Pass a `Record<string, Value>`
|
|
603
|
-
* or `FormData`. Common fields: `email`, `password`, `code`, `redirectTo`.
|
|
604
|
-
* @returns A {@link SignInResult} indicating the outcome.
|
|
605
|
-
* @throws {ConvexError} When the server action rejects the sign-in attempt (e.g. invalid credentials, provider error, or rate limiting).
|
|
606
|
-
*
|
|
607
|
-
* @example Email magic link
|
|
608
|
-
* ```ts
|
|
609
|
-
* await auth.signIn('email', { email: 'user@example.com' });
|
|
610
|
-
* ```
|
|
611
|
-
*
|
|
612
|
-
* @example Password
|
|
613
|
-
* ```ts
|
|
614
|
-
* const result = await auth.signIn('password', { email, password, flow: 'signIn' });
|
|
615
|
-
* if (result.kind === 'totpRequired') {
|
|
616
|
-
* await auth.totp.verify({ code: totpCode, verifier: result.verifier });
|
|
617
|
-
* }
|
|
618
|
-
* ```
|
|
619
|
-
*
|
|
620
|
-
* @example OAuth (triggers redirect)
|
|
621
|
-
* ```ts
|
|
622
|
-
* await auth.signIn('google'); // redirects to Google
|
|
623
|
-
* ```
|
|
624
|
-
*/
|
|
625
|
-
const signIn = async (
|
|
626
|
-
provider?: string,
|
|
627
|
-
args?: FormData | Record<string, Value>,
|
|
628
|
-
): Promise<SignInResult> => {
|
|
629
|
-
// Persist invite before potential OAuth redirect
|
|
630
|
-
await persistInvite();
|
|
631
|
-
|
|
632
|
-
const params =
|
|
633
|
-
args instanceof FormData
|
|
634
|
-
? (() => {
|
|
635
|
-
const formParams: Record<string, Value> = {};
|
|
636
|
-
args.forEach((value, key) => {
|
|
637
|
-
formParams[key] = typeof value === "string" ? value : value.name;
|
|
638
|
-
});
|
|
639
|
-
return formParams;
|
|
640
|
-
})()
|
|
641
|
-
: (args ?? {});
|
|
642
|
-
const flow =
|
|
643
|
-
typeof params.flow === "string" && params.flow.length > 0
|
|
644
|
-
? params.flow
|
|
645
|
-
: "signIn";
|
|
646
|
-
|
|
647
|
-
const handleSignInActionResult = async (
|
|
648
|
-
result: SignInActionResult,
|
|
649
|
-
options: { shouldStore: boolean; persistVerifier: boolean },
|
|
650
|
-
): Promise<SignInResult> =>
|
|
651
|
-
Fx.run(
|
|
652
|
-
Fx.match(result, result.kind, {
|
|
653
|
-
redirect: (redirectResult) =>
|
|
654
|
-
Fx.promise(async () => {
|
|
655
|
-
const redirectUrl = new URL(redirectResult.redirect);
|
|
656
|
-
if (options.persistVerifier) {
|
|
657
|
-
await storageSet(VERIFIER_STORAGE_KEY, redirectResult.verifier);
|
|
658
|
-
}
|
|
659
|
-
if (typeof window !== "undefined") {
|
|
660
|
-
window.location.href = redirectUrl.toString();
|
|
661
|
-
}
|
|
662
|
-
return {
|
|
663
|
-
kind: "redirect" as const,
|
|
664
|
-
redirect: redirectUrl,
|
|
665
|
-
verifier: redirectResult.verifier,
|
|
666
|
-
};
|
|
667
|
-
}),
|
|
668
|
-
totpRequired: (totpRequiredResult) =>
|
|
669
|
-
Fx.succeed({
|
|
670
|
-
kind: "totpRequired" as const,
|
|
671
|
-
verifier: totpRequiredResult.verifier,
|
|
672
|
-
}),
|
|
673
|
-
deviceCode: (deviceCodeResult) =>
|
|
674
|
-
Fx.succeed({
|
|
675
|
-
kind: "deviceCode" as const,
|
|
676
|
-
deviceCode: normalizeDeviceCodeResult(
|
|
677
|
-
deviceCodeResult.deviceCode,
|
|
678
|
-
),
|
|
679
|
-
}),
|
|
680
|
-
signedIn: (signedInResult) =>
|
|
681
|
-
Fx.promise(async () => {
|
|
682
|
-
const signingIn = await setTokenAndMaybeWait(
|
|
683
|
-
options.shouldStore
|
|
684
|
-
? {
|
|
685
|
-
shouldStore: true as const,
|
|
686
|
-
tokens: signedInResult.tokens,
|
|
687
|
-
waitForHandshake: true,
|
|
688
|
-
context: { provider, flow },
|
|
689
|
-
}
|
|
690
|
-
: {
|
|
691
|
-
shouldStore: false as const,
|
|
692
|
-
tokens:
|
|
693
|
-
signedInResult.tokens === null
|
|
694
|
-
? null
|
|
695
|
-
: { token: signedInResult.tokens.token },
|
|
696
|
-
waitForHandshake: true,
|
|
697
|
-
context: { provider, flow },
|
|
698
|
-
},
|
|
699
|
-
);
|
|
700
|
-
return signingIn
|
|
701
|
-
? ({ kind: "signedIn" as const } as SignInResult)
|
|
702
|
-
: ({ kind: "started" as const } as SignInResult);
|
|
703
|
-
}),
|
|
704
|
-
started: (_startedResult) => Fx.succeed({ kind: "started" as const }),
|
|
705
|
-
passkeyOptions: (_passkeyOptionsResult) =>
|
|
706
|
-
Fx.succeed({ kind: "started" as const }),
|
|
707
|
-
totpSetup: (_totpSetupResult) =>
|
|
708
|
-
Fx.succeed({ kind: "started" as const }),
|
|
709
|
-
}),
|
|
710
|
-
);
|
|
711
|
-
|
|
712
|
-
if (proxy) {
|
|
713
|
-
const result = (await proxyFetch({
|
|
714
|
-
action: "auth:signIn",
|
|
715
|
-
args: { provider, params },
|
|
716
|
-
})) as SignInActionResult;
|
|
717
|
-
return handleSignInActionResult(result, {
|
|
718
|
-
shouldStore: false,
|
|
719
|
-
persistVerifier: false,
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// SPA mode: call Convex directly.
|
|
724
|
-
const verifier = (await storageGet(VERIFIER_STORAGE_KEY)) ?? undefined;
|
|
725
|
-
await storageRemove(VERIFIER_STORAGE_KEY);
|
|
726
|
-
const result = (await convex.action(requireApiRefs().signIn, {
|
|
727
|
-
provider,
|
|
728
|
-
params,
|
|
729
|
-
verifier,
|
|
730
|
-
})) as SignInActionResult;
|
|
731
|
-
return handleSignInActionResult(result, {
|
|
732
|
-
shouldStore: true,
|
|
733
|
-
persistVerifier: true,
|
|
734
|
-
});
|
|
735
|
-
};
|
|
736
|
-
|
|
737
|
-
// ---------------------------------------------------------------------------
|
|
738
|
-
// signOut
|
|
739
|
-
// ---------------------------------------------------------------------------
|
|
740
|
-
|
|
741
|
-
/**
|
|
742
|
-
* Sign out the current user.
|
|
743
|
-
*
|
|
744
|
-
* Invalidates the server session and clears local token state.
|
|
745
|
-
* Errors are silently caught — calling `signOut` on an already
|
|
746
|
-
* signed-out user is a no-op.
|
|
747
|
-
*/
|
|
748
|
-
const signOut = async () => {
|
|
749
|
-
if (proxy) {
|
|
750
|
-
await Fx.run(
|
|
751
|
-
Fx.from({
|
|
752
|
-
ok: () => proxyFetch({ action: "auth:signOut", args: {} }),
|
|
753
|
-
err: () => undefined,
|
|
754
|
-
}).pipe(Fx.recover(() => Fx.succeed(undefined))),
|
|
755
|
-
);
|
|
756
|
-
await setToken({ shouldStore: false, tokens: null });
|
|
757
|
-
if (convex.clearAuth) convex.clearAuth();
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// SPA mode.
|
|
762
|
-
await Fx.run(
|
|
763
|
-
Fx.from({
|
|
764
|
-
ok: () => convex.action(requireApiRefs().signOut, {}),
|
|
765
|
-
err: () => undefined,
|
|
766
|
-
}).pipe(Fx.recover(() => Fx.succeed(undefined))),
|
|
767
|
-
);
|
|
768
|
-
await setToken({ shouldStore: true, tokens: null });
|
|
769
|
-
if (convex.clearAuth) convex.clearAuth();
|
|
770
|
-
};
|
|
771
|
-
|
|
772
|
-
// ---------------------------------------------------------------------------
|
|
773
|
-
// fetchAccessToken — called by convex.setAuth()
|
|
774
|
-
// ---------------------------------------------------------------------------
|
|
775
|
-
|
|
776
|
-
const fetchAccessToken = async ({
|
|
777
|
-
forceRefreshToken,
|
|
778
|
-
}: {
|
|
779
|
-
forceRefreshToken: boolean;
|
|
780
|
-
}): Promise<string | null> => {
|
|
781
|
-
if (!forceRefreshToken) return token;
|
|
782
|
-
|
|
783
|
-
if (proxy) {
|
|
784
|
-
// Proxy mode: POST to the proxy to refresh.
|
|
785
|
-
// The proxy reads the real refresh token from the httpOnly cookie.
|
|
786
|
-
const resolvedProxyUrl = await resolveProxyUrl();
|
|
787
|
-
if (
|
|
788
|
-
typeof window === "undefined" &&
|
|
789
|
-
!(await isAbsoluteUrl(resolvedProxyUrl))
|
|
790
|
-
) {
|
|
791
|
-
finalizeLoadingState();
|
|
792
|
-
return token;
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
const tokenBeforeRefresh = token;
|
|
796
|
-
return await browserMutex("__convexAuthProxyRefresh", async () => {
|
|
797
|
-
// Another tab/call may have already refreshed.
|
|
798
|
-
if (token !== tokenBeforeRefresh) return token;
|
|
799
|
-
|
|
800
|
-
const proxyRefreshRetryPolicy = Fx.retry.while(
|
|
801
|
-
Fx.retry.compose(
|
|
802
|
-
Fx.retry.jittered(Fx.retry.exponential(RETRY_BASE_MS)),
|
|
803
|
-
Fx.retry.recurs(RETRY_MAX_RETRIES),
|
|
804
|
-
),
|
|
805
|
-
(meta) => isRetriableProxyRefreshError(meta.input),
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
await Fx.run(
|
|
809
|
-
Fx.from({
|
|
810
|
-
ok: () =>
|
|
811
|
-
proxyFetch({
|
|
812
|
-
action: "auth:signIn",
|
|
813
|
-
args: { refreshToken: true },
|
|
814
|
-
}),
|
|
815
|
-
err: (e) => e,
|
|
816
|
-
}).pipe(
|
|
817
|
-
Fx.retry(proxyRefreshRetryPolicy),
|
|
818
|
-
Fx.chain((result: any) =>
|
|
819
|
-
Fx.from({
|
|
820
|
-
ok: async () => {
|
|
821
|
-
if (result.tokens) {
|
|
822
|
-
await setToken({
|
|
823
|
-
shouldStore: false,
|
|
824
|
-
tokens: { token: result.tokens.token },
|
|
825
|
-
resyncConvexAuth: false,
|
|
826
|
-
});
|
|
827
|
-
} else {
|
|
828
|
-
await setToken({
|
|
829
|
-
shouldStore: false,
|
|
830
|
-
tokens: null,
|
|
831
|
-
resyncConvexAuth: false,
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
},
|
|
835
|
-
err: (e) => e,
|
|
836
|
-
}),
|
|
837
|
-
),
|
|
838
|
-
Fx.inspect((error) =>
|
|
839
|
-
Fx.sync(() =>
|
|
840
|
-
console.error("[convex-auth] Proxy refresh failed:", error),
|
|
841
|
-
),
|
|
842
|
-
),
|
|
843
|
-
Fx.recover(() => {
|
|
844
|
-
if (token === null) {
|
|
845
|
-
finalizeLoadingState();
|
|
846
|
-
}
|
|
847
|
-
return Fx.succeed(undefined);
|
|
848
|
-
}),
|
|
849
|
-
),
|
|
850
|
-
);
|
|
851
|
-
return token;
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// SPA mode: refresh via localStorage + httpClient.
|
|
856
|
-
const tokenBeforeLockAcquisition = token;
|
|
857
|
-
return await browserMutex(REFRESH_TOKEN_STORAGE_KEY, async () => {
|
|
858
|
-
const tokenAfterLockAcquisition = token;
|
|
859
|
-
if (tokenAfterLockAcquisition !== tokenBeforeLockAcquisition) {
|
|
860
|
-
return tokenAfterLockAcquisition;
|
|
861
|
-
}
|
|
862
|
-
const refreshToken =
|
|
863
|
-
(await storageGet(REFRESH_TOKEN_STORAGE_KEY)) ?? null;
|
|
864
|
-
if (!refreshToken) {
|
|
865
|
-
finalizeLoadingState();
|
|
866
|
-
return null;
|
|
867
|
-
}
|
|
868
|
-
await verifyCodeAndSetToken(
|
|
869
|
-
{ refreshToken },
|
|
870
|
-
{ resyncConvexAuth: false },
|
|
871
|
-
);
|
|
872
|
-
return token;
|
|
873
|
-
});
|
|
874
|
-
};
|
|
875
|
-
|
|
876
|
-
// ---------------------------------------------------------------------------
|
|
877
|
-
// OAuth code flow (SPA mode only — server handles this in proxy mode)
|
|
878
|
-
// ---------------------------------------------------------------------------
|
|
879
|
-
|
|
880
|
-
const handleCodeFlow = async () => {
|
|
881
|
-
if (typeof window === "undefined") return;
|
|
882
|
-
if (handlingCodeFlow) return;
|
|
883
|
-
const code = new URLSearchParams(window.location.search).get("code");
|
|
884
|
-
if (!code) return;
|
|
885
|
-
handlingCodeFlow = true;
|
|
886
|
-
await Fx.run(
|
|
887
|
-
Fx.from({
|
|
888
|
-
ok: async () => {
|
|
889
|
-
await signIn(undefined, { code });
|
|
890
|
-
const codeUrl = new URL(window.location.href);
|
|
891
|
-
codeUrl.searchParams.delete("code");
|
|
892
|
-
await replaceUrl(codeUrl.pathname + codeUrl.search + codeUrl.hash);
|
|
893
|
-
},
|
|
894
|
-
err: (e) => e,
|
|
895
|
-
}).pipe(
|
|
896
|
-
Fx.recover(() => Fx.succeed(undefined)),
|
|
897
|
-
Fx.tap(() =>
|
|
898
|
-
Fx.sync(() => {
|
|
899
|
-
handlingCodeFlow = false;
|
|
900
|
-
}),
|
|
901
|
-
),
|
|
902
|
-
Fx.inspect(() =>
|
|
903
|
-
Fx.sync(() => {
|
|
904
|
-
handlingCodeFlow = false;
|
|
905
|
-
}),
|
|
906
|
-
),
|
|
907
|
-
),
|
|
908
|
-
);
|
|
909
|
-
// The flag is always reset — Fx.recover above ensures success path,
|
|
910
|
-
// but reset defensively here too.
|
|
911
|
-
handlingCodeFlow = false;
|
|
912
|
-
};
|
|
913
|
-
|
|
914
|
-
// ---------------------------------------------------------------------------
|
|
915
|
-
// Hydrate from storage (SPA mode only)
|
|
916
|
-
// ---------------------------------------------------------------------------
|
|
917
|
-
|
|
918
|
-
const hydrateFromStorage = async () => {
|
|
919
|
-
const storedToken = (await storageGet(JWT_STORAGE_KEY)) ?? null;
|
|
920
|
-
await setToken({
|
|
921
|
-
shouldStore: false,
|
|
922
|
-
tokens: storedToken === null ? null : { token: storedToken },
|
|
923
|
-
});
|
|
924
|
-
};
|
|
925
|
-
|
|
926
|
-
// ---------------------------------------------------------------------------
|
|
927
|
-
// Subscribe
|
|
928
|
-
// ---------------------------------------------------------------------------
|
|
929
|
-
|
|
930
|
-
/**
|
|
931
|
-
* Subscribe to auth state changes. Invokes the callback immediately
|
|
932
|
-
* with the current state, then again on every state transition.
|
|
933
|
-
*
|
|
934
|
-
* ```ts
|
|
935
|
-
* const unsub = auth.onChange(setState);
|
|
936
|
-
* ```
|
|
937
|
-
*
|
|
938
|
-
* @param cb - Callback receiving the latest {@link AuthState}.
|
|
939
|
-
* @returns An unsubscribe function.
|
|
940
|
-
*/
|
|
941
|
-
const onChange = (cb: (state: AuthState) => void): (() => void) => {
|
|
942
|
-
cb(snapshot);
|
|
943
|
-
const wrapped = () => cb(snapshot);
|
|
944
|
-
subscribers.add(wrapped);
|
|
945
|
-
return () => {
|
|
946
|
-
subscribers.delete(wrapped);
|
|
947
|
-
};
|
|
948
|
-
};
|
|
949
|
-
|
|
950
|
-
// ---------------------------------------------------------------------------
|
|
951
|
-
// Initialization
|
|
952
|
-
// ---------------------------------------------------------------------------
|
|
953
|
-
|
|
954
|
-
// Cross-tab sync via storage events (SPA mode only).
|
|
955
|
-
if (!proxy && typeof window !== "undefined") {
|
|
956
|
-
const registryKey = key(JWT_STORAGE_KEY);
|
|
957
|
-
const registry = getStorageListenerRegistry();
|
|
958
|
-
const existingListener = registry[registryKey];
|
|
959
|
-
if (existingListener !== undefined) {
|
|
960
|
-
window.removeEventListener("storage", existingListener);
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
const onStorage = (event: StorageEvent) => {
|
|
964
|
-
Fx.detach(async () => {
|
|
965
|
-
if (event.key !== key(JWT_STORAGE_KEY)) return;
|
|
966
|
-
await setToken({
|
|
967
|
-
shouldStore: false,
|
|
968
|
-
tokens: event.newValue === null ? null : { token: event.newValue },
|
|
969
|
-
});
|
|
970
|
-
}, "[convex-auth] Storage event handler failed:");
|
|
971
|
-
};
|
|
972
|
-
window.addEventListener("storage", onStorage);
|
|
973
|
-
registry[registryKey] = onStorage;
|
|
974
|
-
disposeStorageListener = () => {
|
|
975
|
-
if (registry[registryKey] === onStorage) {
|
|
976
|
-
delete registry[registryKey];
|
|
977
|
-
}
|
|
978
|
-
window.removeEventListener("storage", onStorage);
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
// Auto-wire: feed our tokens into the Convex client so
|
|
983
|
-
// queries and mutations are automatically authenticated.
|
|
984
|
-
bindConvexAuth();
|
|
985
|
-
|
|
986
|
-
// Auto-hydrate and handle code flow.
|
|
987
|
-
if (typeof window !== "undefined") {
|
|
988
|
-
if (proxy) {
|
|
989
|
-
// Proxy mode: eagerly resolve auth once on startup so routes that only
|
|
990
|
-
// read auth state (and do not issue Convex queries yet) don't stay in
|
|
991
|
-
// the initial loading phase.
|
|
992
|
-
if (!hasServerToken) {
|
|
993
|
-
Fx.detach(
|
|
994
|
-
() => fetchAccessToken({ forceRefreshToken: true }),
|
|
995
|
-
"[convex-auth] Proxy token refresh failed:",
|
|
996
|
-
);
|
|
997
|
-
}
|
|
998
|
-
} else {
|
|
999
|
-
// SPA mode: hydrate from localStorage, then handle OAuth code flow.
|
|
1000
|
-
Fx.detach(async () => {
|
|
1001
|
-
await Fx.run(
|
|
1002
|
-
Fx.from({
|
|
1003
|
-
ok: async () => {
|
|
1004
|
-
await hydrateFromStorage();
|
|
1005
|
-
await handleCodeFlow();
|
|
1006
|
-
},
|
|
1007
|
-
err: (e) => e,
|
|
1008
|
-
}).pipe(
|
|
1009
|
-
Fx.inspect((error) =>
|
|
1010
|
-
Fx.sync(() =>
|
|
1011
|
-
console.error(
|
|
1012
|
-
"[convex-auth] Client initialization failed:",
|
|
1013
|
-
error,
|
|
1014
|
-
),
|
|
1015
|
-
),
|
|
1016
|
-
),
|
|
1017
|
-
Fx.recover((_error) =>
|
|
1018
|
-
Fx.from({
|
|
1019
|
-
ok: () => setToken({ shouldStore: false, tokens: null }),
|
|
1020
|
-
err: (e) => e,
|
|
1021
|
-
}).pipe(Fx.recover(() => Fx.succeed(undefined))),
|
|
1022
|
-
),
|
|
1023
|
-
),
|
|
1024
|
-
);
|
|
1025
|
-
}, "[convex-auth] SPA initialization failed:");
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
// ---------------------------------------------------------------------------
|
|
1030
|
-
// Auth factor helpers
|
|
1031
|
-
// ---------------------------------------------------------------------------
|
|
1032
|
-
|
|
1033
|
-
const passkey = createPasskeyClient({
|
|
1034
|
-
proxy,
|
|
1035
|
-
convex,
|
|
1036
|
-
requireApiRefs,
|
|
1037
|
-
proxyFetch,
|
|
1038
|
-
setTokenAndMaybeWait,
|
|
1039
|
-
});
|
|
1040
|
-
|
|
1041
|
-
const totp = createTotpClient({
|
|
1042
|
-
proxy,
|
|
1043
|
-
convex,
|
|
1044
|
-
requireApiRefs,
|
|
1045
|
-
proxyFetch,
|
|
1046
|
-
setTokenAndMaybeWait,
|
|
1047
|
-
});
|
|
1048
|
-
|
|
1049
|
-
const device = createDeviceClient({
|
|
1050
|
-
proxy,
|
|
1051
|
-
convex,
|
|
1052
|
-
requireApiRefs,
|
|
1053
|
-
proxyFetch,
|
|
1054
|
-
setTokenAndMaybeWait,
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
return {
|
|
1058
|
-
/** Current auth state snapshot. */
|
|
1059
|
-
get state(): AuthState {
|
|
1060
|
-
return snapshot;
|
|
1061
|
-
},
|
|
1062
|
-
/** SSR-safe URL param reader. */
|
|
1063
|
-
param,
|
|
1064
|
-
/** Pending invite from URL or recovered from storage. Null if none. */
|
|
1065
|
-
get invite(): PendingInvite | null {
|
|
1066
|
-
const pendingInvite = getPendingInvite();
|
|
1067
|
-
if (!pendingInvite) return null;
|
|
1068
|
-
return {
|
|
1069
|
-
token: pendingInvite.token,
|
|
1070
|
-
email: pendingInvite.email,
|
|
1071
|
-
accept: acceptInvite,
|
|
1072
|
-
};
|
|
1073
|
-
},
|
|
1074
|
-
/** Sign in with a provider. See {@link SignInResult} for return shape. */
|
|
1075
|
-
signIn,
|
|
1076
|
-
/** Sign out and clear all token state. */
|
|
1077
|
-
signOut,
|
|
1078
|
-
/** Subscribe to auth state changes. Returns an unsubscribe function. */
|
|
1079
|
-
onChange,
|
|
1080
|
-
/** Passkey (WebAuthn) authentication helpers. */
|
|
1081
|
-
passkey,
|
|
1082
|
-
/** TOTP two-factor authentication helpers. */
|
|
1083
|
-
totp,
|
|
1084
|
-
/** Device authorization (RFC 8628) helpers. */
|
|
1085
|
-
device,
|
|
1086
|
-
/**
|
|
1087
|
-
* Tear down this auth client instance.
|
|
1088
|
-
*
|
|
1089
|
-
* Removes the cross-tab `storage` event listener, clears all
|
|
1090
|
-
* `onChange` subscribers, and rejects any in-flight handshake
|
|
1091
|
-
* waiters. Call this when the client is no longer needed
|
|
1092
|
-
* (e.g. on SPA unmount or hot-module replacement) to prevent
|
|
1093
|
-
* memory leaks and stale callbacks.
|
|
1094
|
-
*
|
|
1095
|
-
* @example
|
|
1096
|
-
* ```ts
|
|
1097
|
-
* // SvelteKit onDestroy
|
|
1098
|
-
* import { onDestroy } from "svelte";
|
|
1099
|
-
* const auth = client({ convex, api: api.auth });
|
|
1100
|
-
* onDestroy(() => auth.destroy());
|
|
1101
|
-
* ```
|
|
1102
|
-
*
|
|
1103
|
-
* @example
|
|
1104
|
-
* ```ts
|
|
1105
|
-
* const unsubscribe = auth.onChange((state) => console.log(state.phase));
|
|
1106
|
-
*
|
|
1107
|
-
* // Later, during cleanup:
|
|
1108
|
-
* unsubscribe();
|
|
1109
|
-
* auth.destroy();
|
|
1110
|
-
* ```
|
|
1111
|
-
*/
|
|
1112
|
-
destroy: () => {
|
|
1113
|
-
destroyed = true;
|
|
1114
|
-
settleHandshakeWaiters(authEpoch, {
|
|
1115
|
-
type: "reject",
|
|
1116
|
-
error: createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
|
|
1117
|
-
reason: "destroyed",
|
|
1118
|
-
}),
|
|
1119
|
-
});
|
|
1120
|
-
disposeStorageListener?.();
|
|
1121
|
-
subscribers.clear();
|
|
1122
|
-
},
|
|
1123
|
-
} as AuthClient<Api>;
|
|
1124
|
-
}
|