mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-26 08:56:40 -05:00
feat: changes in api
This commit is contained in:
Binary file not shown.
@@ -21,6 +21,9 @@ function App() {
|
||||
<p>
|
||||
{session.user.name}
|
||||
</p>
|
||||
<p>
|
||||
{session.user.username}
|
||||
</p>
|
||||
<p>
|
||||
{session.user.email}
|
||||
</p>
|
||||
@@ -58,6 +61,7 @@ function App() {
|
||||
}}>
|
||||
Continue with github
|
||||
</button>
|
||||
<SignIn />
|
||||
<SignUp />
|
||||
</div>
|
||||
)
|
||||
@@ -73,6 +77,7 @@ export default App;
|
||||
function SignUp() {
|
||||
const [email, setEmail] = useState("")
|
||||
const [name, setName] = useState("")
|
||||
const [username, setUsername] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
return (
|
||||
<div style={{
|
||||
@@ -96,6 +101,12 @@ function SignUp() {
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
<input type="text" id="username" placeholder="username" style={{
|
||||
width: "100%"
|
||||
}}
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
<input type="password" id="password" placeholder="Password" style={{
|
||||
width: "100%"
|
||||
}}
|
||||
@@ -103,15 +114,66 @@ function SignUp() {
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button onClick={async () => {
|
||||
const res = await auth.signUp.credential({
|
||||
await auth.signUp.username({
|
||||
email,
|
||||
password,
|
||||
name
|
||||
name,
|
||||
username
|
||||
})
|
||||
console.log(res)
|
||||
|
||||
}}>
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function SignIn() {
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
return (
|
||||
<div style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
borderRadius: "10px",
|
||||
border: "1px solid #4B453F",
|
||||
padding: "20px",
|
||||
marginTop: "10px"
|
||||
}}>
|
||||
<input type="email" id="email" placeholder="Email" style={{
|
||||
width: "100%",
|
||||
}}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
|
||||
<input type="password" id="password" placeholder="Password" style={{
|
||||
width: "100%"
|
||||
}}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button onClick={async () => {
|
||||
await auth.signIn.username({
|
||||
username: email,
|
||||
password,
|
||||
options: {
|
||||
onSuccess(context) {
|
||||
console.log({
|
||||
context
|
||||
})
|
||||
if (context.data.twoFactorRedirect) {
|
||||
alert("two factor required")
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
}}>
|
||||
Sign In
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import { createAuthClient } from "better-auth/react";
|
||||
import { twoFactorClient } from "better-auth/client";
|
||||
import { twoFactorClient, usernameClient } from "better-auth/client";
|
||||
|
||||
export const auth = createAuthClient({
|
||||
baseURL: "http://localhost:3000/api/auth",
|
||||
authPlugins: [twoFactorClient],
|
||||
authPlugins: [
|
||||
twoFactorClient({
|
||||
twoFactorPage: "/two-factor",
|
||||
}),
|
||||
usernameClient,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -10,14 +10,14 @@ import {
|
||||
getSession,
|
||||
resetPassword,
|
||||
sendVerificationEmail,
|
||||
signInCredential,
|
||||
signInEmail,
|
||||
signInOAuth,
|
||||
signOut,
|
||||
verifyEmail,
|
||||
} from "./routes";
|
||||
import { getCSRFToken } from "./routes/csrf";
|
||||
import { ok, welcome } from "./routes/ok";
|
||||
import { signUpCredential } from "./routes/sign-up";
|
||||
import { signUpEmail } from "./routes/sign-up";
|
||||
import { error } from "./routes/error";
|
||||
import type { z, ZodAny, ZodObject, ZodOptional, ZodString } from "zod";
|
||||
|
||||
@@ -85,8 +85,8 @@ export const router = <C extends AuthContext, Option extends BetterAuthOptions>(
|
||||
getCSRFToken,
|
||||
getSession: typedSession,
|
||||
signOut,
|
||||
signUpCredential,
|
||||
signInCredential,
|
||||
signUpEmail,
|
||||
signInEmail,
|
||||
forgetPassword,
|
||||
resetPassword,
|
||||
verifyEmail,
|
||||
@@ -136,10 +136,10 @@ export const router = <C extends AuthContext, Option extends BetterAuthOptions>(
|
||||
for (const hook of plugin.hooks.after) {
|
||||
const match = hook.matcher(context);
|
||||
if (match) {
|
||||
const hookRes = await hook.handler({
|
||||
...context,
|
||||
const obj = Object.assign(context, {
|
||||
returned: endpointRes,
|
||||
});
|
||||
const hookRes = await hook.handler(obj);
|
||||
if (hookRes && "response" in hookRes) {
|
||||
response = hookRes.response as any;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ export const signInOAuth = createAuthEndpoint(
|
||||
if (!provider) {
|
||||
throw new APIError("NOT_FOUND");
|
||||
}
|
||||
|
||||
const cookie = c.context.authCookies;
|
||||
const currentURL = c.query?.currentURL
|
||||
? new URL(c.query?.currentURL)
|
||||
@@ -78,8 +77,8 @@ export const signInOAuth = createAuthEndpoint(
|
||||
},
|
||||
);
|
||||
|
||||
export const signInCredential = createAuthEndpoint(
|
||||
"/sign-in/email-password",
|
||||
export const signInEmail = createAuthEndpoint(
|
||||
"/sign-in/email",
|
||||
{
|
||||
method: "POST",
|
||||
body: z.object({
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Argon2id } from "oslo/password";
|
||||
import { z } from "zod";
|
||||
import { createAuthEndpoint } from "../call";
|
||||
|
||||
export const signUpCredential = createAuthEndpoint(
|
||||
"/sign-up/credential",
|
||||
export const signUpEmail = createAuthEndpoint(
|
||||
"/sign-up/email",
|
||||
{
|
||||
method: "POST",
|
||||
body: z.object({
|
||||
|
||||
@@ -28,15 +28,25 @@ export const createAuthClient = <O extends ClientOptions = ClientOptions>(
|
||||
: {}) &
|
||||
Auth["api"]
|
||||
: Auth["api"];
|
||||
/**
|
||||
* used for plugins only
|
||||
*/
|
||||
const $baseFetch = createFetch({
|
||||
...options,
|
||||
baseURL: getBaseURL(options?.baseURL).withPath,
|
||||
});
|
||||
const $fetch = createFetch({
|
||||
method: "GET",
|
||||
...options,
|
||||
baseURL: getBaseURL(options?.baseURL).withPath,
|
||||
plugins: [
|
||||
...(options?.plugins || []),
|
||||
...(options?.authPlugins
|
||||
?.flatMap((plugin) => plugin($baseFetch).fetchPlugins)
|
||||
.filter((plugin) => plugin !== undefined) || []),
|
||||
...(options?.csrfPlugin !== false ? [csrfPlugin] : []),
|
||||
redirectPlugin,
|
||||
addCurrentURL,
|
||||
...(options?.csrfPlugin !== false ? [csrfPlugin] : []),
|
||||
...(options?.plugins || []),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -121,11 +131,10 @@ export const createAuthClient = <O extends ClientOptions = ClientOptions>(
|
||||
atom: "$activeOrgSignal",
|
||||
},
|
||||
{
|
||||
matcher: (path) => path === "/sign-out",
|
||||
atom: "$sessionSignal",
|
||||
},
|
||||
{
|
||||
matcher: (path) => path.startsWith("/sign-up"),
|
||||
matcher: (path) =>
|
||||
path === "/sign-out" ||
|
||||
path.startsWith("/sign-up") ||
|
||||
path.startsWith("/sign-in"),
|
||||
atom: "$sessionSignal",
|
||||
},
|
||||
...pluginProxySignals,
|
||||
|
||||
@@ -30,7 +30,13 @@ describe("client path to object", async () => {
|
||||
client.$atoms.$session;
|
||||
|
||||
const client2 = createReactClient({
|
||||
authPlugins: [organization, twoFactorClient, usernameClient],
|
||||
authPlugins: [
|
||||
organization,
|
||||
twoFactorClient({
|
||||
twoFactorPage: "/two-factor",
|
||||
}),
|
||||
usernameClient,
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BetterFetch } from "@better-fetch/fetch";
|
||||
import type { BetterFetch, BetterFetchPlugin } from "@better-fetch/fetch";
|
||||
import type { Endpoint } from "better-call";
|
||||
import type { AuthProxySignal } from "./proxy";
|
||||
import type { Atom, PreinitializedWritableAtom } from "nanostores";
|
||||
@@ -25,6 +25,7 @@ export const createClientPlugin = <E extends BetterAuthPlugin = never>() => {
|
||||
atoms?: Record<string, Atom<any>>;
|
||||
integrations?: Integrations;
|
||||
pathMethods?: Record<string, "POST" | "GET">;
|
||||
fetchPlugins?: BetterFetchPlugin[];
|
||||
},
|
||||
) => {
|
||||
return ($fetch: BetterFetch) => {
|
||||
|
||||
@@ -7,7 +7,6 @@ export const redirectPlugin = {
|
||||
hooks: {
|
||||
onSuccess(context) {
|
||||
if (context.data?.url && context.data?.redirect) {
|
||||
console.log("redirecting to", context.data.url);
|
||||
window.location.href = context.data.url;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -64,12 +64,18 @@ export function createDynamicPathProxy<T extends Record<string, any>>(
|
||||
body: method === "GET" ? undefined : body,
|
||||
query: query,
|
||||
method,
|
||||
onSuccess() {
|
||||
async onSuccess(context) {
|
||||
const signal = $signal?.find((s) => s.matcher(routePath));
|
||||
if (!signal) return;
|
||||
const signalAtom = $signals?.[signal.atom];
|
||||
if (!signalAtom) return;
|
||||
signalAtom.set(!signalAtom.get());
|
||||
/**
|
||||
* call if options.onSuccess
|
||||
* is passed since we are
|
||||
* overriding onSuccess
|
||||
*/
|
||||
await options?.onSuccess?.(context);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { BetterFetch, BetterFetchOption } from "@better-fetch/fetch";
|
||||
import type {
|
||||
BetterFetch,
|
||||
BetterFetchOption,
|
||||
BetterFetchPlugin,
|
||||
} from "@better-fetch/fetch";
|
||||
import type { Auth } from "../auth";
|
||||
import type { UnionToIntersection } from "../types/helper";
|
||||
import type { useAuthStore as reactStore } from "./react";
|
||||
@@ -30,6 +34,7 @@ export type AuthPlugin = ($fetch: BetterFetch) => {
|
||||
preact?: (useStore: typeof preactStore) => Record<string, any>;
|
||||
};
|
||||
pathMethods?: Record<string, "POST" | "GET">;
|
||||
fetchPlugins?: BetterFetchPlugin[];
|
||||
};
|
||||
export interface ClientOptions extends BetterFetchOption {
|
||||
/**
|
||||
|
||||
@@ -19,7 +19,7 @@ export const bearer = () => {
|
||||
?.startsWith("Bearer ") || false
|
||||
);
|
||||
},
|
||||
handler: createAuthMiddleware(async (ctx) => {
|
||||
handler: async (ctx) => {
|
||||
const token = ctx.request?.headers
|
||||
.get("authorization")
|
||||
?.replace("Bearer ", "");
|
||||
@@ -38,7 +38,7 @@ export const bearer = () => {
|
||||
ctx.context.authCookies.sessionToken.name
|
||||
}=${signedToken.replace("=", "")}`,
|
||||
);
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { z } from "zod";
|
||||
import { createAuthEndpoint } from "../../../api/call";
|
||||
import { sessionMiddleware } from "../../../api/middlewares/session";
|
||||
import { symmetricDecrypt, symmetricEncrypt } from "../../../crypto";
|
||||
import { verifyTwoFactorMiddleware } from "../two-fa-middleware";
|
||||
import { verifyTwoFactorMiddleware } from "../verify-middleware";
|
||||
import type { TwoFactorProvider, UserWithTwoFactor } from "../types";
|
||||
|
||||
export interface BackupCodeOptions {
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
import { createClientPlugin } from "../../client/create-client-plugin";
|
||||
import type { twoFactor as twoFa } from "../../plugins/two-factor";
|
||||
|
||||
export const twoFactorClient = createClientPlugin<ReturnType<typeof twoFa>>()(
|
||||
($fetch) => {
|
||||
export const twoFactorClient = (
|
||||
options: {
|
||||
twoFactorPage: string;
|
||||
/**
|
||||
* Redirect to the two factor page. If twoFactorPage
|
||||
* is not set this will redirect to the root path.
|
||||
* @default true
|
||||
*/
|
||||
redirect?: boolean;
|
||||
} = {
|
||||
redirect: true,
|
||||
twoFactorPage: "/",
|
||||
},
|
||||
) => {
|
||||
return createClientPlugin<ReturnType<typeof twoFa>>()(($fetch) => {
|
||||
return {
|
||||
id: "two-factor",
|
||||
authProxySignal: [
|
||||
@@ -18,6 +31,21 @@ export const twoFactorClient = createClientPlugin<ReturnType<typeof twoFa>>()(
|
||||
"/two-factor/enable": "POST",
|
||||
"/two-factor/send-otp": "POST",
|
||||
},
|
||||
fetchPlugins: [
|
||||
{
|
||||
id: "two-factor",
|
||||
name: "two-factor",
|
||||
hooks: {
|
||||
async onSuccess(context) {
|
||||
if (context.data?.twoFactorRedirect) {
|
||||
if (options.redirect) {
|
||||
window.location.href = options.twoFactorPage;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { alphabet, generateRandomString } from "oslo/crypto";
|
||||
import { z } from "zod";
|
||||
import { createAuthEndpoint } from "../../api/call";
|
||||
import { createAuthEndpoint, createAuthMiddleware } from "../../api/call";
|
||||
import { sessionMiddleware } from "../../api/middlewares/session";
|
||||
import { symmetricEncrypt } from "../../crypto";
|
||||
import { hs256, symmetricEncrypt } from "../../crypto";
|
||||
import type { BetterAuthPlugin } from "../../types/plugins";
|
||||
import { backupCode2fa, generateBackupCodes } from "./backup-codes";
|
||||
import { otp2fa } from "./otp";
|
||||
import { totp2fa } from "./totp";
|
||||
import {
|
||||
twoFactorMiddleware,
|
||||
verifyTwoFactorMiddleware,
|
||||
} from "./two-fa-middleware";
|
||||
|
||||
import type { TwoFactorOptions, UserWithTwoFactor } from "./types";
|
||||
import type { Session } from "../../adapters/schema";
|
||||
|
||||
export const twoFactor = <O extends TwoFactorOptions>(options: O) => {
|
||||
const totp = totp2fa({
|
||||
@@ -86,12 +84,65 @@ export const twoFactor = <O extends TwoFactorOptions>(options: O) => {
|
||||
),
|
||||
},
|
||||
options: options,
|
||||
middlewares: [
|
||||
{
|
||||
path: "/sign-in/credential",
|
||||
middleware: twoFactorMiddleware(options),
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
after: [
|
||||
{
|
||||
matcher(context) {
|
||||
return (
|
||||
context.path === "/sign-in/email" ||
|
||||
context.path === "/sign-in/username"
|
||||
);
|
||||
},
|
||||
handler: createAuthMiddleware(async (ctx) => {
|
||||
const returned = (await (ctx as any).returned) as Response;
|
||||
if (returned?.status !== 200) {
|
||||
return;
|
||||
}
|
||||
const response = (await returned.json()) as {
|
||||
user: UserWithTwoFactor;
|
||||
session: Session;
|
||||
};
|
||||
if (!response.user.twoFactorEnabled) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* remove the session cookie. It's set by the sign in credential
|
||||
*/
|
||||
ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", {
|
||||
path: "/",
|
||||
sameSite: "lax",
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
maxAge: 0,
|
||||
});
|
||||
const hash = await hs256(ctx.context.secret, response.session.id);
|
||||
/**
|
||||
* We set the user id and the session
|
||||
* id as a hash. Later will fetch for
|
||||
* sessions with the user id compare
|
||||
* the hash and set that as session.
|
||||
*/
|
||||
await ctx.setSignedCookie(
|
||||
"better-auth.two-factor",
|
||||
`${response.session.userId}!${hash}`,
|
||||
ctx.context.secret,
|
||||
ctx.context.authCookies.sessionToken.options,
|
||||
);
|
||||
const res = new Response(
|
||||
JSON.stringify({
|
||||
twoFactorRedirect: true,
|
||||
}),
|
||||
{
|
||||
headers: ctx.responseHeader,
|
||||
},
|
||||
);
|
||||
return {
|
||||
response: res,
|
||||
};
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
user: {
|
||||
fields: {
|
||||
|
||||
@@ -3,9 +3,8 @@ import { generateRandomInteger } from "oslo/crypto";
|
||||
import { generateHOTP } from "oslo/otp";
|
||||
import { z } from "zod";
|
||||
import { createAuthEndpoint } from "../../../api/call";
|
||||
import { sessionMiddleware } from "../../../api/middlewares/session";
|
||||
import { OTP_RANDOM_NUMBER_COOKIE_NAME } from "../constant";
|
||||
import { verifyTwoFactorMiddleware } from "../two-fa-middleware";
|
||||
import { verifyTwoFactorMiddleware } from "../verify-middleware";
|
||||
import type { TwoFactorProvider, UserWithTwoFactor } from "../types";
|
||||
|
||||
export interface OTPOptions {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createAuthEndpoint } from "../../../api/call";
|
||||
import { sessionMiddleware } from "../../../api/middlewares/session";
|
||||
import { symmetricDecrypt } from "../../../crypto";
|
||||
import type { BackupCodeOptions } from "../backup-codes";
|
||||
import { verifyTwoFactorMiddleware } from "../two-fa-middleware";
|
||||
import { verifyTwoFactorMiddleware } from "../verify-middleware";
|
||||
import type { TwoFactorProvider, UserWithTwoFactor } from "../types";
|
||||
|
||||
export type TOTPOptions = {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { APIError } from "better-call";
|
||||
import { z } from "zod";
|
||||
import type { Session } from "../../adapters/schema";
|
||||
import { createAuthMiddleware } from "../../api/call";
|
||||
import { signInCredential } from "../../api/routes";
|
||||
import { hs256 } from "../../crypto";
|
||||
import { TWO_FACTOR_COOKIE_NAME } from "./constant";
|
||||
import type { TwoFactorOptions, UserWithTwoFactor } from "./types";
|
||||
import type { UserWithTwoFactor } from "./types";
|
||||
|
||||
export const verifyTwoFactorMiddleware = createAuthMiddleware(async (ctx) => {
|
||||
const cookie = await ctx.getSignedCookie(
|
||||
@@ -107,69 +105,3 @@ export const verifyTwoFactorMiddleware = createAuthMiddleware(async (ctx) => {
|
||||
message: "invalid two factor authentication",
|
||||
});
|
||||
});
|
||||
|
||||
export const twoFactorMiddleware = (options: TwoFactorOptions) =>
|
||||
createAuthMiddleware(
|
||||
{
|
||||
body: z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
/**
|
||||
* Callback URL to
|
||||
* redirect to after
|
||||
* the user has signed in.
|
||||
*/
|
||||
callbackURL: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
async (ctx) => {
|
||||
//@ts-ignore
|
||||
const signIn = await signInCredential({
|
||||
...ctx,
|
||||
body: ctx.body,
|
||||
});
|
||||
if (!signIn?.user) {
|
||||
return new Response(null, {
|
||||
status: 401,
|
||||
});
|
||||
}
|
||||
const user = signIn.user as UserWithTwoFactor;
|
||||
if (!user.twoFactorEnabled) {
|
||||
return new Response(JSON.stringify(signIn), {
|
||||
headers: ctx.responseHeader,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* remove the session cookie. It's set by the sign in credential
|
||||
*/
|
||||
ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", {
|
||||
path: "/",
|
||||
sameSite: "lax",
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
maxAge: 0,
|
||||
});
|
||||
const hash = await hs256(ctx.context.secret, signIn.session.id);
|
||||
/**
|
||||
* We set the user id and the session
|
||||
* id as a hash. Later will fetch for
|
||||
* sessions with the user id compare
|
||||
* the hash and set that as session.
|
||||
*/
|
||||
await ctx.setSignedCookie(
|
||||
"better-auth.two-factor",
|
||||
`${signIn.session.userId}!${hash}`,
|
||||
ctx.context.secret,
|
||||
ctx.context.authCookies.sessionToken.options,
|
||||
);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
url: options.twoFactorURL || ctx.body.callbackURL || "/",
|
||||
redirect: true,
|
||||
}),
|
||||
{
|
||||
headers: ctx.responseHeader,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -4,14 +4,14 @@ import type { BetterAuthPlugin } from "../../types/plugins";
|
||||
import { Argon2id } from "oslo/password";
|
||||
import { APIError } from "better-call";
|
||||
import type { Account, User } from "../../adapters/schema";
|
||||
import { signUpCredential } from "../../api/routes/sign-up";
|
||||
import { signUpEmail } from "../../api/routes/sign-up";
|
||||
|
||||
export const username = () => {
|
||||
return {
|
||||
id: "username",
|
||||
endpoints: {
|
||||
signInUsername: createAuthEndpoint(
|
||||
"/sign-in/username-password",
|
||||
"/sign-in/username",
|
||||
{
|
||||
method: "POST",
|
||||
body: z.object({
|
||||
@@ -102,7 +102,7 @@ export const username = () => {
|
||||
{
|
||||
method: "POST",
|
||||
body: z.object({
|
||||
username: z.string().min(3).max(20).optional(),
|
||||
username: z.string().min(3).max(20),
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
@@ -111,7 +111,11 @@ export const username = () => {
|
||||
}),
|
||||
},
|
||||
async (ctx) => {
|
||||
const res = await signUpCredential(ctx);
|
||||
const res = await signUpEmail({
|
||||
...ctx,
|
||||
//@ts-expect-error
|
||||
_flag: undefined,
|
||||
});
|
||||
if (!res) {
|
||||
return ctx.json(null, {
|
||||
status: 400,
|
||||
@@ -121,9 +125,14 @@ export const username = () => {
|
||||
},
|
||||
});
|
||||
}
|
||||
await ctx.context.internalAdapter.updateUserByEmail(res.user.email, {
|
||||
username: ctx.body.username,
|
||||
});
|
||||
const updatedUser =
|
||||
await ctx.context.internalAdapter.updateUserByEmail(
|
||||
res.user.email,
|
||||
{
|
||||
username: ctx.body.username,
|
||||
},
|
||||
);
|
||||
console.log(updatedUser);
|
||||
return ctx.json(res);
|
||||
},
|
||||
),
|
||||
@@ -135,6 +144,8 @@ export const username = () => {
|
||||
username: {
|
||||
type: "string",
|
||||
required: false,
|
||||
unique: true,
|
||||
returned: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -26,23 +26,19 @@ export type BetterAuthPlugin = {
|
||||
hooks?: {
|
||||
before?: {
|
||||
matcher: (context: GenericEndpointContext) => boolean;
|
||||
handler: Endpoint<
|
||||
(context: GenericEndpointContext) => Promise<void | {
|
||||
context: Partial<GenericEndpointContext>;
|
||||
}>
|
||||
>;
|
||||
handler: (context: GenericEndpointContext) => Promise<void | {
|
||||
context: Partial<GenericEndpointContext>;
|
||||
}>;
|
||||
}[];
|
||||
after?: {
|
||||
matcher: (context: GenericEndpointContext) => boolean;
|
||||
handler: Endpoint<
|
||||
(
|
||||
context: GenericEndpointContext & {
|
||||
returned: EndpointResponse;
|
||||
},
|
||||
) => Promise<void | {
|
||||
response: EndpointResponse;
|
||||
}>
|
||||
>;
|
||||
handler: (
|
||||
context: GenericEndpointContext & {
|
||||
returned: EndpointResponse;
|
||||
},
|
||||
) => Promise<void | {
|
||||
response: EndpointResponse;
|
||||
}>;
|
||||
}[];
|
||||
};
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user