mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-07 20:37:44 +00:00
fix: add rate limter error
This commit is contained in:
@@ -74,7 +74,7 @@ export function OrganizationCard(props: { session: Session | null }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="rounded-none md:rounded-sm !bg-none border-none">
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Organization</CardTitle>
|
<CardTitle>Organization</CardTitle>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
import { auth } from "@/lib/auth"
|
import { auth } from "@/lib/auth";
|
||||||
import { headers } from "next/headers"
|
import { headers } from "next/headers";
|
||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation";
|
||||||
import UserCard from "./user-card"
|
import UserCard from "./user-card";
|
||||||
import { OrganizationCard } from "./organization-card"
|
import { OrganizationCard } from "./organization-card";
|
||||||
|
|
||||||
|
|
||||||
export default async function DashboardPage() {
|
export default async function DashboardPage() {
|
||||||
const [session, activeSessions] = await Promise.all([
|
const [session, activeSessions] = await Promise.all([
|
||||||
auth.api.getSession({
|
auth.api.getSession({
|
||||||
headers: headers()
|
headers: headers(),
|
||||||
}),
|
}),
|
||||||
auth.api.listSessions({
|
auth.api.listSessions({
|
||||||
headers: headers()
|
headers: headers(),
|
||||||
})
|
}),
|
||||||
]).catch(e => {
|
]).catch((e) => {
|
||||||
throw redirect("/sign-in")
|
throw redirect("/sign-in");
|
||||||
})
|
});
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex gap-4 flex-col">
|
<div className="flex gap-4 flex-col">
|
||||||
<UserCard session={JSON.parse(JSON.stringify(session))} activeSessions={JSON.parse(JSON.stringify(activeSessions))} />
|
<UserCard
|
||||||
<OrganizationCard session={JSON.parse(JSON.stringify(session))} />
|
session={JSON.parse(JSON.stringify(session))}
|
||||||
</div>
|
activeSessions={JSON.parse(JSON.stringify(activeSessions))}
|
||||||
</div>
|
/>
|
||||||
)
|
<OrganizationCard session={JSON.parse(JSON.stringify(session))} />
|
||||||
}
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default function UserCard(props: {
|
|||||||
const [isSignOut, setIsSignOut] = useState<boolean>(false);
|
const [isSignOut, setIsSignOut] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="rounded-none md:rounded-sm !bg-none border-none">
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>User</CardTitle>
|
<CardTitle>User</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@@ -407,6 +407,7 @@ function ChangePassword() {
|
|||||||
toast.error("Passwords do not match");
|
toast.error("Passwords do not match");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
a;
|
||||||
if (newPassword.length < 8) {
|
if (newPassword.length < 8) {
|
||||||
toast.error("Password must be at least 8 characters");
|
toast.error("Password must be at least 8 characters");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
passkeyClient,
|
passkeyClient,
|
||||||
twoFactorClient,
|
twoFactorClient,
|
||||||
} from "better-auth/client/plugins";
|
} from "better-auth/client/plugins";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export const client = createAuthClient({
|
export const client = createAuthClient({
|
||||||
plugins: [
|
plugins: [
|
||||||
@@ -14,7 +15,11 @@ export const client = createAuthClient({
|
|||||||
passkeyClient(),
|
passkeyClient(),
|
||||||
],
|
],
|
||||||
fetchOptions: {
|
fetchOptions: {
|
||||||
credentials: "include",
|
onError(e) {
|
||||||
|
if (e.error.status === 429) {
|
||||||
|
toast.error("Too many requests. Please try again later.");
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export const auth = betterAuth({
|
|||||||
plugins: [
|
plugins: [
|
||||||
rateLimiter({
|
rateLimiter({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|
||||||
|
max: 1000,
|
||||||
}),
|
}),
|
||||||
organization({
|
organization({
|
||||||
async sendInvitationEmail(data) {
|
async sendInvitationEmail(data) {
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export const getSession = <Option extends BetterAuthOptions>() =>
|
|||||||
requireHeaders: true,
|
requireHeaders: true,
|
||||||
},
|
},
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
console.log("called");
|
|
||||||
try {
|
try {
|
||||||
const sessionCookieToken = await ctx.getSignedCookie(
|
const sessionCookieToken = await ctx.getSignedCookie(
|
||||||
ctx.context.authCookies.sessionToken.name,
|
ctx.context.authCookies.sessionToken.name,
|
||||||
@@ -213,7 +212,6 @@ export const revokeSession = createAuthEndpoint(
|
|||||||
},
|
},
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
const id = ctx.body.id;
|
const id = ctx.body.id;
|
||||||
console.log(id);
|
|
||||||
try {
|
try {
|
||||||
await ctx.context.internalAdapter.deleteSession(id);
|
await ctx.context.internalAdapter.deleteSession(id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const getClientConfig = <O extends ClientOptions>(options?: O) => {
|
|||||||
.filter((pl) => pl !== undefined) || []),
|
.filter((pl) => pl !== undefined) || []),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const plugins = options?.plugins || [];
|
const plugins = options?.plugins || [];
|
||||||
let pluginsActions = {} as Record<string, any>;
|
let pluginsActions = {} as Record<string, any>;
|
||||||
let pluginsAtoms = {} as Record<string, Atom<any>>;
|
let pluginsAtoms = {} as Record<string, Atom<any>>;
|
||||||
|
|||||||
@@ -61,11 +61,17 @@ export const csrfPlugin = {
|
|||||||
"CSRF route not found. Make sure the server is running and the base URL is correct and includes the path (e.g. http://localhost:3000/api/auth).",
|
"CSRF route not found. Make sure the server is running and the base URL is correct and includes the path (e.g. http://localhost:3000/api/auth).",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.status === 429) {
|
if (error.status === 429) {
|
||||||
return new Response(null, {
|
return new Response(
|
||||||
status: 429,
|
JSON.stringify({
|
||||||
statusText: "Too Many Requests",
|
message: "Too many requests. Please try again later.",
|
||||||
});
|
}),
|
||||||
|
{
|
||||||
|
status: 429,
|
||||||
|
statusText: "Too Many Requests",
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
throw new BetterAuthError(
|
throw new BetterAuthError(
|
||||||
"Failed to fetch CSRF token: " + error.message,
|
"Failed to fetch CSRF token: " + error.message,
|
||||||
|
|||||||
@@ -207,22 +207,27 @@ export const rateLimiter = (options: RateLimitOptions) => {
|
|||||||
rateLimit.lastRequest >= windowStart &&
|
rateLimit.lastRequest >= windowStart &&
|
||||||
rateLimit.count >= opts.max
|
rateLimit.count >= opts.max
|
||||||
) {
|
) {
|
||||||
return new Response(null, {
|
return new Response(
|
||||||
status: 429,
|
JSON.stringify({
|
||||||
statusText: "Too Many Requests",
|
message: "Too many requests. Please try again later.",
|
||||||
headers: {
|
}),
|
||||||
"X-RateLimit-Window": opts.window.toString(),
|
{
|
||||||
"X-RateLimit-Max": opts.max.toString(),
|
status: 429,
|
||||||
"X-RateLimit-Remaining": (
|
statusText: "Too Many Requests",
|
||||||
opts.max - rateLimit.count
|
headers: {
|
||||||
).toString(),
|
"X-RateLimit-Window": opts.window.toString(),
|
||||||
"X-RateLimit-Reset": (
|
"X-RateLimit-Max": opts.max.toString(),
|
||||||
rateLimit.lastRequest +
|
"X-RateLimit-Remaining": (
|
||||||
opts.window * 1000 -
|
opts.max - rateLimit.count
|
||||||
now
|
).toString(),
|
||||||
).toString(),
|
"X-RateLimit-Reset": (
|
||||||
|
rateLimit.lastRequest +
|
||||||
|
opts.window * 1000 -
|
||||||
|
now
|
||||||
|
).toString(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rateLimit.lastRequest < windowStart) {
|
if (rateLimit.lastRequest < windowStart) {
|
||||||
|
|||||||
1
todo.md
1
todo.md
@@ -12,6 +12,7 @@
|
|||||||
[x] fix the issue with the client triggers not working fot 2 consecutive calls
|
[x] fix the issue with the client triggers not working fot 2 consecutive calls
|
||||||
[ ] add a way to specify on the client to use the base path as the whole path
|
[ ] add a way to specify on the client to use the base path as the whole path
|
||||||
[x] implement magic links
|
[x] implement magic links
|
||||||
|
[ ] rate limiter error handling
|
||||||
|
|
||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|||||||
Reference in New Issue
Block a user