Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 52 additions & 17 deletions apps/lfx-one/src/server/controllers/profile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,26 @@ export class ProfileController {
return next(validationError);
}

// Get the bearer token from the request (set by auth middleware) or OIDC access token
const token = req.bearerToken || req.oidc?.accessToken?.access_token;
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property path req.oidc?.accessToken?.access_token appears to have redundant 'access_token' properties. Verify this is the correct path or if it should be req.oidc?.accessToken instead.

Suggested change
const token = req.bearerToken || req.oidc?.accessToken?.access_token;
const token = req.bearerToken || req.oidc?.accessToken;

Copilot uses AI. Check for mistakes.
if (!token) {
Logger.error(req, 'get_current_user_profile', startTime, new Error('No authentication token found'));

const validationError = ServiceValidationError.forField('token', 'Authentication token required', {
operation: 'get_current_user_profile',
service: 'profile_controller',
path: req.path,
});

return next(validationError);
}

let combinedProfile: CombinedProfile | null = null;

// Step 1: Try to get user metadata from NATS first (authoritative source)
let natsUserData: UserMetadata | null = null;
try {
const natsResponse = await this.userService.getUserInfo(req, userId);
const natsResponse = await this.userService.getUserInfo(req, token);
req.log.info({ userId, natsSuccess: natsResponse.success }, 'Fetched user data from NATS');

if (natsResponse.success && natsResponse.data) {
Expand All @@ -72,13 +86,43 @@ export class ProfileController {
const user = await this.supabaseService.getUser(userId);

if (!user) {
const validationError = ServiceValidationError.forField('user_id', 'User profile not found', {
operation: 'get_current_user_profile',
service: 'profile_controller',
path: req.path,
// If no Supabase user, return Auth0 user data only (from NATS)
req.log.info({ userId }, 'No Supabase user found, returning Auth0 user data only');

const auth0User = natsUserData;
if (!auth0User) {
const validationError = ServiceValidationError.forField('user_id', 'User profile not found', {
operation: 'get_current_user_profile',
service: 'profile_controller',
path: req.path,
});
return next(validationError);
}

// Build minimal user from Auth0 data
const minimalUser = {
id: userId,
username: userId,
email: (auth0User as any).email || '',
first_name: (auth0User as any).given_name || '',
last_name: (auth0User as any).family_name || '',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};

combinedProfile = {
user: minimalUser,
profile: natsUserData || null,
};

Logger.success(req, 'get_current_user_profile', startTime, {
user_id: userId,
source: 'auth0_only',
has_nats_data: !!natsUserData,
});

return next(validationError);
res.json(combinedProfile);
return;
}

// Step 3: Merge NATS data into Supabase user if available
Expand All @@ -94,18 +138,9 @@ export class ProfileController {
profile: null,
};

// Step 4: Get profile details from Supabase
// Step 4: Get profile details from Supabase (optional)
const profile = await this.supabaseService.getProfile(user.id);

// If no profile details exist, create them
if (!profile) {
await this.supabaseService.createProfileIfNotExists(user.id);
// Refetch the combined profile with the newly created profile
const updatedProfile = await this.supabaseService.getProfile(user.id);
combinedProfile.profile = updatedProfile || null;
} else {
combinedProfile.profile = profile;
}
combinedProfile.profile = profile || null;

// Step 5: Merge NATS metadata into profile if available
if (natsUserData && combinedProfile.profile) {
Expand Down
4 changes: 2 additions & 2 deletions apps/lfx-one/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ const authConfig: ConfigParams = {
secret: process.env['PCC_AUTH0_SECRET'] || 'sufficiently-long-string',
authorizationParams: {
response_type: 'code',
audience: process.env['PCC_AUTH0_AUDIENCE'] || 'https://example.com',
scope: 'openid email profile access:api offline_access',
audience: 'https://linuxfoundation-dev.auth0.com/api/v2/',
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding the Auth0 audience URL removes environment flexibility. Consider using an environment variable like process.env['PCC_AUTH0_AUDIENCE'] with this as a fallback to maintain consistency with other configuration values.

Suggested change
audience: 'https://linuxfoundation-dev.auth0.com/api/v2/',
audience: process.env['PCC_AUTH0_AUDIENCE'] || 'https://linuxfoundation-dev.auth0.com/api/v2/',

Copilot uses AI. Check for mistakes.
scope: 'openid email profile access:api offline_access update:current_user_metadata read:current_user',
},
clientSecret: process.env['PCC_AUTH0_CLIENT_SECRET'] || 'bar',
routes: {
Expand Down
Loading