Skip to content

Commit 74e6353

Browse files
authored
fix: better-auth configuration (#29)
* fix: auth configuration * fix: stateless sign out * fix: replace all port * chore: removed unneeded configuration
1 parent f4c2d3e commit 74e6353

File tree

5 files changed

+79
-89
lines changed

5 files changed

+79
-89
lines changed

src/app/dashboard/page.tsx

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
1-
import { headers } from "next/headers";
2-
import { redirect } from "next/navigation";
3-
import { auth } from "@/lib/auth";
1+
"use client";
42

5-
export default async function DashboardPage() {
6-
const session = await auth.api.getSession({
7-
headers: await headers(),
8-
});
3+
import { useRouter } from "next/navigation";
4+
import { useEffect, useState } from "react";
5+
import { signOut, useSession } from "@/lib/auth-client";
96

10-
if (!session) {
11-
redirect("/login");
7+
export default function DashboardPage() {
8+
const router = useRouter();
9+
const { data: session, isPending } = useSession();
10+
const [isSigningOut, setIsSigningOut] = useState(false);
11+
12+
useEffect(() => {
13+
if (!isPending && !session) {
14+
router.push("/sign-in");
15+
}
16+
}, [session, isPending, router]);
17+
18+
const handleSignOut = async () => {
19+
setIsSigningOut(true);
20+
try {
21+
await signOut();
22+
} catch (error) {
23+
console.error("Sign-out error:", error);
24+
setIsSigningOut(false);
25+
}
26+
};
27+
28+
if (isPending || !session) {
29+
return null;
1230
}
1331

1432
return (
@@ -36,14 +54,14 @@ export default async function DashboardPage() {
3654
</div>
3755
</div>
3856

39-
<form action="/api/auth/sign-out" method="POST">
40-
<button
41-
type="submit"
42-
className="rounded-full bg-red-600 px-6 py-3 text-white transition-colors hover:bg-red-700"
43-
>
44-
Sign Out
45-
</button>
46-
</form>
57+
<button
58+
type="button"
59+
onClick={handleSignOut}
60+
disabled={isSigningOut}
61+
className="rounded-full bg-red-600 px-6 py-3 text-white transition-colors hover:bg-red-700 disabled:opacity-50"
62+
>
63+
{isSigningOut ? "Signing Out..." : "Sign Out"}
64+
</button>
4765
</main>
4866
</div>
4967
);

src/app/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ export default function Home() {
3333
) : (
3434
<div className="flex flex-col items-center gap-4">
3535
<p className="text-zinc-600 dark:text-zinc-400">
36-
Please log in to access the application
36+
Please sign in to access the application
3737
</p>
3838
<Link
39-
href="/login"
39+
href="/sign-in"
4040
className="rounded-full bg-black px-6 py-3 text-white transition-colors hover:bg-zinc-800 dark:bg-white dark:text-black dark:hover:bg-zinc-200"
4141
>
42-
Log In
42+
Sign In
4343
</Link>
4444
</div>
4545
)}
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
import { authClient } from "@/lib/auth-client";
44

5-
export default function LoginPage() {
6-
const handleOIDCLogin = async () => {
5+
const OIDC_PROVIDER_ID = process.env.NEXT_PUBLIC_OIDC_PROVIDER_ID || "oidc";
6+
const OIDC_PROVIDER_NAME =
7+
process.env.NEXT_PUBLIC_OIDC_PROVIDER_NAME || "OIDC Provider";
8+
9+
export default function SignInPage() {
10+
const handleOIDCSignIn = async () => {
711
try {
812
console.log("Initiating OIDC sign-in...");
913
const { data, error } = await authClient.signIn.oauth2({
10-
providerId: "oidc",
14+
providerId: OIDC_PROVIDER_ID,
1115
callbackURL: "/dashboard",
1216
});
1317

@@ -31,7 +35,7 @@ export default function LoginPage() {
3135
<div className="flex min-h-screen items-center justify-center bg-zinc-50 dark:bg-black">
3236
<main className="flex flex-col items-center gap-8 rounded-lg bg-white p-12 shadow-lg dark:bg-zinc-900">
3337
<h1 className="text-3xl font-bold text-black dark:text-white">
34-
Log In
38+
Sign In
3539
</h1>
3640

3741
<p className="text-center text-zinc-600 dark:text-zinc-400">
@@ -40,10 +44,10 @@ export default function LoginPage() {
4044

4145
<button
4246
type="button"
43-
onClick={handleOIDCLogin}
47+
onClick={handleOIDCSignIn}
4448
className="rounded-full bg-black px-8 py-3 text-white transition-colors hover:bg-zinc-800 dark:bg-white dark:text-black dark:hover:bg-zinc-200"
4549
>
46-
Sign In with OIDC
50+
Sign In with {OIDC_PROVIDER_NAME}
4751
</button>
4852
</main>
4953
</div>

src/lib/auth-client.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,18 @@ export const authClient = createAuthClient({
77
plugins: [genericOAuthClient()],
88
});
99

10-
// You can also export specific methods if you prefer
11-
export const { signIn, signOut, useSession } = authClient;
10+
export const { signIn, useSession } = authClient;
11+
12+
export const signOut = async (options?: { redirectTo?: string }) => {
13+
const redirectUri = options?.redirectTo || "/sign-in";
14+
15+
// Note: This does NOT logout from Okta SSO session
16+
// User will be automatically re-authenticated on next sign-in (SSO behavior)
17+
await authClient.signOut({
18+
fetchOptions: {
19+
onSuccess: () => {
20+
window.location.href = redirectUri;
21+
},
22+
},
23+
});
24+
};

src/lib/auth.ts

Lines changed: 16 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,41 @@
1+
import type { BetterAuthOptions } from "better-auth";
12
import { betterAuth } from "better-auth";
23
import { genericOAuth } from "better-auth/plugins";
34

4-
// Read from environment variables to support any OIDC provider
5-
const OIDC_ISSUER = process.env.OIDC_ISSUER_URL || "";
6-
const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID || "";
7-
const OIDC_CLIENT_SECRET = process.env.OIDC_CLIENT_SECRET || "";
8-
const BETTER_AUTH_SECRET =
9-
process.env.BETTER_AUTH_SECRET || "build-time-placeholder";
10-
const BETTER_AUTH_URL = process.env.BETTER_AUTH_URL || "http://localhost:3000";
5+
const OIDC_PROVIDER_ID = process.env.OIDC_PROVIDER_ID || "oidc";
6+
const OIDC_ISSUER = process.env.OIDC_ISSUER || "";
7+
const BASE_URL = process.env.BETTER_AUTH_URL || "http://localhost:3000";
118

12-
// Validate required environment variables (warnings only during build)
13-
const isBuild = process.env.NEXT_PHASE === "phase-production-build";
14-
15-
if (!process.env.BETTER_AUTH_SECRET) {
16-
const message =
17-
"[Better Auth] BETTER_AUTH_SECRET is required. Set it in .env.local to a strong, random value.";
18-
if (isBuild) {
19-
console.warn(message);
20-
} else {
21-
throw new Error(message);
22-
}
23-
}
24-
25-
if (!OIDC_ISSUER || !OIDC_CLIENT_ID || !OIDC_CLIENT_SECRET) {
26-
const message =
27-
"[Better Auth] OIDC configuration is incomplete. Set OIDC_ISSUER_URL, OIDC_CLIENT_ID, and OIDC_CLIENT_SECRET in .env.local";
28-
if (isBuild) {
29-
console.warn(message);
30-
} else {
31-
throw new Error(message);
32-
}
33-
}
34-
35-
console.log("[Better Auth] OIDC Configuration:", {
36-
issuer: OIDC_ISSUER,
37-
clientId: OIDC_CLIENT_ID,
38-
baseURL: BETTER_AUTH_URL,
39-
discoveryUrl: `${OIDC_ISSUER}/.well-known/openid-configuration`,
40-
callbackURL: `${BETTER_AUTH_URL}/api/auth/oauth2/callback/oidc`,
41-
});
42-
43-
// Configure trusted origins - defaults to localhost ports for development
44-
// Set TRUSTED_ORIGINS environment variable for production (comma-separated list)
459
const trustedOrigins = process.env.TRUSTED_ORIGINS
46-
? process.env.TRUSTED_ORIGINS.split(",").map((origin) => origin.trim())
47-
: [
48-
"http://localhost:3000",
49-
"http://localhost:3001",
50-
"http://localhost:3002",
51-
"http://localhost:3003",
52-
];
10+
? process.env.TRUSTED_ORIGINS.split(",").map((s) => s.trim())
11+
: [BASE_URL, "http://localhost:3002", "http://localhost:3003"];
5312

54-
// Always include BETTER_AUTH_URL if not already present
55-
if (BETTER_AUTH_URL && !trustedOrigins.includes(BETTER_AUTH_URL)) {
56-
trustedOrigins.push(BETTER_AUTH_URL);
13+
if (!trustedOrigins.includes(BASE_URL)) {
14+
trustedOrigins.push(BASE_URL);
5715
}
5816

5917
export const auth = betterAuth({
60-
secret: BETTER_AUTH_SECRET,
61-
baseURL: BETTER_AUTH_URL,
18+
secret: process.env.BETTER_AUTH_SECRET || "build-time-placeholder",
19+
baseURL: BASE_URL,
6220
trustedOrigins,
63-
// No database configuration - running in stateless mode
6421
session: {
65-
expiresIn: 60 * 60 * 24 * 7, // 7 days
6622
cookieCache: {
6723
enabled: true,
68-
maxAge: 30 * 24 * 60 * 60, // 30 days cache duration
69-
strategy: "jwe", // Use encrypted tokens for better security
70-
refreshCache: true, // Enable stateless refresh
7124
},
7225
},
7326
plugins: [
7427
genericOAuth({
7528
config: [
7629
{
77-
providerId: "oidc",
30+
providerId: OIDC_PROVIDER_ID,
7831
discoveryUrl: `${OIDC_ISSUER}/.well-known/openid-configuration`,
79-
clientId: OIDC_CLIENT_ID,
80-
clientSecret: OIDC_CLIENT_SECRET,
32+
redirectURI: `${BASE_URL}/api/auth/oauth2/callback/${OIDC_PROVIDER_ID}`,
33+
clientId: process.env.OIDC_CLIENT_ID || "",
34+
clientSecret: process.env.OIDC_CLIENT_SECRET || "",
8135
scopes: ["openid", "email", "profile"],
36+
pkce: true,
8237
},
8338
],
8439
}),
8540
],
86-
});
41+
} as BetterAuthOptions);

0 commit comments

Comments
 (0)