@robelest/convex-auth 0.0.4-preview.28 → 0.0.4-preview.30

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.
Files changed (75) hide show
  1. package/dist/bin.js +38 -2
  2. package/dist/browser/passkey.js +2 -2
  3. package/dist/client/core/types.d.ts +4 -15
  4. package/dist/client/factors/device.js +3 -3
  5. package/dist/client/factors/totp.js +8 -8
  6. package/dist/client/index.js +7 -7
  7. package/dist/component/_generated/component.d.ts +344 -0
  8. package/dist/component/model.d.ts +25 -25
  9. package/dist/component/model.js +1 -0
  10. package/dist/component/public/factors/totp.js +12 -0
  11. package/dist/component/public/groups/core.js +89 -1
  12. package/dist/component/public/groups/members.js +39 -1
  13. package/dist/component/public/identity/accounts.js +22 -3
  14. package/dist/component/public/identity/users.js +79 -5
  15. package/dist/component/public/sso/audit.js +5 -2
  16. package/dist/component/public/sso/core.js +3 -3
  17. package/dist/component/public/sso/domains.js +7 -3
  18. package/dist/component/public/sso/scim.js +36 -1
  19. package/dist/component/public.js +5 -5
  20. package/dist/component/schema.d.ts +289 -285
  21. package/dist/component/schema.js +2 -1
  22. package/dist/core/index.d.ts +52 -35
  23. package/dist/core/index.js +3 -15
  24. package/dist/providers/credentials.d.ts +12 -0
  25. package/dist/providers/password.js +28 -7
  26. package/dist/server/auth-context.d.ts +18 -1
  27. package/dist/server/auth-context.js +15 -2
  28. package/dist/server/auth.d.ts +4 -6
  29. package/dist/server/auth.js +2 -6
  30. package/dist/server/config.js +10 -0
  31. package/dist/server/context.js +13 -9
  32. package/dist/server/contract.d.ts +1 -1
  33. package/dist/server/contract.js +44 -12
  34. package/dist/server/convexIdentity.d.ts +15 -0
  35. package/dist/server/convexIdentity.js +1 -0
  36. package/dist/server/core.js +140 -156
  37. package/dist/server/ctxCache.js +94 -0
  38. package/dist/server/device.js +13 -12
  39. package/dist/server/env.js +10 -2
  40. package/dist/server/http.d.ts +1 -1
  41. package/dist/server/identity.js +30 -4
  42. package/dist/server/index.d.ts +1 -0
  43. package/dist/server/index.js +1 -0
  44. package/dist/server/limits.js +39 -9
  45. package/dist/server/mounts.d.ts +81 -81
  46. package/dist/server/mutations/credentialsSignIn.js +114 -0
  47. package/dist/server/mutations/index.js +2 -1
  48. package/dist/server/mutations/refresh.js +59 -15
  49. package/dist/server/mutations/retrieve.js +2 -1
  50. package/dist/server/mutations/signin.js +37 -9
  51. package/dist/server/mutations/signout.js +27 -10
  52. package/dist/server/mutations/store.js +6 -1
  53. package/dist/server/mutations/verify.js +32 -8
  54. package/dist/server/oauth/factory.js +2 -1
  55. package/dist/server/passkey.js +12 -13
  56. package/dist/server/prefetch.js +8 -8
  57. package/dist/server/runtime.d.ts +74 -46
  58. package/dist/server/runtime.js +7 -40
  59. package/dist/server/services/group.js +23 -16
  60. package/dist/server/sessions.d.ts +22 -0
  61. package/dist/server/sessions.js +52 -32
  62. package/dist/server/signin.js +48 -24
  63. package/dist/server/sso/domain.d.ts +1 -1
  64. package/dist/server/sso/domain.js +1 -2
  65. package/dist/server/sso/oidc.js +1 -1
  66. package/dist/server/telemetry.js +58 -0
  67. package/dist/server/tokens.js +41 -5
  68. package/dist/server/totp.js +13 -11
  69. package/dist/server/types.d.ts +58 -2
  70. package/dist/server/users.js +3 -2
  71. package/dist/server/utils/span.js +28 -1
  72. package/dist/shared/authResults.d.ts +16 -0
  73. package/package.json +6 -3
  74. package/README.md +0 -161
  75. package/dist/server/constants.js +0 -6
