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

  1. Add the required scope to the client_credentials token request body (scope=pay,order)
  2. Extend the client's allow-scope configuration on the OAuth2 server
  3. 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

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.