better logging for auth

This commit is contained in:
Nate Kelley 2025-07-16 13:09:30 -06:00
parent b1ac8b51b3
commit 32a0b84eeb
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
2 changed files with 120 additions and 35 deletions

View File

@ -7,40 +7,91 @@ const supabase = createSupabaseClient();
export const requireAuth = bearerAuth({
verifyToken: async (token, c) => {
const { data, error } = await supabase.auth.getUser(token); //usually takes about 3 - 7ms
try {
const { data, error } = await supabase.auth.getUser(token); //usually takes about 3 - 7ms
if (error || !data.user) {
if (error) {
// Log specific auth errors to help with debugging
console.warn('Token validation failed:', {
error: error.message,
// Don't log the actual token for security
tokenPrefix: `${token.substring(0, 20)}...`,
});
return false;
}
if (!data.user) {
console.warn('No user found for valid token');
return false;
}
// Set user in context for use in route handlers
c.set('supabaseUser', data.user);
// Get the corresponding user from your database
const busterUser = await getUser({ id: data.user.id });
if (!busterUser) {
console.warn('Supabase user found but no corresponding database user:', {
supabaseUserId: data.user.id,
email: data.user.email,
});
return false;
}
c.set('busterUser', busterUser);
// Ensure user is not anonymous
const isAuthenticated = !data.user.is_anonymous;
if (!isAuthenticated) {
console.info('Anonymous user attempted to access protected resource');
}
return isAuthenticated;
} catch (error) {
console.error('Unexpected error during token validation:', error);
return false;
}
c.set('supabaseUser', data.user);
const busterUser = await getUser({ id: data.user.id });
if (!busterUser) {
return false;
}
c.set('busterUser', busterUser);
return !!data.user.is_anonymous === false;
},
});
export async function requireUser(c: Context, next: Next) {
const token = c.req.header('Authorization')?.split(' ')[1];
const authHeader = c.req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
console.warn('Missing or invalid Authorization header');
throw new Error('User not authenticated - missing Authorization header');
}
const token = authHeader.split(' ')[1];
if (!token) {
throw new Error('User not authenticated');
console.warn('Empty token in Authorization header');
throw new Error('User not authenticated - empty token');
}
const { data, error } = await supabase.auth.getUser(token);
try {
const { data, error } = await supabase.auth.getUser(token);
const user = data.user;
if (error) {
console.warn('Token validation failed in requireUser:', {
error: error.message,
});
throw new Error('User not authenticated - invalid token');
}
if (!user || error || user.is_anonymous) {
throw new Error('User not authenticated');
const user = data.user;
if (!user || user.is_anonymous) {
console.warn('No valid user found or user is anonymous');
throw new Error('User not authenticated - no valid user');
}
// User is valid, continue to next middleware/handler
return next();
} catch (error) {
console.error('Unexpected error in requireUser:', error);
throw new Error('User not authenticated - server error');
}
return next();
}

View File

@ -17,17 +17,43 @@ export const createAxiosInstance = (baseURL: string) => {
}
});
// Response interceptor with retry logic for auth errors
apiInstance.interceptors.response.use(
(resp) => {
return resp;
},
(error: AxiosError) => {
async (error: AxiosError) => {
const errorCode = error.response?.status;
//402 is the payment required error code
if (errorCode === 402) {
window.location.href = createBusterRoute({
route: BusterRoutes.INFO_GETTING_STARTED
});
return Promise.reject(rustErrorHandler(error));
}
// Handle 401 Unauthorized - token might be expired
if (errorCode === 401 && !isServer) {
// Only retry once to avoid infinite loops
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
if (originalRequest && !originalRequest._retry) {
originalRequest._retry = true;
try {
// Force token refresh and retry the request
console.info('401 error detected, attempting to refresh token and retry request');
// The request interceptor will handle getting the new token
return apiInstance(originalRequest);
} catch (refreshError) {
console.error('Failed to refresh token and retry request:', refreshError);
// If refresh fails, redirect to login or show error
window.location.href = createBusterRoute({
route: BusterRoutes.AUTH_LOGIN
});
}
}
}
return Promise.reject(rustErrorHandler(error));
@ -45,17 +71,25 @@ export const defaultAxiosRequestHandler = async (
}
) => {
let token = '';
if (isServer) {
token = await getSupabaseTokenFromCookies();
} else {
token = (await options?.checkTokenValidity()?.then((res) => res?.access_token || '')) || '';
try {
if (isServer) {
token = await getSupabaseTokenFromCookies();
} else {
// Always check token validity before making requests
const tokenResult = await options?.checkTokenValidity();
token = tokenResult?.access_token || '';
}
if (!token) {
throw new Error('User authentication error - no token found');
}
(config.headers as AxiosRequestHeaders).Authorization = `Bearer ${token}`;
return config;
} catch (error) {
console.error('Error getting auth token for request:', error);
throw new Error('User authentication error - failed to get valid token');
}
if (!token) {
throw new Error('User authentication error - no token found');
}
(config.headers as AxiosRequestHeaders).Authorization = `Bearer ${token}`;
return config;
};