package/dist/bin.js CHANGED
@@ -6255,7 +6255,10 @@ gradient.pastel = pastel;
6255
6255
  //#region src/cli/keys.ts
6256
6256
  async function generateKeys() {
6257
6257
  try {
6258
- const keys = await generateKeyPair("RS256", { extractable: true });
6258
+ const keys = await generateKeyPair("EdDSA", {
6259
+ crv: "Ed25519",
6260
+ extractable: true
6261
+ });
6259
6262
  const privateKey = await exportPKCS8(keys.privateKey);
6260
6263
  const publicKey = await exportJWK(keys.publicKey);
6261
6264
  const jwks = JSON.stringify({ keys: [{
@@ -6263,7 +6266,7 @@ async function generateKeys() {
6263
6266
  ...publicKey
6264
6267
  }] });
6265
6268
  return {
6266
- JWT_PRIVATE_KEY: `${privateKey.trimEnd().replace(/\n/g, " ")}`,
6269
+ JWT_PRIVATE_KEY: privateKey.trimEnd(),
6267
6270
  JWKS: jwks,
6268
6271
  AUTH_SECRET_ENCRYPTION_KEY: randomBytes(32).toString("base64url")
6269
6272
  };
@@ -6503,6 +6506,7 @@ async function runSetup(options) {
6503
6506
  await configureConvexConfig(config);
6504
6507
  await initializeAuth(config);
6505
6508
  await initializeAuthCore(config);
6509
+ await initializeAuthConfig(config);
6506
6510
  await configureHttp(config);
6507
6511
  if (options.variables !== void 0) await configureOtherVariables(config, options.variables);
6508
6512
  else printFinalSuccessMessage(config);
@@ -6816,6 +6820,38 @@ export const auth = createAuthContext(components.auth);
6816
6820
  O.success(`Created ${newPath}`);
6817
6821
  }
6818
6822
  }
6823
+ async function initializeAuthConfig(config) {
6824
+ logStep(config, "Initialize auth.config file");
6825
+ const sourceTemplate = `\
6826
+ export default {$$
6827
+ providers: [$$
6828
+ {$$
6829
+ domain: process.env.CONVEX_SITE_URL,$$
6830
+ applicationID: "convex",$$
6831
+ },$$
6832
+ ],$$
6833
+ };
6834
+ `;
6835
+ const source = templateToSource(sourceTemplate);
6836
+ const authConfigPath = path.join(config.convexFolderPath, "auth.config");
6837
+ const existingPath = existingNonEmptySourcePath(authConfigPath);
6838
+ if (existingPath !== null) if (doesAlreadyMatchTemplate(readFileSync(existingPath, "utf8"), sourceTemplate)) O.success(`${existingPath} is already set up.`);
6839
+ else {
6840
+ O.info(`You already have ${existingPath}. Make sure it trusts CONVEX_SITE_URL as the Convex auth issuer:`);
6841
+ O.message(indent(`\n${source}\n`));
6842
+ const ready = await ot({ message: "Ready to continue?" });
6843
+ handleCancel(ready);
6844
+ if (!ready) {
6845
+ pt("Setup cancelled.");
6846
+ process$1.exit(1);
6847
+ }
6848
+ }
6849
+ else {
6850
+ const newPath = config.usesTypeScript ? `${authConfigPath}.ts` : `${authConfigPath}.js`;
6851
+ writeFileSync(newPath, source);
6852
+ O.success(`Created ${newPath}`);
6853
+ }
6854
+ }
6819
6855
  async function configureHttp(config) {
6820
6856
  logStep(config, "Configure http file");
6821
6857
  const sourceTemplate = `\
@@ -8,7 +8,7 @@ function createPasskeyClient(deps) {
8
8
  if (result.kind !== "signedIn") return { kind: "started" };
9
9
  return await setTokenAndMaybeWait(proxy ? {
10
10
  shouldStore: false,
11
- tokens: result.tokens === null ? null : { token: result.tokens.token },
11
+ tokens: result.session === null ? null : { token: result.session.token },
12
12
  waitForHandshake: true,
13
13
  context: {
14
14
  provider: "passkey",
@@ -16,7 +16,7 @@ function createPasskeyClient(deps) {
16
16
  }
17
17
  } : {
18
18
  shouldStore: true,
19
- tokens: result.tokens,
19
+ tokens: result.session,
20
20
  waitForHandshake: true,
21
21
  context: {
22
22
  provider: "passkey",
@@ -1,3 +1,4 @@
1
+ import { AuthTokens, DeviceCodePayload } from "../../shared/authResults.js";
1
2
  import { ConvexError, Value } from "convex/values";
2
3
  import { FunctionReference } from "convex/server";
3
4
 
@@ -76,7 +77,7 @@ interface ClientAdapterDeps {
76
77
  proxyFetch: (body: Record<string, unknown>) => Promise<unknown>;
77
78
  setTokenAndMaybeWait: (args: {
78
79
  shouldStore: true;
79
- tokens: AuthSession | null;
80
+ tokens: AuthTokens | null;
80
81
  waitForHandshake: boolean;
81
82
  context: {
82
83
  provider?: string;
@@ -97,28 +98,17 @@ interface ClientAdapterDeps {
97
98
  interface ClientAdapterFactories {
98
99
  passkey?: (deps: ClientAdapterDeps) => PasskeyClient;
99
100
  }
100
- type AuthSession = {
101
- token: string;
102
- refreshToken: string;
103
- };
101
+ type DeviceCodeResult = DeviceCodePayload;
104
102
  /**
105
103
  * Device code response returned when signing in with the `"device"` provider.
106
104
  *
107
105
  * The device displays the `userCode` (or `verificationUriComplete`) and
108
106
  * polls via `auth.device.poll()` until the user authorizes.
109
107
  */
110
- type DeviceCodeResult = {
111
- /** High-entropy device code used for polling (keep secret). */deviceCode: string; /** Short human-readable code the user enters (e.g. "WDJB-MJHT"). */
112
- userCode: string; /** Base verification URL (e.g. "https://myapp.com/device"). */
113
- verificationUri: string; /** Verification URL with user code pre-filled as `?code=XXXX-XXXX`. */
114
- verificationUriComplete: string; /** Lifetime of the codes in seconds. */
115
- expiresIn: number; /** Minimum polling interval in seconds. */
116
- interval: number;
117
- };
118
108
  /**
119
109
  * Result of a `signIn` call.
120
110
  *
121
- * - `kind: "signedIn"` — credentials were accepted and the user is authenticated.
111
+ * - `kind: "signedIn"` — credentials were accepted and a client session is now available.
122
112
  * - `kind: "redirect"` — OAuth flow initiated; redirect the user to `redirect.toString()`.
123
113
  * - `kind: "totpRequired"` — credentials valid but 2FA is needed; call `auth.totp.verify()`.
124
114
  * - `kind: "deviceCode"` — device flow initiated; display the code and poll via `auth.device.poll()`.
@@ -163,7 +153,6 @@ type AuthState = {
163
153
  type AuthApiRefs<HasPasskey extends boolean = boolean, HasTotp extends boolean = boolean, HasDevice extends boolean = boolean> = {
164
154
  signIn: FunctionReference<"action", "public", Record<string, Value>, unknown>;
165
155
  signOut: FunctionReference<"action", "public", Record<string, Value>, unknown>;
166
- store: FunctionReference<"mutation", "public", Record<string, Value>, unknown>;
167
156
  };
168
157
  /**
169
158
  * Passkey (WebAuthn) client-side helpers.
@@ -45,10 +45,10 @@ function createDeviceClient(deps) {
45
45
  throw error;
46
46
  }
47
47
  if (pollResult === null) continue;
48
- if (isSignedInResult(pollResult) && pollResult.tokens) {
48
+ if (isSignedInResult(pollResult) && pollResult.session) {
49
49
  if (proxy) await setTokenAndMaybeWait({
50
50
  shouldStore: false,
51
- tokens: pollResult.tokens === null ? null : { token: pollResult.tokens.token },
51
+ tokens: pollResult.session === null ? null : { token: pollResult.session.token },
52
52
  waitForHandshake: true,
53
53
  context: {
54
54
  provider: "device",
@@ -57,7 +57,7 @@ function createDeviceClient(deps) {
57
57
  });
58
58
  else await setTokenAndMaybeWait({
59
59
  shouldStore: true,
60
- tokens: pollResult.tokens ?? null,
60
+ tokens: pollResult.session ?? null,
61
61
  waitForHandshake: true,
62
62
  context: {
63
63
  provider: "device",
@@ -56,9 +56,9 @@ function createTotpClient(deps) {
56
56
  verifier: opts.verifier
57
57
  }
58
58
  });
59
- if (isSignedInResult(result$1) && result$1.tokens) await setTokenAndMaybeWait({
59
+ if (isSignedInResult(result$1) && result$1.session) await setTokenAndMaybeWait({
60
60
  shouldStore: false,
61
- tokens: result$1.tokens === null ? null : { token: result$1.tokens.token },
61
+ tokens: result$1.session === null ? null : { token: result$1.session.token },
62
62
  waitForHandshake: true,
63
63
  context: {
64
64
  provider: "totp",
@@ -72,9 +72,9 @@ function createTotpClient(deps) {
72
72
  params,
73
73
  verifier: opts.verifier
74
74
  });
75
- if (isSignedInResult(result) && result.tokens) await setTokenAndMaybeWait({
75
+ if (isSignedInResult(result) && result.session) await setTokenAndMaybeWait({
76
76
  shouldStore: true,
77
- tokens: result.tokens ?? null,
77
+ tokens: result.session ?? null,
78
78
  waitForHandshake: true,
79
79
  context: {
80
80
  provider: "totp",
@@ -96,9 +96,9 @@ function createTotpClient(deps) {
96
96
  verifier: opts.verifier
97
97
  }
98
98
  });
99
- if (isSignedInResult(result$1) && result$1.tokens) await setTokenAndMaybeWait({
99
+ if (isSignedInResult(result$1) && result$1.session) await setTokenAndMaybeWait({
100
100
  shouldStore: false,
101
- tokens: result$1.tokens === null ? null : { token: result$1.tokens.token },
101
+ tokens: result$1.session === null ? null : { token: result$1.session.token },
102
102
  waitForHandshake: true,
103
103
  context: {
104
104
  provider: "totp",
@@ -112,9 +112,9 @@ function createTotpClient(deps) {
112
112
  params,
113
113
  verifier: opts.verifier
114
114
  });
115
- if (isSignedInResult(result) && result.tokens) await setTokenAndMaybeWait({
115
+ if (isSignedInResult(result) && result.session) await setTokenAndMaybeWait({
116
116
  shouldStore: true,
117
- tokens: result.tokens ?? null,
117
+ tokens: result.session ?? null,
118
118
  waitForHandshake: true,
119
119
  context: {
120
120
  provider: "totp",
@@ -361,13 +361,13 @@ function client(options) {
361
361
  const verifyCodeAndSetToken = async (args, opts) => {
362
362
  const result = await verifyCode(args);
363
363
  if (result.kind !== "signedIn") throw new Error("Code exchange did not return tokens.");
364
- const { tokens } = result;
364
+ const { session } = result;
365
365
  await setToken({
366
366
  shouldStore: true,
367
- tokens: tokens ?? null,
367
+ tokens: session ?? null,
368
368
  resyncConvexAuth: opts?.resyncConvexAuth
369
369
  });
370
- return tokens !== null;
370
+ return session !== null;
371
371
  };
372
372
  const normalizeDeviceCodeResult = (device_code) => {
373
373
  const input = device_code;
@@ -441,7 +441,7 @@ function client(options) {
441
441
  };
442
442
  if (result.kind === "signedIn") return await setTokenAndMaybeWait(resultOptions.shouldStore ? {
443
443
  shouldStore: true,
444
- tokens: result.tokens,
444
+ tokens: result.session,
445
445
  waitForHandshake: true,
446
446
  context: {
447
447
  provider,
@@ -449,7 +449,7 @@ function client(options) {
449
449
  }
450
450
  } : {
451
451
  shouldStore: false,
452
- tokens: result.tokens === null ? null : { token: result.tokens.token },
452
+ tokens: result.session === null ? null : { token: result.session.token },
453
453
  waitForHandshake: true,
454
454
  context: {
455
455
  provider,
@@ -538,9 +538,9 @@ function client(options) {
538
538
  action: "auth:signIn",
539
539
  args: { refreshToken: true }
540
540
  }), isRetriableProxyRefreshError);
541
- if (isSignedInResult(result) && result.tokens) await setToken({
541
+ if (isSignedInResult(result) && result.session) await setToken({
542
542
  shouldStore: false,
543
- tokens: { token: result.tokens.token },
543
+ tokens: { token: result.session.token },
544
544
  resyncConvexAuth: false
545
545
  });
546
546
  else await setToken({