dromara/Sa-Token · error · SaOAuth2ClientTokenScopeException
30109
30109
Error message
该 client_token 不具备 scope:
What it means
Thrown by checkClientTokenScope when a valid client token lacks one of the enforced scopes (code 30109, SaOAuth2ClientTokenScopeException). Analogous to the access-token scope check but for client_credentials tokens: the token authenticates the app, but the app was not granted the specific scope the API demands.
Source
Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java:685
return true;
} catch (SaOAuth2ClientTokenException e) {
return false;
}
}
/**
* 校验:指定 Client-Token 是否具有指定 Scope 列表,如果不具备则抛出异常
* @param clientToken Client-Token
* @param scopes 需要校验的权限列表
*/
public void checkClientTokenScope(String clientToken, String... scopes) {
ClientTokenModel ct = checkClientToken(clientToken);
if(SaFoxUtil.isEmptyArray(scopes)) {
return;
}
for (String scope : scopes) {
if(! ct.scopes.contains(scope)) {
throw new SaOAuth2ClientTokenScopeException("该 client_token 不具备 scope:" + scope)
.setClientToken(clientToken)
.setScope(scope)
.setCode(SaOAuth2ErrorCode.CODE_30109);
}
}
}
/**
* 回收一个 ClientToken
*
* @param clientToken /
*/
public void revokeClientToken(String clientToken) {
ClientTokenModel ct = getClientToken(clientToken);
if(ct == null) {
return;
}
// 删 ct、删索引View on GitHub (pinned to ac2c7f6e94)
Solutions
- Add the required scope to the client_credentials token request body (scope=pay,order)
- Extend the client's allow-scope configuration on the OAuth2 server
- Align scope naming/separators between the issuing request and the enforcement check
Example fix
// before
Map<String,Object> p = new HashMap<>(); p.put("grant_type","client_credentials"); p.put("client_id","1001"); p.put("client_secret","xx");
// after
p.put("scope","pay,order"); // request the scopes the API will check Defensive patterns
Strategy: try-catch
Validate before calling
ClientTokenModel ct = saOAuth2Template.getClientToken(token); boolean ok = ct != null && java.util.Arrays.asList(scopes).stream().allMatch(s -> ct.scopes.contains(s));
Try / catch
try { saOAuth2Template.checkClientTokenScope(token, "pay"); } catch (SaOAuth2ClientTokenScopeException e) { return status(403, "client lacks scope: " + e.getScope()); } Prevention
- Align the client_credentials scope request with every API the integration calls
- Add scope requirements to the API registry so new endpoints update the grant automatically
When it happens
Trigger: An API validates a client token with SaOAuth2Util.checkClientToken(token, "pay") but the client_credentials token request omitted scope=pay or the server's client config does not allow it.
Common situations: New downstream API requires a scope the caller never requests; server-side allow-scope for the client is missing entries; scope spelling/separator mismatch between grant and check.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/01416eb869ffc6d2.
Report an issue: GitHub.