220 lines
6.1 KiB
TypeScript
220 lines
6.1 KiB
TypeScript
import NextAuth, { NextAuthOptions } from "next-auth";
|
|
import CredentialsProvider from "next-auth/providers/credentials";
|
|
import GithubProvider from "next-auth/providers/github";
|
|
import GoogleProvider from "next-auth/providers/google";
|
|
import { JWT } from "next-auth/jwt";
|
|
|
|
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000/api/v1";
|
|
|
|
// Django REST API - Token Refresh
|
|
async function refreshAccessToken(token: JWT) {
|
|
try {
|
|
const response = await fetch(
|
|
`${API_BASE_URL}/auth/jwt/refresh/`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
refresh: token.refreshToken,
|
|
}),
|
|
}
|
|
);
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw data;
|
|
}
|
|
|
|
return {
|
|
...token,
|
|
accessToken: data.access,
|
|
refreshToken: data.refresh ?? token.refreshToken,
|
|
// Django JWT access token: 60 dakika (3600000 ms)
|
|
accessTokenExpiry: Date.now() + 3600000,
|
|
};
|
|
} catch (error) {
|
|
console.error("Error refreshing access token:", error);
|
|
return {
|
|
...token,
|
|
error: "RefreshAccessTokenError",
|
|
};
|
|
}
|
|
}
|
|
|
|
// Django REST API - Social Auth Handler
|
|
async function handleSocialAuth(provider: string, accessToken: string) {
|
|
const response = await fetch(
|
|
`${API_BASE_URL}/auth/social/${provider}/`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
access_token: accessToken,
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new Error(error.error || "Social authentication failed");
|
|
}
|
|
|
|
return await response.json();
|
|
}
|
|
|
|
export const authOptions: NextAuthOptions = {
|
|
providers: [
|
|
GithubProvider({
|
|
clientId: process.env.GITHUB_ID || "",
|
|
clientSecret: process.env.GITHUB_SECRET || "",
|
|
}),
|
|
GoogleProvider({
|
|
clientId: process.env.GOOGLE_ID || "",
|
|
clientSecret: process.env.GOOGLE_SECRET || "",
|
|
}),
|
|
CredentialsProvider({
|
|
name: "Django REST API",
|
|
credentials: {
|
|
email: { label: "Email", type: "email" },
|
|
password: { label: "Password", type: "password" },
|
|
},
|
|
async authorize(credentials) {
|
|
if (!credentials?.email || !credentials?.password) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
// Django REST API - Login
|
|
const response = await fetch(
|
|
`${API_BASE_URL}/auth/jwt/create/`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
email: credentials.email,
|
|
password: credentials.password,
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
return null;
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data?.access && data?.refresh) {
|
|
// Get user profile
|
|
const userResponse = await fetch(
|
|
`${API_BASE_URL}/auth/users/me/`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${data.access}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
const userData = await userResponse.json();
|
|
|
|
return {
|
|
id: userData.id?.toString() || credentials.email,
|
|
email: userData.email,
|
|
name: `${userData.first_name || ""} ${userData.last_name || ""}`.trim(),
|
|
accessToken: data.access,
|
|
refreshToken: data.refresh,
|
|
accessTokenExpiry: Date.now() + 3600000, // 60 dakika
|
|
};
|
|
}
|
|
|
|
return null;
|
|
} catch (error) {
|
|
console.error("Authentication error:", error);
|
|
return null;
|
|
}
|
|
},
|
|
}),
|
|
],
|
|
callbacks: {
|
|
async jwt({ token, user, account }) {
|
|
// İlk login - Email/Password (Credentials Provider)
|
|
if (user && account?.provider === "credentials") {
|
|
token.accessToken = user.accessToken;
|
|
token.refreshToken = user.refreshToken;
|
|
token.accessTokenExpiry = user.accessTokenExpiry;
|
|
return token;
|
|
}
|
|
|
|
// İlk login - Social Auth (Google/GitHub)
|
|
if (account && (account.provider === "google" || account.provider === "github")) {
|
|
try {
|
|
const providerMap: Record<string, string> = {
|
|
google: "google-oauth2",
|
|
github: "github",
|
|
};
|
|
|
|
const djangoProvider = providerMap[account.provider];
|
|
const socialData = await handleSocialAuth(
|
|
djangoProvider,
|
|
account.access_token!
|
|
);
|
|
|
|
token.accessToken = socialData.access;
|
|
token.refreshToken = socialData.refresh;
|
|
token.accessTokenExpiry = Date.now() + 3600000;
|
|
token.email = socialData.user.email;
|
|
token.name = `${socialData.user.first_name || ""} ${socialData.user.last_name || ""}`.trim();
|
|
|
|
return token;
|
|
} catch (error) {
|
|
console.error("Social auth error:", error);
|
|
token.error = "SocialAuthError";
|
|
return token;
|
|
}
|
|
}
|
|
|
|
// Token hala geçerliyse mevcut tokeni döndür
|
|
if (token.accessTokenExpiry && Date.now() < (token.accessTokenExpiry as number)) {
|
|
return token;
|
|
}
|
|
|
|
// Token süresi dolmuşsa refresh et
|
|
return refreshAccessToken(token);
|
|
},
|
|
async session({ session, token }) {
|
|
session.accessToken = token.accessToken as string;
|
|
session.refreshToken = token.refreshToken as string;
|
|
session.error = token.error as string | undefined;
|
|
|
|
if (token.email) {
|
|
session.user = {
|
|
...session.user,
|
|
email: token.email as string,
|
|
name: token.name as string,
|
|
};
|
|
}
|
|
|
|
return session;
|
|
},
|
|
},
|
|
pages: {
|
|
signIn: "/auth/login",
|
|
error: "/auth/error",
|
|
},
|
|
session: {
|
|
strategy: "jwt",
|
|
maxAge: 7 * 24 * 60 * 60, // 7 gün (refresh token süresi)
|
|
},
|
|
secret: process.env.NEXTAUTH_SECRET,
|
|
};
|
|
|
|
const handler = NextAuth(authOptions);
|
|
|
|
export { handler as GET, handler as POST };
|