Service accounts for authenticated calls

following the standards used for opensaas i have created a bunch of operations that first check if its an authenticated user making the call. i.e:
if (!context.user) {
throw new HttpError(401, 'User not authenticated');
}
if (!context.user) {
throw new HttpError(401, 'User not authenticated');
}
however i have a bunch of jobs that also need to use these queries. has anyone come up with any patterns to safely bypass authentication checks i.e through the use of serviceaccounts or rules. im thinking to implement something like this:
// Skip authentication check if called from a server job (context.user will be undefined)
// We can detect if it's a server-side call by checking if context.entities exists but context.user doesn't
const isServerSideCall = !context.user && context.entities;

// Only check authentication for client-side calls
if (!context.user && !isServerSideCall) {
throw new HttpError(401, 'User not authenticated');
}

// Log the source of the call for debugging
if (isServerSideCall) {
console.log('getNewsArticles called from server-side job or process');
} else {
console.log(`getNewsArticles called by user: ${context.user?.id}`);
}
// Skip authentication check if called from a server job (context.user will be undefined)
// We can detect if it's a server-side call by checking if context.entities exists but context.user doesn't
const isServerSideCall = !context.user && context.entities;

// Only check authentication for client-side calls
if (!context.user && !isServerSideCall) {
throw new HttpError(401, 'User not authenticated');
}

// Log the source of the call for debugging
if (isServerSideCall) {
console.log('getNewsArticles called from server-side job or process');
} else {
console.log(`getNewsArticles called by user: ${context.user?.id}`);
}
4 Replies
kapa.ai
kapa.ai2d ago
Hi @Kofi_anan. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Your approach to handling authentication for both user requests and server jobs is on the right track. Currently, Wasp doesn't have a built-in mechanism specifically for service accounts, but there are several patterns you can use to solve this problem. Authentication Bypass for Server Jobs Your proposed solution using isServerSideCall = !context.user && context.entities is a reasonable approach. This checks if the operation is being called from server-side code (where context.entities exists) but without a user context. However, this approach has a potential security risk - it assumes that any call with context.entities but no context.user is a legitimate server-side call, which might not always be true. Alternative Approaches 1. Environment-based checks You could use environment variables to determine if the code is running in a job:
// In your operation
if (!context.user && process.env.NODE_ENV !== 'job') {
throw new HttpError(401, 'User not authenticated');
}
// In your operation
if (!context.user && process.env.NODE_ENV !== 'job') {
throw new HttpError(401, 'User not authenticated');
}
2. Role-based approach As mentioned in the Discord support thread, you could implement a role-based system:
// Add a role field to User entity
if (!context.user && !context.user?.role === 'service') {
throw new HttpError(401, 'User not authenticated');
}
// Add a role field to User entity
if (!context.user && !context.user?.role === 'service') {
throw new HttpError(401, 'User not authenticated');
}
This approach is also suggested in the Discord support: Hi, how do you add authorization? 3. Secret key for server-side operations You could pass a secret key from your job to the operation:
export const getNewsArticles = async (args, context) => {
// Check for either user authentication or server key
if (!context.user && args.serverKey !== process.env.SERVER_SECRET_KEY) {
throw new HttpError(401, 'Not authenticated');
}

// Rest of your code
}
export const getNewsArticles = async (args, context) => {
// Check for either user authentication or server key
if (!context.user && args.serverKey !== process.env.SERVER_SECRET_KEY) {
throw new HttpError(401, 'Not authenticated');
}

// Rest of your code
}
Future Improvements Wasp is considering improvements to the auth system. There's an open RFC about permissions and access control that might address this use case in the future: RFC: Support for Permissions (Access Control) There's also an issue discussing revisiting the auth middleware behavior: Revisit our auth middleware behaviour For now, I'd recommend using the secret key approach as it provides better security than simply checking for the existence of context.entities. This way, your server jobs can authenticate themselves properly when calling operations. Hope this helps with your implementation! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @Kofi_anan, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
toticozeni
toticozeni2d ago
Hi @Kofi_anan, could you perhaps extract the queries as separate constants/functions and then use them in both the operations and jobs? The operations would check that user exists while the jobs wont. If you gave a bit more context I could help more.
Kofi_anan
Kofi_ananOP2d ago
Yeah I've done that before for another project. That works, but I quite like the ai suggestion above of using a server env var that ie passed as an arg in the job query
toticozeni
toticozeni2d ago
Glad you've found a solution which works.

Did you find this page helpful?