diff --git a/CHANGELOG.md b/CHANGELOG.md
index 566ec4e..7cd47ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## Unreleased
+
+- Expose `createOrupdateUsers` callback helper functions
+ `basicCreateOrUpdateUser` and `callAfterUserCreatedOrUpdated`. This makes
+ writing custom `createOrUpdateUsers` callbacks simpler.
+
## 0.0.78
- Add support for
diff --git a/docs/package-lock.json b/docs/package-lock.json
index cd0fce6..062dcd4 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -9,7 +9,6 @@
"version": "0.0.1",
"license": "MIT",
"dependencies": {
- "@hookform/resolvers": "^3.9.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-slot": "^1.1.0",
"class-variance-authority": "^0.7.1",
@@ -142,15 +141,6 @@
"react-dom": "^16 || ^17 || ^18"
}
},
- "node_modules/@hookform/resolvers": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz",
- "integrity": "sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==",
- "license": "MIT",
- "peerDependencies": {
- "react-hook-form": "^7.0.0"
- }
- },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
diff --git a/docs/pages/advanced.mdx b/docs/pages/advanced.mdx
index e909d18..f306d53 100644
--- a/docs/pages/advanced.mdx
+++ b/docs/pages/advanced.mdx
@@ -77,6 +77,7 @@ providing the
import GitHub from "@auth/core/providers/github";
import Password from "@convex-dev/auth/providers/Password";
import { MutationCtx } from "./_generated/server";
+import { basicCreateOrUpdateUser, callAfterUserCreatedOrUpdated } from "@convex-dev/auth/server";
export const { auth, signIn, signOut, store, isAuthenticated } = {
providers: [GitHub, Password],
@@ -84,19 +85,25 @@ export const { auth, signIn, signOut, store, isAuthenticated } = {
// `args.type` is one of "oauth" | "email" | "phone" | "credentials" | "verification"
// `args.provider` is the currently used provider config
async createOrUpdateUser(ctx: MutationCtx, args) {
- if (args.existingUserId) {
- // Optionally merge updated fields into the existing user object here
- return args.existingUserId;
- }
-
- // Implement your own account linking logic:
- const existingUser = await findUserByEmail(ctx, args.profile.email);
- if (existingUser) return existingUser._id;
-
- // Implement your own user creation:
- return ctx.db.insert("users", {
- /* ... */
- });
+ // This implementation matches the default, modify it to implement custom logic.
+
+ // Replace this logic with custom account linking and custom
+ // users table insert/update if desired.
+ const { userId, existingUserId: existingOrLinkedUserId } =
+ await basicCreateOrUpdateUser(ctx, {
+ existingUserId,
+ ...args,
+ });
+
+ // Include this or else any passed
+ // `config.callbacks.afterUserCreatedOrUpdated` won't run.
+ await callAfterUserCreatedOrUpdated(
+ ctx,
+ userId,
+ existingOrLinkedUserId,
+ args,
+ config,
+ );
},
},
};
diff --git a/docs/pages/api_reference/nextjs/server.mdx b/docs/pages/api_reference/nextjs/server.mdx
index 839aaa7..6f21edb 100644
--- a/docs/pages/api_reference/nextjs/server.mdx
+++ b/docs/pages/api_reference/nextjs/server.mdx
@@ -457,8 +457,8 @@ Returns a function that accepts a `Request` object and returns whether the reque
predefined routes that can be passed in as the first argument.
You can use glob patterns to match multiple routes or a function to match against the request object.
-Path patterns and regular expressions are supported, for example: `['/foo', '/bar(.*)'] or `[/^/foo/.*$/]`
-For more information, see: https://github.com/pillarjs/path-to-regexp
+Path patterns and limited regular expressions are supported.
+For more information, see: https://www.npmjs.com/package/path-to-regexp/v/6.3.0
Parameters
diff --git a/docs/pages/api_reference/server.mdx b/docs/pages/api_reference/server.mdx
index d219b32..cedc6ee 100644
--- a/docs/pages/api_reference/server.mdx
+++ b/docs/pages/api_reference/server.mdx
@@ -1341,7 +1341,24 @@ config.
-`args.shouldLink`?
+`args.shouldLinkViaEmail`?
+
+ |
+
+
+`boolean`
+
+ |
+
+
+Whether to link the account via email.
+
+ |
+
+
+
+
+`args.shouldLinkViaPhone`?
|
@@ -1351,7 +1368,7 @@ config.
|
-The `shouldLink` argument passed to `createAccount`.
+Whether to link the account via phone.
|
@@ -1514,7 +1531,24 @@ config.
-`args.shouldLink`?
+`args.shouldLinkViaEmail`?
+
+ |
+
+
+`boolean`
+
+ |
+
+
+Whether to link the account via email.
+
+ |
+
+
+
+
+`args.shouldLinkViaPhone`?
|
@@ -1524,7 +1558,7 @@ config.
|
-The `shouldLink` argument passed to `createAccount`.
+Whether to link the account via phone.
|
@@ -1549,7 +1583,7 @@ service.
Defined in
-[src/server/types.ts:232](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L232)
+[src/server/types.ts:240](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L240)
***
@@ -1916,7 +1950,7 @@ The values passed to the `signIn` function.
Defined in
-[src/server/types.ts:256](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L256)
+[src/server/types.ts:264](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L264)
***
@@ -1927,7 +1961,7 @@ Configurable options for an email provider config.
Defined in
-[src/server/types.ts:268](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L268)
+[src/server/types.ts:276](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L276)
***
@@ -1944,14 +1978,14 @@ phone number instead of the email address.
Defined in
-[src/server/types.ts:279](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L279)
+[src/server/types.ts:287](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L287)
#### type
Defined in
-[src/server/types.ts:280](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L280)
+[src/server/types.ts:288](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L288)
#### maxAge
@@ -1961,7 +1995,7 @@ Token expiration in seconds.
Defined in
-[src/server/types.ts:284](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L284)
+[src/server/types.ts:292](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L292)
#### sendVerificationRequest()
@@ -2067,7 +2101,7 @@ Send the phone number verification request.
Defined in
-[src/server/types.ts:288](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L288)
+[src/server/types.ts:296](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L296)
#### apiKey?
@@ -2077,7 +2111,7 @@ Defaults to `process.env.AUTH__KEY`.
Defined in
-[src/server/types.ts:301](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L301)
+[src/server/types.ts:309](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L309)
#### generateVerificationToken()?
@@ -2088,7 +2122,7 @@ Defaults to `process.env.AUTH__KEY`.
Defined in
-[src/server/types.ts:310](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L310)
+[src/server/types.ts:318](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L318)
#### normalizeIdentifier()?
@@ -2128,7 +2162,7 @@ The phone number used in `sendVerificationRequest`.
Defined in
-[src/server/types.ts:316](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L316)
+[src/server/types.ts:324](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L324)
#### authorize()?
@@ -2189,14 +2223,14 @@ The values passed to the `signIn` function.
Defined in
-[src/server/types.ts:324](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L324)
+[src/server/types.ts:332](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L332)
#### options
Defined in
-[src/server/types.ts:331](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L331)
+[src/server/types.ts:339](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L339)
***
@@ -2207,7 +2241,7 @@ Configurable options for a phone provider config.
Defined in
-[src/server/types.ts:337](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L337)
+[src/server/types.ts:345](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L345)
***
@@ -2227,7 +2261,7 @@ Similar to Auth.js Credentials config.
Defined in
-[src/server/types.ts:344](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L344)
+[src/server/types.ts:352](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L352)
***
@@ -2247,7 +2281,7 @@ the config passed to `convexAuth`.
Defined in
-[src/server/types.ts:353](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L353)
+[src/server/types.ts:361](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L361)
***
@@ -2270,7 +2304,7 @@ See [ConvexAuthConfig](server.mdx#convexauthconfig)
Defined in
-[src/server/types.ts:364](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L364)
+[src/server/types.ts:372](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L372)
***
@@ -2281,4 +2315,4 @@ Materialized Auth.js provider config.
Defined in
-[src/server/types.ts:372](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L372)
+[src/server/types.ts:380](https://github.com/get-convex/convex-auth/blob/main/src/server/types.ts#L380)
diff --git a/src/server/implementation/index.ts b/src/server/implementation/index.ts
index 20a2857..0d0521c 100644
--- a/src/server/implementation/index.ts
+++ b/src/server/implementation/index.ts
@@ -49,9 +49,16 @@ import {
import { signInImpl } from "./signIn.js";
import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
import { getAuthorizationUrl } from "../oauth/authorizationUrl.js";
-import { defaultCookiesOptions, oAuthConfigToInternalProvider } from "../oauth/convexAuth.js";
+import {
+ defaultCookiesOptions,
+ oAuthConfigToInternalProvider,
+} from "../oauth/convexAuth.js";
import { handleOAuth } from "../oauth/callback.js";
export { getAuthSessionId } from "./sessions.js";
+export {
+ basicCreateOrUpdateUser,
+ callAfterUserCreatedOrUpdated,
+} from "./users.js";
/**
* @internal
@@ -240,12 +247,10 @@ export function convexAuth(config_: ConvexAuthConfig) {
providerId,
) as OAuthConfig;
const { redirect, cookies, signature } =
- await getAuthorizationUrl(
- {
- provider: await oAuthConfigToInternalProvider(provider),
- cookies: defaultCookiesOptions(providerId),
- },
- );
+ await getAuthorizationUrl({
+ provider: await oAuthConfigToInternalProvider(provider),
+ cookies: defaultCookiesOptions(providerId),
+ });
await callVerifierSignature(ctx, {
verifier,
@@ -295,10 +300,11 @@ export function convexAuth(config_: ConvexAuthConfig) {
});
const params = url.searchParams;
-
+
// Handle OAuth providers that use formData (such as Apple)
if (
- request.headers.get("Content-Type") === "application/x-www-form-urlencoded"
+ request.headers.get("Content-Type") ===
+ "application/x-www-form-urlencoded"
) {
const formData = await request.formData();
for (const [key, value] of formData.entries()) {
@@ -441,15 +447,18 @@ export function convexAuth(config_: ConvexAuthConfig) {
return storeImpl(ctx, args, getProviderOrThrow, config);
},
}),
-
+
/**
* Utility function for frameworks to use to get the current auth state
* based on credentials that they've supplied separately.
*/
- isAuthenticated: queryGeneric({args: {}, handler: async (ctx, _args): Promise => {
- const ident = await ctx.auth.getUserIdentity();
- return ident !== null;
- }}),
+ isAuthenticated: queryGeneric({
+ args: {},
+ handler: async (ctx, _args): Promise => {
+ const ident = await ctx.auth.getUserIdentity();
+ return ident !== null;
+ },
+ }),
};
}
diff --git a/src/server/implementation/users.ts b/src/server/implementation/users.ts
index a91da71..3bdd6d2 100644
--- a/src/server/implementation/users.ts
+++ b/src/server/implementation/users.ts
@@ -1,5 +1,5 @@
import { GenericId } from "convex/values";
-import { AuthDataModel, Doc, MutationCtx, QueryCtx } from "./types.js";
+import { Doc, MutationCtx, QueryCtx } from "./types.js";
import { AuthProviderMaterializedConfig, ConvexAuthConfig } from "../types.js";
import { LOG_LEVELS, logWithLevel } from "./utils.js";
import {
@@ -38,7 +38,7 @@ export async function upsertUserAndAccount(
userId: GenericId<"users">;
accountId: GenericId<"authAccounts">;
}> {
- const userId = await defaultCreateOrUpdateUser(
+ const userId = await callCreateOrUpdateUser(
ctx,
sessionId,
"existingAccount" in account ? account.existingAccount : null,
@@ -49,14 +49,14 @@ export async function upsertUserAndAccount(
return { userId, accountId };
}
-async function defaultCreateOrUpdateUser(
+async function callCreateOrUpdateUser(
ctx: MutationCtx,
existingSessionId: GenericId<"authSessions"> | null,
existingAccount: Doc<"authAccounts"> | null,
args: CreateOrUpdateUserArgs,
config: ConvexAuthConfig,
) {
- logWithLevel(LOG_LEVELS.DEBUG, "defaultCreateOrUpdateUser args:", {
+ logWithLevel(LOG_LEVELS.DEBUG, "callCreateOrUpdateUser args:", {
existingAccountId: existingAccount?._id,
existingSessionId,
args,
@@ -102,7 +102,7 @@ export async function basicCreateOrUpdateUser(
return { userId, existingUserId: existingOrLinkedUserId };
}
-async function callAfterUserCreatedOrUpdated(
+export async function callAfterUserCreatedOrUpdated(
ctx: MutationCtx,
userId: GenericId<"users">,
existingUserId: GenericId<"users"> | null,
diff --git a/src/server/index.ts b/src/server/index.ts
index 3aadb8a..a355b23 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -20,6 +20,8 @@ export {
signInViaProvider,
invalidateSessions,
modifyAccountCredentials,
+ callAfterUserCreatedOrUpdated,
+ basicCreateOrUpdateUser,
Tokens,
Doc,
} from "./implementation/index.js";
diff --git a/test/convex/schema.ts b/test/convex/schema.ts
index a055ad0..9ce5d4c 100644
--- a/test/convex/schema.ts
+++ b/test/convex/schema.ts
@@ -8,8 +8,10 @@ export default defineSchema({
name: v.optional(v.string()),
image: v.optional(v.string()),
email: v.optional(v.string()),
+ emailVerified: v.optional(v.boolean()),
emailVerificationTime: v.optional(v.number()),
phone: v.optional(v.string()),
+ phoneVerified: v.optional(v.boolean()),
phoneVerificationTime: v.optional(v.number()),
isAnonymous: v.optional(v.boolean()),
// Custom field.