mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 21:48:36 +00:00
* Feat: add onboarding screens * Fix: integration account handling and improve webhook event processing * Bump version: 0.1.12 --------- Co-authored-by: Manoj K <saimanoj58@gmail.com>
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import { json, redirect } from "@remix-run/node";
|
|
import { getUser } from "~/services/session.server";
|
|
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
|
|
import { oauth2Service } from "~/services/oauth2.server";
|
|
import { getUserById } from "~/models/user.server";
|
|
|
|
export type AuthenticatedUser = {
|
|
id: string;
|
|
email: string;
|
|
name: string | null;
|
|
displayName: string | null;
|
|
avatarUrl: string | null;
|
|
admin: boolean;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
confirmedBasicDetails: boolean;
|
|
onboardingComplete: boolean;
|
|
authMethod: 'session' | 'pat' | 'oauth2';
|
|
oauth2?: {
|
|
clientId: string;
|
|
scope: string | null;
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Authenticates a request using session, PAT, or OAuth2 access token
|
|
* Returns the authenticated user or throws an error response
|
|
*/
|
|
export async function requireAuth(request: Request): Promise<AuthenticatedUser> {
|
|
const authHeader = request.headers.get("authorization");
|
|
|
|
// Try OAuth2 access token authentication
|
|
if (authHeader && authHeader.startsWith("Bearer ")) {
|
|
const token = authHeader.substring(7);
|
|
|
|
// Check if it's a PAT token first
|
|
const patAuth = await authenticateApiRequestWithPersonalAccessToken(request);
|
|
if (patAuth) {
|
|
const user = await getUserById(patAuth.userId);
|
|
if (user) {
|
|
return {
|
|
id: user.id,
|
|
email: user.email,
|
|
name: user.name,
|
|
displayName: user.displayName,
|
|
avatarUrl: user.avatarUrl,
|
|
admin: user.admin,
|
|
createdAt: user.createdAt,
|
|
updatedAt: user.updatedAt,
|
|
confirmedBasicDetails: user.confirmedBasicDetails,
|
|
onboardingComplete: user.onboardingComplete,
|
|
authMethod: 'pat',
|
|
};
|
|
}
|
|
}
|
|
|
|
// Try OAuth2 access token
|
|
try {
|
|
const accessToken = await oauth2Service.validateAccessToken(token);
|
|
return {
|
|
id: accessToken.user.id,
|
|
email: accessToken.user.email,
|
|
name: accessToken.user.name,
|
|
displayName: accessToken.user.displayName,
|
|
avatarUrl: accessToken.user.avatarUrl,
|
|
admin: accessToken.user.admin,
|
|
createdAt: accessToken.user.createdAt,
|
|
updatedAt: accessToken.user.updatedAt,
|
|
confirmedBasicDetails: accessToken.user.confirmedBasicDetails,
|
|
onboardingComplete: accessToken.user.onboardingComplete,
|
|
authMethod: 'oauth2',
|
|
oauth2: {
|
|
clientId: accessToken.client.clientId,
|
|
scope: accessToken.scope,
|
|
},
|
|
};
|
|
} catch (error) {
|
|
// OAuth2 token validation failed, continue to session auth
|
|
}
|
|
}
|
|
|
|
// Try session authentication
|
|
const sessionUser = await getUser(request);
|
|
if (sessionUser) {
|
|
return {
|
|
id: sessionUser.id,
|
|
email: sessionUser.email,
|
|
name: sessionUser.name,
|
|
displayName: sessionUser.displayName,
|
|
avatarUrl: sessionUser.avatarUrl,
|
|
admin: sessionUser.admin,
|
|
createdAt: sessionUser.createdAt,
|
|
updatedAt: sessionUser.updatedAt,
|
|
confirmedBasicDetails: sessionUser.confirmedBasicDetails,
|
|
onboardingComplete: sessionUser.onboardingComplete,
|
|
authMethod: 'session',
|
|
};
|
|
}
|
|
|
|
// If no authentication method worked, return 401
|
|
throw json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
/**
|
|
* Optional authentication - returns user if authenticated, null otherwise
|
|
*/
|
|
export async function getAuthenticatedUser(request: Request): Promise<AuthenticatedUser | null> {
|
|
try {
|
|
return await requireAuth(request);
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
} |