SonarSource/sonarqube · error · UnauthorizedException

'%s' must be a member of at least one organization which has

Error message

'%s' must be a member of at least one organization which has installed the SonarQube GitHub app

What it means

Thrown by GitHubIdentityProvider.check when isUserAuthorized returns false: the authenticated GitHub user's organizations do not satisfy the configured organization restriction. When no organizations are configured, the message asks that the user belong to at least one organization with the SonarQube GitHub App installed.

Source

Thrown at server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubIdentityProvider.java:150

    if (user.getEmail() == null) {
      // if the user has not specified a public email address in their profile
      email = gitHubRestClient.getEmail(scribe, accessToken);
    } else {
      email = user.getEmail();
    }

    UserIdentity userIdentity = userIdentityFactory.create(user, email,
      settings.syncGroups() ? gitHubRestClient.getTeams(scribe, accessToken) : null);
    context.authenticate(userIdentity);
    context.redirectToRequestedPage();
  }

  private void check(OAuth20Service scribe, OAuth2AccessToken accessToken, GsonUser user) {
    if (!isUserAuthorized(scribe, accessToken)) {
      String message = settings.getOrganizations().isEmpty()
        ? format("'%s' must be a member of at least one organization which has installed the SonarQube GitHub app", user.getLogin())
        : format("'%s' must be a member of at least one organization: '%s'", user.getLogin(), String.join("', '", settings.getOrganizations().stream().sorted().toList()));
      throw new UnauthorizedException(message);
    }
  }

  private boolean isUserAuthorized(OAuth20Service scribe, OAuth2AccessToken accessToken) {
    Set<String> userOrganizationNames = getUserOrganizationNames(scribe, accessToken);
    if (isOrganizationMembershipRequired()) {
      return isOrganizationsMember(settings.getOrganizations(), userOrganizationNames);
    } else {
      return isMemberOfInstallationOrganization(userOrganizationNames);
    }
  }

  private static boolean isOrganizationsMember(Set<String> organizations, Set<String> userOrganizationNames) {
    return organizations.stream().anyMatch(userOrganizationNames::contains);
  }

  @NotNull
  private Set<String> getUserOrganizationNames(OAuth20Service scribe, OAuth2AccessToken accessToken) {

View on GitHub (pinned to 184c821202)

Solutions

  1. Install the SonarQube GitHub App on the user's organization (or add the org to sonar.auth.github.organizations).
  2. Verify the user's membership is public in the org or that the app has permission to see memberships.
  3. Check the configured organization names match GitHub org slugs exactly (case-insensitive, no typos).
  4. Test with a known member of an authorized org to confirm the check works.
Defensive patterns

Strategy: validation

Validate before calling

// Verify app installation and org membership before login
// GET /user/memberships/orgs/{org} with the app token must return 200

Try / catch

try {
    check(scribe, accessToken, user);
} catch (UnauthorizedException e) {
    LOG.warn("GitHub org check failed: {}", e.getMessage());
    throw e; // surface to the login page
}

Prevention

When it happens

Trigger: onCallback -> check(scribe, accessToken, user); getUserOrganizationNames returns a set that fails isUserAuthorized — either organizations are configured and the user belongs to none of them, or none are configured and the user belongs to no organization that has the SonarQube GitHub App installed.

Common situations: User belongs to an org but the SonarQube GitHub App is not installed on that org; user's org membership is private so it is not visible to the app; admin listed wrong org names; user logs in with a personal account unrelated to the company's GitHub orgs.

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


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/22dba7eac9c9080b. Report an issue: GitHub.