-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathauth.ts
More file actions
123 lines (112 loc) · 3.39 KB
/
auth.ts
File metadata and controls
123 lines (112 loc) · 3.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import NextAuth from "next-auth";
import type { NextAuthConfig, User } from "next-auth";
import "next-auth/jwt";
// Zitadel provider configuration
const zitadelProvider = {
id: "zitadel",
name: "Zitadel",
type: "oidc" as const,
issuer: process.env.ZITADEL_ISSUER || "http://localhost:8080",
clientId: process.env.ZITADEL_CLIENT_ID || "",
clientSecret: process.env.ZITADEL_CLIENT_SECRET || "",
authorization: {
params: {
scope: "openid profile email offline_access",
},
},
profile(profile: Record<string, unknown>) {
return {
id: profile.sub as string,
name: (profile.name || profile.preferred_username) as string,
email: profile.email as string,
image: profile.picture as string | undefined,
};
},
};
export const authConfig: NextAuthConfig = {
providers: [zitadelProvider],
events: {
async signOut(_message) {
// This event fires after local session is cleared
// The actual redirect to Zitadel's end_session happens in the component
},
},
callbacks: {
async jwt({ token, account, user }) {
// Initial sign in
if (account && user) {
return {
...token,
accessToken: account.access_token,
refreshToken: account.refresh_token,
expiresAt: account.expires_at,
user,
};
}
// Return previous token if the access token has not expired yet
if (Date.now() < ((token.expiresAt as number) ?? 0) * 1000) {
return token;
}
// Access token has expired, try to refresh it
if (token.refreshToken) {
try {
const response = await fetch(
`${process.env.ZITADEL_ISSUER}/oauth/v2/token`,
{
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: process.env.ZITADEL_CLIENT_ID || "",
client_secret: process.env.ZITADEL_CLIENT_SECRET || "",
grant_type: "refresh_token",
refresh_token: token.refreshToken as string,
}),
}
);
const tokens = await response.json();
if (!response.ok) throw tokens;
return {
...token,
accessToken: tokens.access_token,
expiresAt: Math.floor(Date.now() / 1000 + tokens.expires_in),
refreshToken: tokens.refresh_token ?? token.refreshToken,
};
} catch (error) {
console.error("Error refreshing access token", error);
return { ...token, error: "RefreshAccessTokenError" };
}
}
return token;
},
async session({ session, token }) {
session.accessToken = token.accessToken as string;
session.error = token.error as string | undefined;
if (token.user) {
Object.assign(session.user, token.user);
}
return session;
},
},
pages: {
signIn: "/auth/signin",
error: "/auth/error",
},
debug: process.env.NODE_ENV === "development",
};
export const { handlers, auth, signIn, signOut } = NextAuth(authConfig);
// Extend the types
declare module "next-auth" {
interface Session {
accessToken?: string;
error?: string;
}
}
declare module "next-auth/jwt" {
interface JWT {
accessToken?: string;
refreshToken?: string;
expiresAt?: number;
error?: string;
user?: User;
}
}