dromara/Sa-Token · error · SaOAuth2AccessTokenScopeException

30108

30108

Error message

该 access_token 不具备 scope:

What it means

Thrown by checkAccessTokenScope when a valid access token does not include one of the scopes being enforced (code 30108, SaOAuth2AccessTokenScopeException). The token exists but was granted a narrower scope set than the API requires, so access is denied at the scope level rather than the token level.

Source

Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java:477

			return true;
		} catch (SaOAuth2AccessTokenException e) {
			return false;
		}
	}

	/**
	 * 校验:指定 Access-Token 是否具有指定 Scope 列表,如果不具备则抛出异常
	 * @param accessToken Access-Token
	 * @param scopes 需要校验的权限列表
	 */
	public void checkAccessTokenScope(String accessToken, String... scopes) {
		AccessTokenModel at = checkAccessToken(accessToken);
		if(SaFoxUtil.isEmptyArray(scopes)) {
			return;
		}
		for (String scope : scopes) {
			if(! at.scopes.contains(scope)) {
				throw new SaOAuth2AccessTokenScopeException("该 access_token 不具备 scope:" + scope)
						.setAccessToken(accessToken)
						.setScope(scope)
						.setCode(SaOAuth2ErrorCode.CODE_30108);
			}
		}
	}

	/**
	 * 获取 Access-Token 所代表的LoginId
	 * @param accessToken Access-Token
	 * @return LoginId
	 */
	public Object getLoginIdByAccessToken(String accessToken) {
		return checkAccessToken(accessToken).loginId;
	}

	/**
	 * 获取 Access-Token 所代表的 clientId

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Include the required scope in the authorize request: /oauth2/authorize?...&scope=userinfo,order
  2. Add the missing scope to the client's allow-scope configuration on the OAuth2 server so it can be requested/granted
  3. Verify scope name spelling and separator (sa-token uses commas) match between the API check and the granted token

Example fix

// before
String url = server + "/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=..."; // no scope
// after
String url = server + "/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=...&scope=userinfo,order";
Defensive patterns

Strategy: try-catch

Validate before calling

AccessTokenModel at = saOAuth2Template.getAccessToken(token);
boolean hasScope = at != null && java.util.Arrays.asList(scopes).stream().allMatch(s -> at.scopes.contains(s));

Try / catch

try { saOAuth2Template.checkAccessTokenScope(token, "userinfo"); } catch (SaOAuth2AccessTokenScopeException e) { return status(403, "missing scope: " + e.getScope()); }

Prevention

When it happens

Trigger: Calling an API protected with SaOAuth2Util.checkAccessToken(token, "userinfo") (or StpUtil with scope checks) when the authorize request that minted the token did not include 'userinfo' in its scope parameter.

Common situations: New API endpoint requires a scope the client never requests; user unchecked a scope on the consent page; client's configured allow-scopes on the server omit the needed scope; scopes string case or separator mismatch (comma vs space).

Related errors


AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14). Data as JSON: /api/errors/6ae553b682fb3289. Report an issue: GitHub.