mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-06 04:19:20 +00:00
Merge remote-tracking branch 'origin/main' into v1.3
This commit is contained in:
@@ -159,38 +159,38 @@ export default function UserCard(props: {
|
||||
Please verify your email address. Check your inbox for the
|
||||
verification email. If you haven't received the email, click the
|
||||
button below to resend.
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
className="mt-2"
|
||||
onClick={async () => {
|
||||
await client.sendVerificationEmail(
|
||||
{
|
||||
email: session?.user.email || "",
|
||||
},
|
||||
{
|
||||
onRequest(context) {
|
||||
setEmailVerificationPending(true);
|
||||
},
|
||||
onError(context) {
|
||||
toast.error(context.error.message);
|
||||
setEmailVerificationPending(false);
|
||||
},
|
||||
onSuccess() {
|
||||
toast.success("Verification email sent successfully");
|
||||
setEmailVerificationPending(false);
|
||||
},
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
{emailVerificationPending ? (
|
||||
<Loader2 size={15} className="animate-spin" />
|
||||
) : (
|
||||
"Resend Verification Email"
|
||||
)}
|
||||
</Button>
|
||||
</AlertDescription>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
className="mt-2"
|
||||
onClick={async () => {
|
||||
await client.sendVerificationEmail(
|
||||
{
|
||||
email: session?.user.email || "",
|
||||
},
|
||||
{
|
||||
onRequest(context) {
|
||||
setEmailVerificationPending(true);
|
||||
},
|
||||
onError(context) {
|
||||
toast.error(context.error.message);
|
||||
setEmailVerificationPending(false);
|
||||
},
|
||||
onSuccess() {
|
||||
toast.success("Verification email sent successfully");
|
||||
setEmailVerificationPending(false);
|
||||
},
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
{emailVerificationPending ? (
|
||||
<Loader2 size={15} className="animate-spin" />
|
||||
) : (
|
||||
"Resend Verification Email"
|
||||
)}
|
||||
</Button>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
|
||||
@@ -597,7 +597,7 @@ await authClient.organization.updateMemberRole({
|
||||
|
||||
### Get Active Member
|
||||
|
||||
To get the current member of the organization you can use the `organization.getActiveMember` function. This function will return the current active member.
|
||||
To get the member details of the active organization you can use the `organization.getActiveMember` function.
|
||||
|
||||
```ts title="auth-client.ts"
|
||||
const member = await authClient.organization.getActiveMember()
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"@simplewebauthn/server": "^13.0.0",
|
||||
"better-call": "catalog:",
|
||||
"defu": "^6.1.4",
|
||||
"jose": "^5.9.6",
|
||||
"jose": "^6.0.11",
|
||||
"kysely": "^0.28.2",
|
||||
"nanostores": "^0.11.3",
|
||||
"zod": "^3.24.1"
|
||||
|
||||
@@ -159,11 +159,20 @@ export const createAdapter =
|
||||
* then we should return the model name ending with an `s`.
|
||||
*/
|
||||
const getModelName = (model: string) => {
|
||||
return schema[getDefaultModelName(model)].modelName !== model
|
||||
? schema[getDefaultModelName(model)].modelName
|
||||
: config.usePlural
|
||||
? `${model}s`
|
||||
: model;
|
||||
const defaultModelKey = getDefaultModelName(model);
|
||||
const usePlural = config && config.usePlural;
|
||||
const useCustomModelName =
|
||||
schema &&
|
||||
schema[defaultModelKey] &&
|
||||
schema[defaultModelKey].modelName !== model;
|
||||
|
||||
if (useCustomModelName) {
|
||||
return usePlural
|
||||
? `${schema[defaultModelKey].modelName}s`
|
||||
: schema[defaultModelKey].modelName;
|
||||
}
|
||||
|
||||
return usePlural ? `${model}s` : model;
|
||||
};
|
||||
/**
|
||||
* Get the field name which is expected to be saved in the database based on the user's schema.
|
||||
@@ -477,6 +486,8 @@ export const createAdapter =
|
||||
? undefined
|
||||
: CleanedWhere[] => {
|
||||
if (!where) return undefined as any;
|
||||
const newMappedKeys = config.mapKeysTransformInput ?? {};
|
||||
|
||||
return where.map((w) => {
|
||||
const {
|
||||
field: unsafe_field,
|
||||
@@ -495,11 +506,13 @@ export const createAdapter =
|
||||
field: unsafe_field,
|
||||
model,
|
||||
});
|
||||
const fieldName: string =
|
||||
newMappedKeys[defaultFieldName] ||
|
||||
getFieldName({
|
||||
field: defaultFieldName,
|
||||
model: defaultModelName,
|
||||
});
|
||||
|
||||
const fieldName = getFieldName({
|
||||
field: defaultFieldName,
|
||||
model: defaultModelName,
|
||||
});
|
||||
const fieldAttr = getFieldAttributes({
|
||||
field: defaultFieldName,
|
||||
model: defaultModelName,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { describe, test, expect } from "vitest";
|
||||
import { createAdapter } from "..";
|
||||
import type { AdapterConfig, CreateCustomAdapter } from "../types";
|
||||
import type {
|
||||
AdapterConfig,
|
||||
CleanedWhere,
|
||||
CreateCustomAdapter,
|
||||
} from "../types";
|
||||
import type { BetterAuthOptions, User, Where } from "../../../types";
|
||||
import { betterAuth } from "../../../auth";
|
||||
|
||||
@@ -161,6 +165,31 @@ describe("Create Adapter Helper", async () => {
|
||||
expect(res.id).toBe("HARD-CODED-ID");
|
||||
});
|
||||
|
||||
test("Should not generate an id if `advanced.database.generateId` is not defined or false", async () => {
|
||||
const adapter = await createTestAdapter({
|
||||
config: {},
|
||||
options: {
|
||||
advanced: {
|
||||
database: { generateId: false, useNumberId: false },
|
||||
},
|
||||
},
|
||||
adapter(args_0) {
|
||||
return {
|
||||
async create(data) {
|
||||
expect(data.data.id).not.toBeDefined();
|
||||
return data.data;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const testResult = await adapter.create({
|
||||
model: "user",
|
||||
data: { name: "test-name" },
|
||||
});
|
||||
expect(testResult.id).not.toBeDefined();
|
||||
});
|
||||
|
||||
test("Should throw an error if the database doesn't support numeric ids and the user has enabled `useNumberId`", async () => {
|
||||
let error: any | null = null;
|
||||
try {
|
||||
@@ -559,6 +588,7 @@ describe("Create Adapter Helper", async () => {
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const res = (await adapter.create({
|
||||
model: "user",
|
||||
data: { email: "test@test.com" },
|
||||
@@ -572,6 +602,33 @@ describe("Create Adapter Helper", async () => {
|
||||
expect(parameters.data.email_address).toEqual("test@test.com");
|
||||
});
|
||||
|
||||
test("Should allow custom transform input to transform the where clause", async () => {
|
||||
const parameters: CleanedWhere[] = await new Promise(async (r) => {
|
||||
const adapter = await createTestAdapter({
|
||||
config: {
|
||||
debugLogs: {},
|
||||
mapKeysTransformInput: {
|
||||
id: "_id",
|
||||
},
|
||||
},
|
||||
adapter(args_0) {
|
||||
return {
|
||||
async findOne({ model, where, select }) {
|
||||
r(where);
|
||||
return {} as any;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
adapter.findOne({
|
||||
model: "user",
|
||||
where: [{ field: "id", value: "123" }],
|
||||
});
|
||||
});
|
||||
|
||||
expect(parameters[0]!.field).toEqual("_id");
|
||||
});
|
||||
|
||||
test("Should allow custom map output key transformation", async () => {
|
||||
const parameters: {
|
||||
data: { email: string };
|
||||
|
||||
@@ -94,7 +94,10 @@ export const originCheck = (
|
||||
if (pattern.includes("*")) {
|
||||
return wildcardMatch(pattern)(getHost(url));
|
||||
}
|
||||
return url.startsWith(pattern);
|
||||
const protocol = getProtocol(url);
|
||||
return protocol === "http:" || protocol === "https:" || !protocol
|
||||
? pattern === getOrigin(url)
|
||||
: url.startsWith(pattern);
|
||||
};
|
||||
|
||||
const validateURL = (url: string | undefined, label: string) => {
|
||||
|
||||
@@ -256,6 +256,7 @@ export const linkSocialAccount = createAuthEndpoint(
|
||||
if (hasBeenLinked) {
|
||||
return c.json({
|
||||
redirect: false,
|
||||
url: "", // this is for type inference
|
||||
status: true,
|
||||
});
|
||||
}
|
||||
@@ -316,6 +317,7 @@ export const linkSocialAccount = createAuthEndpoint(
|
||||
|
||||
return c.json({
|
||||
redirect: false,
|
||||
url: "", // this is for type inference
|
||||
status: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { parseSetCookieHeader } from "../cookies";
|
||||
|
||||
let isBuilding: boolean | undefined;
|
||||
|
||||
let isBuilding: boolean | undefined;
|
||||
|
||||
export const toSvelteKitHandler = (auth: {
|
||||
handler: (request: Request) => any;
|
||||
options: BetterAuthOptions;
|
||||
|
||||
@@ -392,6 +392,10 @@ export const emailOTP = (options: EmailOTPOptions) => {
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
await ctx.context.options.emailVerification?.onEmailVerification?.(
|
||||
updatedUser,
|
||||
ctx.request,
|
||||
);
|
||||
|
||||
if (
|
||||
ctx.context.options.emailVerification?.autoSignInAfterVerification
|
||||
|
||||
@@ -430,7 +430,7 @@ export const getActiveMember = createAuthEndpoint(
|
||||
use: [orgMiddleware, orgSessionMiddleware],
|
||||
metadata: {
|
||||
openapi: {
|
||||
description: "Get the active member in the organization",
|
||||
description: "Get the member details of the active organization",
|
||||
responses: {
|
||||
"200": {
|
||||
description: "Success",
|
||||
|
||||
385
pnpm-lock.yaml
generated
385
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user