dromara/Sa-Token · error · SaOAuth2Exception

30102

30102

Error message

scope 不可为空

What it means

Thrown by RequestAuthModel.checkModel() when the scopes list is empty. sa-token requires every authorization request to carry at least one scope; this is the second self-check performed on the RequestAuthModel. Error code 30102.

Source

Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/model/request/RequestAuthModel.java:192

	/**
	 * @param nonce 要设置的随机数
	 * @return 对象自身
	 */
	public RequestAuthModel setNonce(String nonce) {
		this.nonce = nonce;
		return this;
	}

	/**
	 * 数据自检
	 * @return 对象自身
	 */
	public RequestAuthModel checkModel() {
		if(SaFoxUtil.isEmpty(clientId)) {
			throw new SaOAuth2Exception("client_id 不可为空").setCode(SaOAuth2ErrorCode.CODE_30101);
		}
		if(SaFoxUtil.isEmpty(scopes)) {
			throw new SaOAuth2Exception("scope 不可为空").setCode(SaOAuth2ErrorCode.CODE_30102);
		}
		if(SaFoxUtil.isEmpty(redirectUri)) {
			throw new SaOAuth2Exception("redirect_uri 不可为空").setCode(SaOAuth2ErrorCode.CODE_30103);
		}
		if(SaFoxUtil.isEmpty(String.valueOf(loginId))) {
			throw new SaOAuth2Exception("LoginId 不可为空").setCode(SaOAuth2ErrorCode.CODE_30104);
		}
		return this;
	}

	@Override
	public String toString() {
		return "RequestAuthModel{" +
				"clientId='" + clientId + '\'' +
				", scopes=" + scopes +
				", loginId=" + loginId +
				", redirectUri='" + redirectUri + '\'' +
				", responseType='" + responseType + '\'' +

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Add a scope parameter with at least one value that the client has contracted, e.g. scope=getuserinfo
  2. If a default scope is desired, set it server-side before checkModel() runs (e.g. in a custom grant-type handler or authorize pre-processing)
  3. Verify the registered SaClientModel contractScopes includes the requested scope so the next check (30112) does not fail

Example fix

// before
ra.scopes = scopes; // scopes may be null

// after
ra.scopes = (scopes == null || scopes.isEmpty()) ? Arrays.asList("getuserinfo") : scopes;
ra.checkModel();
Defensive patterns

Strategy: validation

Validate before calling

List<String> scopes = converter.convertScopeStringToList(scopeParam);
if(scopes == null || scopes.isEmpty()) {
    throw new IllegalArgumentException("at least one scope is required");
}

Try / catch

catch(SaOAuth2Exception e) { if("30102".equals(e.getCode())) return badRequest("scope required"); }

Prevention

When it happens

Trigger: Calling /oauth2/authorize without the scope parameter, or passing an empty scope string (e.g. scope=) which converts to an empty list. Custom code that constructs RequestAuthModel without assigning ra.scopes and then generates a token.

Common situations: Client app does not send scope because it assumes a default; integration code copies an example URL that omitted scope; scope string parsing returns empty after trimming separators.

Related errors


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