feat(auth): implement Gitea authentication provider with NextAuth integration
This commit is contained in:
parent
12e37e908f
commit
b04613784a
@ -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",
|
||||
|
3
src/app/api/auth/[...nextauth]/route.ts
Normal file
3
src/app/api/auth/[...nextauth]/route.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { handlers } from "@/auth";
|
||||
|
||||
export const { GET, POST } = handlers;
|
15
src/auth.ts
Normal file
15
src/auth.ts
Normal 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,
|
||||
});
|
72
src/lib/providers/gitea.ts
Normal file
72
src/lib/providers/gitea.ts
Normal 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
1
src/middleware.ts
Normal file
@ -0,0 +1 @@
|
||||
export { auth as middleware } from "@/auth";
|
Loading…
Reference in New Issue
Block a user