calcom/cal.diy · error · ForbiddenException
PermissionsGuard - no oAuth client found for access token=${
Error message
PermissionsGuard - no oAuth client found for access token=${accessToken} What it means
ForbiddenException from PermissionsGuard.getOAuthClientByAccessToken when tokensRepository.getAccessTokenClient(accessToken) returns null — i.e. the supplied Bearer access token does not match any row in the platform access-tokens store. The token itself is echoed back in the message (note: this leaks the credential into logs/responses — a hygiene concern).
Source
Thrown at apps/api/v2/src/modules/auth/guards/permissions/permissions.guard.ts:79
`PermissionsGuard - oAuth client with id=${
oAuthClient.id
} does not have the required permissions=${requiredPermissions
.map((permission) => this.oAuthClientsOutputService.transformOAuthClientPermission(permission))
.join(
", "
)}. Go to platform dashboard settings and add the required permissions to the oAuth client.`
);
}
return true;
}
async getOAuthClientByAccessToken(
accessToken: string
): Promise<Pick<PlatformOAuthClient, "id" | "permissions">> {
const oAuthClient = await this.tokensRepository.getAccessTokenClient(accessToken);
if (!oAuthClient) {
throw new ForbiddenException(
`PermissionsGuard - no oAuth client found for access token=${accessToken}`
);
}
return oAuthClient;
}
async getOAuthClientById(id: string): Promise<Pick<PlatformOAuthClient, "id" | "permissions">> {
const oAuthClient = await this.oAuthClientRepository.getOAuthClient(id);
if (!oAuthClient) {
throw new ForbiddenException(`PermissionsGuard - no oAuth client found for client id=${id}`);
}
return oAuthClient;
}
getDecodedThirdPartyAccessToken(bearerToken: string) {
return this.tokensService.getDecodedThirdPartyAccessToken(bearerToken);
}
}View on GitHub (pinned to 176037d0af)
Solutions
- Refresh the access token using the OAuth2 refresh_token grant, then retry.
- Confirm the token was issued by this platform's OAuth server and not by a third-party IdP.
- If revoked, re-issue via the authorize flow.
- As a platform maintainer: do NOT echo the raw accessToken back in the error message — log only a truncated/hash identifier to avoid credential leakage.
Example fix
// before — leaks the credential
throw new ForbiddenException(
`PermissionsGuard - no oAuth client found for access token=${accessToken}`
);
// after — redact
throw new ForbiddenException(
`PermissionsGuard - no oAuth client found for access token=${accessToken.slice(0, 6)}…`
); Defensive patterns
Strategy: retry
Validate before calling
// Decode JWT exp client-side to catch expiry before sending
function isLikelyExpired(token: string): boolean {
try {
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
return typeof payload.exp === 'number' && payload.exp * 1000 < Date.now();
} catch { return true; }
} Try / catch
try {
await client.get('/v2/protected');
} catch (e) {
if (e.status === 403 && /no oAuth client found for access token/.test(e.message)) {
await refreshToken(); // refresh_token grant, then retry once
return client.get('/v2/protected');
}
throw e;
} Prevention
- Refresh access tokens before they expire.
- Never log/echo the raw access token — the current server message leaks it; file an issue to redact.
- Distinguish access tokens from refresh tokens and from NextAuth session tokens.
When it happens
Trigger: Calling a PermissionsGuard-protected endpoint with a Bearer token that is expired, revoked, malformed, or issued by a different system (e.g. a NextAuth session token that wasn't short-circuited, or a third-party token that getDecodedThirdPartyAccessToken didn't recognize).
Common situations: Token expired (access tokens are short-lived); token revoked from the dashboard; copy/paste truncation; using a refresh token where an access token is required; environment drift between issuing and verifying API; clock skew causing premature expiry.
Related errors
- PermissionsGuard - no authentication provided. Provide eithe
- PermissionsGuard - oAuth client with id=${oAuthClient.id} do
- PermissionsGuard - no oAuth client found for client id=${id}
- ApiAuthStrategy - access token - Invalid Access Token.
- ApiAuthStrategy - access token - Invalid Access Token.. No o
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/b2d758e8c207b704.
Report an issue: GitHub.