feat(auth): implement Gitea authentication provider with NextAuth integration

This commit is contained in:
ngc2207 2024-12-07 21:01:22 +08:00
parent 12e37e908f
commit b04613784a
5 changed files with 93 additions and 0 deletions

View File

@ -11,8 +11,10 @@
"dependencies": {
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"gitea-js": "^1.22.0",
"lucide-react": "^0.468.0",
"next": "15.0.4",
"next-auth": "^5.0.0-beta.25",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwind-merge": "^2.5.5",

View File

@ -0,0 +1,3 @@
import { handlers } from "@/auth";
export const { GET, POST } = handlers;

15
src/auth.ts Normal file
View File

@ -0,0 +1,15 @@
import NextAuth from "next-auth";
import Gitea from "@/lib/providers/gitea";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Gitea({
clientId: process.env.AUTH_GITEA_ID,
clientSecret: process.env.AUTH_GITEA_SECRET,
enterprise: {
baseUrl: process.env.AUTH_GITEA_URL,
},
}),
],
secret: process.env.AUTH_SECRET,
});

View File

@ -0,0 +1,72 @@
import { Email as GiteaEmail, User } from "gitea-js";
import { OAuthConfig, OAuthUserConfig } from "next-auth/providers";
export interface GiteaProfile extends User {
username: string;
}
export default function Gitea(
config: OAuthUserConfig<GiteaProfile> & {
enterprise?: {
baseUrl?: string;
};
}
): OAuthConfig<GiteaProfile> {
const baseUrl = config?.enterprise?.baseUrl ?? "https://gitea.com";
const apiBaseUrl = config?.enterprise?.baseUrl
? `${config?.enterprise?.baseUrl}/api/v1`
: "https://gitea.com/api/v1";
return {
id: "gitea",
name: "Gitea",
type: "oauth",
authorization: {
url: `${baseUrl}/login/oauth/authorize`,
params: { scope: "read:user user:email" },
},
token: `${baseUrl}/login/oauth/access_token`,
userinfo: {
url: `${apiBaseUrl}/user`,
async request({
tokens,
provider,
}: {
tokens: { access_token: string };
provider: OAuthConfig<GiteaProfile>;
}) {
const profile = await fetch(provider.userinfo?.url as URL, {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
"User-Agent": "authjs",
},
}).then(async (res) => await res.json());
if (!profile.email) {
const res = await fetch(`${apiBaseUrl}/user/emails`, {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
"User-Agent": "authjs",
},
});
if (res.ok) {
const emails: GiteaEmail[] = await res.json();
profile.email = (emails.find((e) => e.primary) ?? emails[0]).email;
}
}
return profile;
},
},
profile(profile) {
return {
id: profile.id?.toString(),
name: profile.username ?? profile.login,
email: profile.email,
image: profile.avatar_url,
};
},
options: config,
};
}

1
src/middleware.ts Normal file
View File

@ -0,0 +1 @@
export { auth as middleware } from "@/auth";