SonarSource/sonarqube · error · UnauthorizedException
User %s is not part of allowed workspaces list
Error message
User %s is not part of allowed workspaces list
What it means
Thrown by checkTeamRestriction when workspace allowlisting is configured and the workspace-access response from Bitbucket is null or contains no workspaces, so the user cannot be matched against any allowed workspace. It is an UnauthorizedException: authentication succeeded but the user is denied by policy.
Source
Thrown at server/sonar-auth-bitbucket/src/main/java/org/sonar/auth/bitbucket/BitbucketIdentityProvider.java:172
@CheckForNull
private GsonEmails requestEmails(OAuth20Service service, OAuth2AccessToken accessToken) throws InterruptedException, ExecutionException, IOException {
OAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + "2.0/user/emails");
service.signRequest(accessToken, userRequest);
Response emailsResponse = service.execute(userRequest);
if (emailsResponse.isSuccessful()) {
return GsonEmails.parse(emailsResponse.getBody());
}
return null;
}
private void checkTeamRestriction(OAuth20Service service, OAuth2AccessToken accessToken, GsonUser user) throws InterruptedException, ExecutionException, IOException {
String[] workspaceAllowed = settings.workspaceAllowedList();
if (workspaceAllowed != null && workspaceAllowed.length > 0) {
GsonWorkspaceAccesses userWorkspaces = requestWorkspaces(service, accessToken);
String errorMessage = format("User %s is not part of allowed workspaces list", user.getUsername());
if (userWorkspaces == null || userWorkspaces.getWorkspaces() == null) {
throw new UnauthorizedException(errorMessage);
} else {
Set<String> uniqueUserWorkspaceSlugs = userWorkspaces.getWorkspaces().stream().map(w -> w.getWorkspace().getSlug()).collect(toSet());
List<String> workspaceAllowedList = asList(workspaceAllowed);
if (uniqueUserWorkspaceSlugs.stream().anyMatch(workspaceAllowedList::contains)) {
return;
}
List<String> workspaceNames = requestWorkspaceNames(service, accessToken, uniqueUserWorkspaceSlugs);
if (workspaceNames.stream().noneMatch(workspaceAllowedList::contains)) {
throw new UnauthorizedException(errorMessage);
}
}
}
}
@CheckForNull
private GsonWorkspaceAccesses requestWorkspaces(OAuth20Service service, OAuth2AccessToken accessToken) throws InterruptedException, ExecutionException, IOException {
OAuthRequest userRequest = new OAuthRequest(Verb.GET, settings.apiURL() + "2.0/user/workspaces");
service.signRequest(accessToken, userRequest);View on GitHub (pinned to 184c821202)
Solutions
- Grant the OAuth consumer the workspace membership scope so requestWorkspaces can read the user's workspaces.
- Confirm the user actually belongs to one of the workspaces listed in sonar.auth.bitbucket.workspaces.
- Check the workspaces API response (add logging) to distinguish 'no memberships' from 'unreadable due to scope'.
- If the restriction is unintended, clear the workspace allowlist setting.
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check that the user has workspace data before enforcing restriction
if (userWorkspaces == null || userWorkspaces.getWorkspaces() == null || userWorkspaces.getWorkspaces().isEmpty()) {
LOG.warn("No workspace memberships returned for user {} — check token scopes", user.getUsername());
} Try / catch
try {
checkTeamRestriction(service, accessToken, user);
} catch (UnauthorizedException e) {
LOG.warn("Workspace restriction rejected login: {}", e.getMessage());
} Prevention
- Grant the workspace membership scope to the OAuth consumer.
- Confirm each configured allowed workspace exists and the users are members.
- Log the raw workspaces response once when setting up to validate scopes.
When it happens
Trigger: settings.workspaceAllowedList() is non-empty, requestWorkspaces returns a GsonWorkspaceAccesses whose getWorkspaces() is null (or the whole object is null), immediately throwing before slug comparison.
Common situations: Bitbucket returns an empty/absent workspaces page (user truly has no workspace memberships); the token lacks the workspace scope so Bitbucket returns no workspace data; apiURL misconfigured so the workspaces endpoint silently returns an empty payload.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Unable to contact Bitbucket Cloud servers: Configure the OAu
- Unable to contact Bitbucket Cloud servers: Check your creden
- Error returned by Bitbucket Cloud: %s
- Unable to contact Bitbucket Cloud servers
- Can not get Bitbucket user profile. HTTP code: %s, response:
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/886bbb895f73fe4f.
Report an issue: GitHub.