Astro กับ Auth.js: Authentication ใน Astro
#astro13 เม.ย. 2569
Astro กับ Auth.js: Authentication ใน Astro
Auth.js (เดิมชื่อ NextAuth.js) เป็น authentication library ที่ได้รับความนิยมสูงสุดใน JavaScript ecosystem ปัจจุบันรองรับ Astro อย่างเป็นทางการ ทำให้การเพิ่ม authentication ให้กับ Astro project เป็นเรื่องง่ายและปลอดภัย
ทำไมต้องใช้ Auth.js?
- Multiple Providers: รองรับ OAuth providers มากกว่า 50 ตัว
- Session Management: จัดการ session อย่างปลอดภัย
- Database Adapters: รองรับ database หลายประเภท
- TypeScript: Type-safe ตั้งแต่ต้น
- Security: จัดการ CSRF, JWT, และ security best practices
การติดตั้ง
npm install auth-astro @auth/core
แก้ไข astro.config.mjs:
import { defineConfig } from 'astro/config';
import auth from 'auth-astro';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
integrations: [auth()],
});
กำหนด Auth Configuration
สร้าง auth.config.ts ที่ root:
import GitHub from '@auth/core/providers/github';
import Google from '@auth/core/providers/google';
import Credentials from '@auth/core/providers/credentials';
import { defineConfig } from 'auth-astro';
export default defineConfig({
providers: [
GitHub({
clientId: import.meta.env.GITHUB_CLIENT_ID,
clientSecret: import.meta.env.GITHUB_CLIENT_SECRET,
}),
Google({
clientId: import.meta.env.GOOGLE_CLIENT_ID,
clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET,
}),
Credentials({
name: 'Email & Password',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
// ตรวจสอบ credentials กับ database
const user = await verifyUser(
credentials.email as string,
credentials.password as string
);
return user ?? null;
},
}),
],
callbacks: {
async session({ session, token }) {
if (session.user && token.sub) {
session.user.id = token.sub;
}
return session;
},
async jwt({ token, user }) {
if (user) {
token.role = (user as any).role;
}
return token;
},
},
pages: {
signIn: '/auth/signin',
signOut: '/auth/signout',
error: '/auth/error',
},
});
async function verifyUser(email: string, password: string) {
// implement your user verification logic
return null;
}
Environment Variables
# .env
AUTH_SECRET=your-secret-key-here
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
Sign In Page
สร้าง src/pages/auth/signin.astro:
---
import { getSession } from 'auth-astro/server';
const session = await getSession(Astro.request);
if (session) {
return Astro.redirect('/');
}
---
<html lang="th">
<head>
<title>เข้าสู่ระบบ</title>
</head>
<body>
<div class="auth-container">
<h1>เข้าสู่ระบบ</h1>
<!-- OAuth Providers -->
<div class="providers">
<button id="github-signin" class="btn-github">
เข้าสู่ระบบด้วย GitHub
</button>
<button id="google-signin" class="btn-google">
เข้าสู่ระบบด้วย Google
</button>
</div>
<!-- Credentials Form -->
<form id="credentials-form">
<input type="email" name="email" placeholder="อีเมล" required />
<input type="password" name="password" placeholder="รหัสผ่าน" required />
<button type="submit">เข้าสู่ระบบ</button>
</form>
</div>
<script>
import { signIn } from 'auth-astro/client';
document.getElementById('github-signin')?.addEventListener('click', () => {
signIn('github', { callbackUrl: '/' });
});
document.getElementById('google-signin')?.addEventListener('click', () => {
signIn('google', { callbackUrl: '/' });
});
document.getElementById('credentials-form')?.addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const email = (form.elements.namedItem('email') as HTMLInputElement).value;
const password = (form.elements.namedItem('password') as HTMLInputElement).value;
await signIn('credentials', { email, password, callbackUrl: '/' });
});
</script>
</body>
</html>
Protected Pages
---
// src/pages/dashboard.astro
import { getSession } from 'auth-astro/server';
const session = await getSession(Astro.request);
if (!session) {
return Astro.redirect('/auth/signin');
}
---
<html lang="th">
<body>
<h1>ยินดีต้อนรับ, {session.user?.name}!</h1>
<p>อีเมล: {session.user?.email}</p>
<img src={session.user?.image ?? ''} alt="Profile" />
<button id="signout">ออกจากระบบ</button>
<script>
import { signOut } from 'auth-astro/client';
document.getElementById('signout')?.addEventListener('click', () => {
signOut({ callbackUrl: '/' });
});
</script>
</body>
</html>
Middleware สำหรับ Route Protection
สร้าง src/middleware.ts:
import { defineMiddleware } from 'astro:middleware';
import { getSession } from 'auth-astro/server';
const protectedRoutes = ['/dashboard', '/profile', '/admin'];
const adminRoutes = ['/admin'];
export const onRequest = defineMiddleware(async (context, next) => {
const { pathname } = context.url;
const isProtected = protectedRoutes.some((route) =>
pathname.startsWith(route)
);
if (isProtected) {
const session = await getSession(context.request);
if (!session) {
return context.redirect(`/auth/signin?callbackUrl=${pathname}`);
}
const isAdmin = adminRoutes.some((route) => pathname.startsWith(route));
if (isAdmin && (session.user as any)?.role !== 'admin') {
return context.redirect('/403');
}
context.locals.session = session;
context.locals.user = session.user;
}
return next();
});
Database Adapter
ใช้ Prisma adapter:
npm install @auth/prisma-adapter
// auth.config.ts
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from './src/lib/prisma';
export default defineConfig({
adapter: PrismaAdapter(prisma),
providers: [...],
session: { strategy: 'database' },
});
สรุป
Auth.js ทำให้การเพิ่ม authentication ใน Astro เป็นเรื่องง่ายและปลอดภัย ด้วย OAuth providers ที่หลากหลาย, session management ที่ดี, และ middleware support ทำให้สามารถสร้าง authentication system ที่สมบูรณ์ได้อย่างรวดเร็ว