{"record":{"id":"7937b3ca6a265c34","repo":"apolloconfig/apollo","slug":"metadata-read-permission-is-required","errorCode":null,"errorMessage":"Metadata read permission is required","messagePattern":"Metadata read permission is required","errorType":"exception","errorClass":"AccessDeniedException","httpStatus":403,"severity":"error","filePath":"apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/EnvController.java","lineNumber":51,"sourceCode":"  private final EnvOpenApiService envOpenApiService;\n  private final UnifiedPermissionValidator unifiedPermissionValidator;\n\n  public EnvController(EnvOpenApiService envOpenApiService,\n      UnifiedPermissionValidator unifiedPermissionValidator) {\n    this.envOpenApiService = envOpenApiService;\n    this.unifiedPermissionValidator = unifiedPermissionValidator;\n  }\n\n  @Override\n  public ResponseEntity<List<String>> getEnvs() {\n    requireMetadataReadPermissionForUserToken();\n    return ResponseEntity.ok(envOpenApiService.getEnvs());\n  }\n\n  private void requireMetadataReadPermissionForUserToken() {\n    if (UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType())\n        && !unifiedPermissionValidator.hasAnyUserTokenOperation(UserTokenOperation.METADATA_READ)) {\n      throw new AccessDeniedException(\"Metadata read permission is required\");\n    }\n  }\n}\n","sourceCodeStart":33,"sourceCodeEnd":55,"githubUrl":"https://github.com/apolloconfig/apollo/blob/d95fc18d112589efc09ddcbe1507047584d55251/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/EnvController.java#L33-L55","documentation":"Thrown by EnvController.requireMetadataReadPermissionForUserToken when a USER_TOKEN identity does not have the METADATA_READ operation scope. The getEnvs endpoint returns the list of configured environments, which Apollo treats as metadata. For USER_TOKEN auth, this requires the token to have any operation in the METADATA_READ bundle (all operations except user:manage). Maps to HTTP 403 AccessDeniedException.","triggerScenarios":"GET /openapi/v1/envs called with a USER_TOKEN that has only user:manage scope or no scopes at all. The hasAnyUserTokenOperation(UserTokenOperation.METADATA_READ) check fails because the token lacks any metadata-read-capable operation.","commonSituations":"A narrowly-scoped user token (e.g. only user:manage for user administration) is reused for a general environment-listing API call. The token is valid but its scope doesn't include metadata-read operations.","solutions":["Grant the USER_TOKEN at least one operation from the METADATA_READ bundle (config:read, config:modify, config:release, namespace:create, namespace:delete, cluster:create, app:create, app:manage-role, system:admin) in the Portal.","Use a CONSUMER token for environment listing, since the METADATA_READ check only applies to USER_TOKEN auth.","If the token's sole purpose is user management, make a separate API call with a broader token for environment metadata."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before calling GET /envs with a USER_TOKEN, verify the token has metadata-read scope\nif (authType.equals(\"USER_TOKEN\") && !tokenHasAnyMetadataReadOperation()) {\n    // The token needs at least one of: config:read, config:modify, config:release,\n    // namespace:create, namespace:delete, cluster:create, app:create, app:manage-role, system:admin\n    throw new SecurityException(\"USER_TOKEN lacks metadata-read scope. Add an operation grant.\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return client.get(\"/openapi/v1/envs\");\n} catch (AccessDeniedException e) {\n    if (e.getMessage().contains(\"Metadata read\")) {\n        // Use a different token with broader scope, or fall back to config-based env list\n        return getEnvsFromConfig();\n    }\n    throw e;\n}","preventionTips":["When generating a USER_TOKEN for general-purpose use, include at least config:read scope.","Reserve user:manage-only tokens strictly for user management APIs.","Document which scopes are needed for each OpenAPI endpoint category."],"tags":["authorization","openapi","environment","permissions","user-token","metadata","access-control","http-403"],"backgroundTag":null,"analyzedSha":"d95fc18d112589efc09ddcbe1507047584d55251","analyzedAt":"2026-08-14T04:00:05.477Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}