core/apps/webapp/app/utils/auth-helper.ts
Harshith Mullapudi 81c18ce9bb
Fix: integration account handling and improve webhook event processing (#31)
* 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>
2025-07-25 11:56:44 +05:30

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;
}
}