2024-03-20 17:51:21 +01:00
|
|
|
import type {
|
|
|
|
|
GetServerSidePropsContext,
|
|
|
|
|
NextApiRequest,
|
|
|
|
|
NextApiResponse,
|
|
|
|
|
} from "next";
|
|
|
|
|
import {
|
|
|
|
|
NextAuthOptions,
|
|
|
|
|
getServerSession as internalGetServerSession,
|
|
|
|
|
} from "next-auth";
|
|
|
|
|
import Google from "next-auth/providers/google";
|
|
|
|
|
import Credentials from "next-auth/providers/credentials";
|
|
|
|
|
import Apple from "next-auth/providers/apple";
|
2025-01-20 11:25:13 +01:00
|
|
|
import AzureADProvider from "next-auth/providers/azure-ad";
|
2025-08-20 11:37:39 +02:00
|
|
|
import { createLogger } from "@link-stack/logger";
|
|
|
|
|
|
|
|
|
|
const logger = createLogger('link-authentication');
|
2024-03-20 17:51:21 +01:00
|
|
|
|
|
|
|
|
const headers = { Authorization: `Token ${process.env.ZAMMAD_API_TOKEN}` };
|
|
|
|
|
|
|
|
|
|
const fetchRoles = async () => {
|
|
|
|
|
const url = `${process.env.ZAMMAD_URL}/api/v1/roles`;
|
|
|
|
|
const res = await fetch(url, { headers });
|
|
|
|
|
const roles = await res.json();
|
|
|
|
|
const formattedRoles = roles.reduce((acc: any, role: any) => {
|
|
|
|
|
acc[role.id] = role.name;
|
|
|
|
|
return acc;
|
|
|
|
|
}, {});
|
|
|
|
|
return formattedRoles;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const fetchUser = async (email: string) => {
|
2025-11-10 14:55:22 +01:00
|
|
|
const url = `${process.env.ZAMMAD_URL}/api/v1/users/search?query=${encodeURIComponent(`login:${email}`)}&limit=1`;
|
2024-03-20 17:51:21 +01:00
|
|
|
const res = await fetch(url, { headers });
|
|
|
|
|
const users = await res.json();
|
|
|
|
|
const user = users?.[0];
|
|
|
|
|
|
|
|
|
|
return user;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getUserRoles = async (email: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const user = await fetchUser(email);
|
2025-01-15 14:15:02 +01:00
|
|
|
if (!user) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2024-03-20 17:51:21 +01:00
|
|
|
const allRoles = await fetchRoles();
|
|
|
|
|
const roles = user.role_ids.map((roleID: number) => {
|
|
|
|
|
const role = allRoles[roleID];
|
|
|
|
|
return role ? role.toLowerCase().replace(" ", "_") : null;
|
|
|
|
|
});
|
|
|
|
|
return roles.filter((role: string) => role !== null);
|
|
|
|
|
} catch (e) {
|
2025-08-20 11:37:39 +02:00
|
|
|
logger.error({ error: e, email }, 'Failed to get user roles');
|
2024-03-20 17:51:21 +01:00
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const login = async (email: string, password: string) => {
|
|
|
|
|
const url = `${process.env.ZAMMAD_URL}/api/v1/users/me`;
|
|
|
|
|
const authorization =
|
|
|
|
|
"Basic " + Buffer.from(email + ":" + password).toString("base64");
|
|
|
|
|
const res = await fetch(url, {
|
|
|
|
|
headers: {
|
|
|
|
|
authorization,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const user = await res.json();
|
|
|
|
|
|
|
|
|
|
if (user && !user.error && user.id) {
|
|
|
|
|
return user;
|
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-09-27 14:52:44 +02:00
|
|
|
const providers = [];
|
|
|
|
|
|
|
|
|
|
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
|
|
|
|
|
providers.push(
|
2024-03-20 17:51:21 +01:00
|
|
|
Google({
|
|
|
|
|
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
|
|
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
|
|
|
}),
|
2024-09-27 14:52:44 +02:00
|
|
|
);
|
|
|
|
|
} else if (process.env.APPLE_CLIENT_ID && process.env.APPLE_CLIENT_SECRET) {
|
|
|
|
|
providers.push(
|
2024-03-20 17:51:21 +01:00
|
|
|
Apple({
|
|
|
|
|
clientId: process.env.APPLE_CLIENT_ID,
|
|
|
|
|
clientSecret: process.env.APPLE_CLIENT_SECRET,
|
|
|
|
|
}),
|
2024-09-27 14:52:44 +02:00
|
|
|
);
|
2025-01-20 11:25:13 +01:00
|
|
|
} else if (
|
|
|
|
|
process.env.AZURE_AD_CLIENT_ID &&
|
|
|
|
|
process.env.AZURE_AD_CLIENT_SECRET &&
|
|
|
|
|
process.env.AZURE_AD_TENANT_ID
|
|
|
|
|
) {
|
|
|
|
|
providers.push(
|
|
|
|
|
AzureADProvider({
|
|
|
|
|
clientId: process.env.AZURE_AD_CLIENT_ID,
|
|
|
|
|
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
|
|
|
|
|
tenantId: process.env.AZURE_AD_TENANT_ID,
|
|
|
|
|
}),
|
|
|
|
|
);
|
2024-09-27 14:52:44 +02:00
|
|
|
} else {
|
|
|
|
|
providers.push(
|
2024-03-20 17:51:21 +01:00
|
|
|
Credentials({
|
|
|
|
|
name: "Zammad",
|
|
|
|
|
credentials: {
|
|
|
|
|
email: { label: "Email", type: "text" },
|
|
|
|
|
password: { label: "Password", type: "password" },
|
|
|
|
|
},
|
2024-08-07 12:02:33 +02:00
|
|
|
async authorize(credentials) {
|
2024-03-20 17:51:21 +01:00
|
|
|
const user = await login(credentials.email, credentials.password);
|
|
|
|
|
if (user) {
|
|
|
|
|
return user;
|
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}),
|
2024-09-27 14:52:44 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const authOptions: NextAuthOptions = {
|
|
|
|
|
pages: {
|
2025-11-10 14:55:22 +01:00
|
|
|
signIn: "/login",
|
|
|
|
|
error: "/login",
|
|
|
|
|
signOut: "/logout",
|
2024-09-27 14:52:44 +02:00
|
|
|
},
|
|
|
|
|
providers,
|
2024-11-25 12:20:49 +01:00
|
|
|
session: {
|
2024-12-13 16:37:20 +01:00
|
|
|
maxAge: 3 * 24 * 60 * 60,
|
2024-11-25 12:20:49 +01:00
|
|
|
},
|
2024-03-20 17:51:21 +01:00
|
|
|
secret: process.env.NEXTAUTH_SECRET,
|
|
|
|
|
callbacks: {
|
2024-08-07 12:02:33 +02:00
|
|
|
signIn: async ({ user }) => {
|
2024-03-20 17:51:21 +01:00
|
|
|
const roles = (await getUserRoles(user.email)) ?? [];
|
2024-09-04 12:09:28 +02:00
|
|
|
return roles.includes("admin") || roles.includes("agent");
|
2024-03-20 17:51:21 +01:00
|
|
|
},
|
2024-08-07 12:02:33 +02:00
|
|
|
session: async ({ session, token }) => {
|
2024-03-20 17:51:21 +01:00
|
|
|
// @ts-ignore
|
|
|
|
|
session.user.roles = token.roles ?? [];
|
2024-10-09 12:21:06 +02:00
|
|
|
// @ts-ignore
|
|
|
|
|
session.user.zammadCsrfToken = token.zammadCsrfToken;
|
2024-08-05 23:31:15 +02:00
|
|
|
|
2024-03-20 17:51:21 +01:00
|
|
|
return session;
|
|
|
|
|
},
|
2024-10-09 12:21:06 +02:00
|
|
|
jwt: async ({ token, user, trigger, session }) => {
|
2024-03-20 17:51:21 +01:00
|
|
|
if (user) {
|
|
|
|
|
token.roles = (await getUserRoles(user.email)) ?? [];
|
|
|
|
|
}
|
2024-08-05 23:31:15 +02:00
|
|
|
|
2024-10-09 12:21:06 +02:00
|
|
|
if (session && trigger === "update") {
|
|
|
|
|
token.zammadCsrfToken = session.zammadCsrfToken;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-20 17:51:21 +01:00
|
|
|
return token;
|
|
|
|
|
},
|
|
|
|
|
},
|
2024-08-05 23:31:15 +02:00
|
|
|
};
|
2024-03-20 17:51:21 +01:00
|
|
|
|
|
|
|
|
export const getServerSession = (
|
|
|
|
|
...args:
|
|
|
|
|
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
|
|
|
|
|
| [NextApiRequest, NextApiResponse]
|
|
|
|
|
| []
|
|
|
|
|
) => internalGetServerSession(...args, authOptions);
|