calcom/cal.diy · error · ForbiddenException
OAuthClientGuard - No organization associated with the user.
Error message
OAuthClientGuard - No organization associated with the user.
What it means
Thrown by OAuthClientGuard when either request.user is falsy (no authenticated principal) or getOrganizationId(context) returns falsy. getOrganizationId prefers request.organizationId (set by the auth method) and otherwise derives the org from the user's profile. A user with no organization membership, or an auth method that doesn't populate organizationId, trips this ForbiddenException (HTTP 403).
Source
Thrown at apps/api/v2/src/modules/oauth-clients/guards/oauth-client-guard.ts:28
NotFoundException,
} from "@nestjs/common";
@Injectable()
export class OAuthClientGuard implements CanActivate {
constructor(private oAuthClientRepository: OAuthClientRepository, private usersService: UsersService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<ApiAuthGuardRequest>();
const organizationId = this.getOrganizationId(context);
const user: ApiAuthGuardUser = request.user;
const oAuthClientId = request.params.clientId;
if (!oAuthClientId) {
throw new ForbiddenException("OAuthClientGuard - No OAuth client associated with the request.");
}
if (!user || !organizationId) {
throw new ForbiddenException("OAuthClientGuard - No organization associated with the user.");
}
const oAuthClient = await this.oAuthClientRepository.getOAuthClient(oAuthClientId);
if (!oAuthClient) {
throw new NotFoundException("OAuthClientGuard - OAuth client not found.");
}
const allowed = Boolean(user.isSystemAdmin || oAuthClient.organizationId === organizationId);
if (!allowed) {
throw new ForbiddenException(
`OAuthClientGuard - forbidden. oAuth client with id=${oAuthClientId} does not belong to the organization with id=${organizationId}.`
);
}
return true;
}
getOrganizationId(context: ExecutionContext) {View on GitHub (pinned to 176037d0af)
Solutions
- Ensure the authenticated user is a member of the organization that owns the OAuth client — assign the user to the org first.
- Use an API key or auth method that populates organizationId for org-scoped routes.
- In tests, seed the user with an organization profile before invoking guarded routes.
- If the user legitimately has no org, route them through org creation/invitation before they hit OAuth-client endpoints.
Example fix
// before — user has no org membership
const apiKey = createPersonalApiKey(userWithoutOrg);
await fetch(`/v2/oauth-clients/${clientId}/events`, { headers: { Authorization: `Bearer ${apiKey}` } }); // 403
// after — invite the user to the org first
await inviteUserToOrganization(userWithoutOrg.id, orgId, 'MEMBER');
await fetch(`/v2/oauth-clients/${clientId}/events`, { headers: { Authorization: `Bearer ${apiKey}` } }); Defensive patterns
Strategy: validation
Validate before calling
// Before calling a guarded route, ensure the auth context carries an org
function assertOrgContext(user: { organizationId?: number | null }, requestOrgId?: number) {
const orgId = requestOrgId ?? user.organizationId;
if (!orgId) throw new Error('User has no organization context for org-scoped routes');
return orgId;
} Type guard
function userHasOrgContext(user: unknown, reqOrgId?: unknown): boolean {
return Boolean(reqOrgId) || Boolean((user as any)?.organizationId);
} Try / catch
try {
await guardedRouteCall();
} catch (e) {
if (e instanceof ForbiddenException && /organization/i.test(e.message)) {
// prompt the user to create/join an organization
} else throw e;
} Prevention
- Use an org-bound API key for org-scoped routes.
- Assign users to an organization during provisioning.
- Seed an org membership in tests before hitting guarded routes.
When it happens
Trigger: An API key / session whose user belongs to no organization calls an OAuthClientGuard-protected route; or the auth strategy in play (e.g. a personal API key without org context) did not set request.organizationId and the user has no org profile.
Common situations: Calling org-scoped endpoints with a legacy personal API key that has no org binding; a user that was created outside any organization; an auth method (custom strategy) that forgets to attach organizationId; testing with a seed user that was never assigned to an org.
Related errors
- PermissionsGuard - oAuth client with id=${oAuthClient.id} do
- RolesGuard - user with id=${user.id} does not have the minim
- RolesGuard - User is not a member of the organization with i
- RolesGuard - User is not part of the organization with id=${
- RolesGuard - User is not part of the team with id=${teamId}
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/c67827d37bbf65f5.
Report an issue: GitHub.