Initial commit
This commit is contained in:
commit
92e96b9107
85 changed files with 24969 additions and 0 deletions
100
app/server/api/auth/callback.get.ts
Normal file
100
app/server/api/auth/callback.get.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { User } from "../../models/User";
|
||||
import jwt from "jsonwebtoken";
|
||||
import type {
|
||||
OAuthTokenResponse,
|
||||
GhostGuildUserInfo,
|
||||
} from "../../../types/auth";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const config = useRuntimeConfig();
|
||||
const query = getQuery(event);
|
||||
|
||||
// Verify state for CSRF protection
|
||||
const storedState = getCookie(event, "oauth_state");
|
||||
if (!storedState || storedState !== query.state) {
|
||||
return sendRedirect(event, "/login?error=invalid_state");
|
||||
}
|
||||
|
||||
// Clear the state cookie
|
||||
deleteCookie(event, "oauth_state");
|
||||
|
||||
// Exchange authorization code for access token
|
||||
try {
|
||||
const tokenResponse = await $fetch<OAuthTokenResponse>(
|
||||
`${config.ghostguildApiUrl}/oauth/token`,
|
||||
{
|
||||
method: "POST",
|
||||
body: {
|
||||
grant_type: "authorization_code",
|
||||
code: query.code,
|
||||
redirect_uri: `${config.public.siteUrl}/api/auth/callback`,
|
||||
client_id: config.ghostguildClientId,
|
||||
client_secret: config.ghostguildClientSecret,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Get user information from Ghost Guild
|
||||
const userInfo = await $fetch<GhostGuildUserInfo>(
|
||||
`${config.ghostguildApiUrl}/user/me`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokenResponse.access_token}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Find or create user in our database
|
||||
let user = await User.findOne({ ghostguildId: userInfo.id });
|
||||
|
||||
if (!user) {
|
||||
user = await User.create({
|
||||
ghostguildId: userInfo.id,
|
||||
email: userInfo.email,
|
||||
username: userInfo.username,
|
||||
displayName: userInfo.displayName || userInfo.username,
|
||||
avatar: userInfo.avatar,
|
||||
roles: userInfo.roles || ["member"],
|
||||
permissions: {
|
||||
canEdit: userInfo.roles?.includes("member") || false,
|
||||
canModerate: userInfo.roles?.includes("moderator") || false,
|
||||
canAdmin: userInfo.roles?.includes("admin") || false,
|
||||
},
|
||||
lastLogin: new Date(),
|
||||
});
|
||||
} else {
|
||||
// Update existing user
|
||||
user.displayName = userInfo.displayName || userInfo.username;
|
||||
user.avatar = userInfo.avatar;
|
||||
user.roles = userInfo.roles || ["member"];
|
||||
user.lastLogin = new Date();
|
||||
await user.save();
|
||||
}
|
||||
|
||||
// Create JWT token
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: user._id,
|
||||
username: user.username,
|
||||
roles: user.roles,
|
||||
permissions: user.permissions,
|
||||
},
|
||||
config.jwtSecret as string,
|
||||
{ expiresIn: "7d" },
|
||||
);
|
||||
|
||||
// Set JWT as httpOnly cookie
|
||||
setCookie(event, "auth-token", token, {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "lax",
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
});
|
||||
|
||||
// Redirect to dashboard or home
|
||||
return sendRedirect(event, "/dashboard");
|
||||
} catch (error) {
|
||||
console.error("OAuth callback error:", error);
|
||||
return sendRedirect(event, "/login?error=authentication_failed");
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue