mirror of https://github.com/halo-dev/halo
feat: Add bearer token errors for rfc6750#section-3.1 (#1868)
parent
c999c35a20
commit
599ab5618b
|
@ -0,0 +1,110 @@
|
||||||
|
package run.halo.app.identity.authentication.verifier;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a
|
||||||
|
* <a href="https://tools.ietf.org/html/rfc6750#section-3.1">Bearer Token Error</a>.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @see BearerTokenErrorCodes
|
||||||
|
* @see
|
||||||
|
* <a href="https://tools.ietf.org/html/rfc6750#section-3">RFC 6750 Section 3: The WWW-Authenticate Response Header Field</a>
|
||||||
|
* @see
|
||||||
|
* <a href="https://github.com/spring-projects/spring-security/blob/e79b6b3ac8/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/BearerTokenError.java">oauth2 resource server BearerTokenError</a>
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public final class BearerTokenError extends OAuth2Error {
|
||||||
|
private final HttpStatus httpStatus;
|
||||||
|
|
||||||
|
private final String scope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@code BearerTokenError} using the provided parameters
|
||||||
|
*
|
||||||
|
* @param errorCode the error code
|
||||||
|
* @param httpStatus the HTTP status
|
||||||
|
*/
|
||||||
|
public BearerTokenError(String errorCode, HttpStatus httpStatus, String description,
|
||||||
|
String errorUri) {
|
||||||
|
this(errorCode, httpStatus, description, errorUri, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@code BearerTokenError} using the provided parameters
|
||||||
|
*
|
||||||
|
* @param errorCode the error code
|
||||||
|
* @param httpStatus the HTTP status
|
||||||
|
* @param description the description
|
||||||
|
* @param errorUri the URI
|
||||||
|
* @param scope the scope
|
||||||
|
*/
|
||||||
|
public BearerTokenError(String errorCode, HttpStatus httpStatus, String description,
|
||||||
|
String errorUri,
|
||||||
|
String scope) {
|
||||||
|
super(errorCode, description, errorUri);
|
||||||
|
Assert.notNull(httpStatus, "httpStatus cannot be null");
|
||||||
|
Assert.isTrue(isDescriptionValid(description),
|
||||||
|
"description contains invalid ASCII characters, it must conform to RFC 6750");
|
||||||
|
Assert.isTrue(isErrorCodeValid(errorCode),
|
||||||
|
"errorCode contains invalid ASCII characters, it must conform to RFC 6750");
|
||||||
|
Assert.isTrue(isErrorUriValid(errorUri),
|
||||||
|
"errorUri contains invalid ASCII characters, it must conform to RFC 6750");
|
||||||
|
Assert.isTrue(isScopeValid(scope),
|
||||||
|
"scope contains invalid ASCII characters, it must conform to RFC 6750");
|
||||||
|
this.httpStatus = httpStatus;
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the HTTP status.
|
||||||
|
*
|
||||||
|
* @return the HTTP status
|
||||||
|
*/
|
||||||
|
public HttpStatus getHttpStatus() {
|
||||||
|
return this.httpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the scope.
|
||||||
|
*
|
||||||
|
* @return the scope
|
||||||
|
*/
|
||||||
|
public String getScope() {
|
||||||
|
return this.scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isDescriptionValid(String description) {
|
||||||
|
return description == null || description.chars().allMatch((c) ->
|
||||||
|
withinTheRangeOf(c, 0x20, 0x21)
|
||||||
|
|| withinTheRangeOf(c, 0x23, 0x5B)
|
||||||
|
|| withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isErrorCodeValid(String errorCode) {
|
||||||
|
return errorCode.chars().allMatch((c) ->
|
||||||
|
withinTheRangeOf(c, 0x20, 0x21)
|
||||||
|
|| withinTheRangeOf(c, 0x23, 0x5B)
|
||||||
|
|| withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isErrorUriValid(String errorUri) {
|
||||||
|
return errorUri == null || errorUri.chars()
|
||||||
|
.allMatch(
|
||||||
|
(c) -> c == 0x21 || withinTheRangeOf(c, 0x23, 0x5B) || withinTheRangeOf(c, 0x5D,
|
||||||
|
0x7E));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isScopeValid(String scope) {
|
||||||
|
return scope == null || scope.chars().allMatch((c) ->
|
||||||
|
withinTheRangeOf(c, 0x20, 0x21)
|
||||||
|
|| withinTheRangeOf(c, 0x23, 0x5B)
|
||||||
|
|| withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean withinTheRangeOf(int c, int min, int max) {
|
||||||
|
return c >= min && c <= max;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package run.halo.app.identity.authentication.verifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard error codes defined by the OAuth 2.0 Authorization Framework: Bearer Token
|
||||||
|
* Usage.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @see
|
||||||
|
* <a href="https://tools.ietf.org/html/rfc6750#section-3.1">RFC 6750 Section 3.1: Error Codes</a>
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public interface BearerTokenErrorCodes {
|
||||||
|
/**
|
||||||
|
* {@code invalid_request} - The request is missing a required parameter, includes an
|
||||||
|
* unsupported parameter or parameter value, repeats the same parameter, uses more
|
||||||
|
* than one method for including an access token, or is otherwise malformed.
|
||||||
|
*/
|
||||||
|
String INVALID_REQUEST = "invalid_request";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code invalid_token} - The access token provided is expired, revoked, malformed,
|
||||||
|
* or invalid for other reasons.
|
||||||
|
*/
|
||||||
|
String INVALID_TOKEN = "invalid_token";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code insufficient_scope} - The request requires higher privileges than provided
|
||||||
|
* by the access token.
|
||||||
|
*/
|
||||||
|
String INSUFFICIENT_SCOPE = "insufficient_scope";
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package run.halo.app.identity.authentication.verifier;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory for creating {@link BearerTokenError} instances that correspond to the
|
||||||
|
* registered <a href="https://tools.ietf.org/html/rfc6750#section-3.1">Bearer Token Error Codes</a>
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class BearerTokenErrors {
|
||||||
|
private static final BearerTokenError DEFAULT_INVALID_REQUEST =
|
||||||
|
invalidRequest("Invalid request");
|
||||||
|
|
||||||
|
private static final BearerTokenError DEFAULT_INVALID_TOKEN = invalidToken("Invalid token");
|
||||||
|
|
||||||
|
private static final BearerTokenError DEFAULT_INSUFFICIENT_SCOPE =
|
||||||
|
insufficientScope("Insufficient scope", null);
|
||||||
|
|
||||||
|
private static final String DEFAULT_URI = "https://tools.ietf.org/html/rfc6750#section-3.1";
|
||||||
|
|
||||||
|
private BearerTokenErrors() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link BearerTokenError} caused by an invalid request
|
||||||
|
*
|
||||||
|
* @param message a description of the error
|
||||||
|
* @return a {@link BearerTokenError}
|
||||||
|
*/
|
||||||
|
public static BearerTokenError invalidRequest(String message) {
|
||||||
|
try {
|
||||||
|
return new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST,
|
||||||
|
HttpStatus.BAD_REQUEST, message,
|
||||||
|
DEFAULT_URI);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
// some third-party library error messages are not suitable for RFC 6750's
|
||||||
|
// error message charset
|
||||||
|
return DEFAULT_INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link BearerTokenError} caused by an invalid token
|
||||||
|
*
|
||||||
|
* @param message a description of the error
|
||||||
|
* @return a {@link BearerTokenError}
|
||||||
|
*/
|
||||||
|
public static BearerTokenError invalidToken(String message) {
|
||||||
|
try {
|
||||||
|
return new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN,
|
||||||
|
HttpStatus.UNAUTHORIZED, message,
|
||||||
|
DEFAULT_URI);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
// some third-party library error messages are not suitable for RFC 6750's
|
||||||
|
// error message charset
|
||||||
|
return DEFAULT_INVALID_TOKEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link BearerTokenError} caused by an invalid token
|
||||||
|
*
|
||||||
|
* @param scope the scope attribute to use in the error
|
||||||
|
* @return a {@link BearerTokenError}
|
||||||
|
*/
|
||||||
|
public static BearerTokenError insufficientScope(String message, String scope) {
|
||||||
|
try {
|
||||||
|
return new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE,
|
||||||
|
HttpStatus.FORBIDDEN, message,
|
||||||
|
DEFAULT_URI, scope);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
// some third-party library error messages are not suitable for RFC 6750's
|
||||||
|
// error message charset
|
||||||
|
return DEFAULT_INSUFFICIENT_SCOPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package run.halo.app.authentication.verifyer;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import run.halo.app.identity.authentication.verifier.BearerTokenError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link BearerTokenError}
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @see <a href="https://tools.ietf.org/html/rfc6750#section-3.1">Bearer Token Error</a>
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class BearerTokenErrorTest {
|
||||||
|
private static final String TEST_ERROR_CODE = "test-code";
|
||||||
|
|
||||||
|
private static final HttpStatus TEST_HTTP_STATUS = HttpStatus.UNAUTHORIZED;
|
||||||
|
|
||||||
|
private static final String TEST_DESCRIPTION = "test-description";
|
||||||
|
|
||||||
|
private static final String TEST_URI = "https://example.com";
|
||||||
|
|
||||||
|
private static final String TEST_SCOPE = "test-scope";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithErrorCodeWhenErrorCodeIsValidThenCreated() {
|
||||||
|
BearerTokenError error =
|
||||||
|
new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, null, null);
|
||||||
|
assertThat(error.getErrorCode()).isEqualTo(TEST_ERROR_CODE);
|
||||||
|
assertThat(error.getHttpStatus()).isEqualTo(TEST_HTTP_STATUS);
|
||||||
|
assertThat(error.getDescription()).isNull();
|
||||||
|
assertThat(error.getUri()).isNull();
|
||||||
|
assertThat(error.getScope()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithErrorCodeThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(null, TEST_HTTP_STATUS, null, null))
|
||||||
|
.withMessage("errorCode cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWhenErrorCodeIsEmptyThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError("", TEST_HTTP_STATUS, null, null))
|
||||||
|
.withMessage("errorCode cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWhenHttpStatusIsNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, null, null, null))
|
||||||
|
.withMessage("httpStatus cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithAllParametersWhenAllParametersAreValidThenCreated() {
|
||||||
|
BearerTokenError error =
|
||||||
|
new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI,
|
||||||
|
TEST_SCOPE);
|
||||||
|
assertThat(error.getErrorCode()).isEqualTo(TEST_ERROR_CODE);
|
||||||
|
assertThat(error.getHttpStatus()).isEqualTo(TEST_HTTP_STATUS);
|
||||||
|
assertThat(error.getDescription()).isEqualTo(TEST_DESCRIPTION);
|
||||||
|
assertThat(error.getUri()).isEqualTo(TEST_URI);
|
||||||
|
assertThat(error.getScope()).isEqualTo(TEST_SCOPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithAllParametersWhenErrorCodeIsNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(
|
||||||
|
() -> new BearerTokenError(null, TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI,
|
||||||
|
TEST_SCOPE))
|
||||||
|
.withMessage("errorCode cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithAllParametersThrowIllegalArgumentException1() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError("", TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI,
|
||||||
|
TEST_SCOPE))
|
||||||
|
.withMessage("errorCode cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithAllParametersThrowIllegalArgumentException2() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(
|
||||||
|
() -> new BearerTokenError(TEST_ERROR_CODE, null, TEST_DESCRIPTION, TEST_URI,
|
||||||
|
TEST_SCOPE))
|
||||||
|
.withMessage("httpStatus cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWhenErrorCodeIsInvalidThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE + "\"",
|
||||||
|
TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE)
|
||||||
|
)
|
||||||
|
.withMessageContaining("errorCode")
|
||||||
|
.withMessageContaining("RFC 6750");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithAllParametersThrowIllegalArgumentException3() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,
|
||||||
|
TEST_DESCRIPTION + "\"", TEST_URI, TEST_SCOPE)
|
||||||
|
)
|
||||||
|
.withMessageContaining("description")
|
||||||
|
.withMessageContaining("RFC 6750");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithAllParametersThrowIllegalArgumentException4() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(
|
||||||
|
() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, TEST_DESCRIPTION,
|
||||||
|
TEST_URI + "\"", TEST_SCOPE)
|
||||||
|
)
|
||||||
|
.withMessageContaining("errorUri")
|
||||||
|
.withMessageContaining("RFC 6750");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructorWithAllParametersWhenScopeIsInvalidThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,
|
||||||
|
TEST_DESCRIPTION, TEST_URI, TEST_SCOPE + "\"")
|
||||||
|
)
|
||||||
|
.withMessageContaining("scope")
|
||||||
|
.withMessageContaining("RFC 6750");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package run.halo.app.authentication.verifyer;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import run.halo.app.identity.authentication.verifier.BearerTokenError;
|
||||||
|
import run.halo.app.identity.authentication.verifier.BearerTokenErrorCodes;
|
||||||
|
import run.halo.app.identity.authentication.verifier.BearerTokenErrors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link BearerTokenErrors}
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class BearerTokenErrorsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalidRequestWhenMessageGivenThenBearerTokenErrorReturned() {
|
||||||
|
String message = "message";
|
||||||
|
BearerTokenError error = BearerTokenErrors.invalidRequest(message);
|
||||||
|
assertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_REQUEST);
|
||||||
|
assertThat(error.getDescription()).isSameAs(message);
|
||||||
|
assertThat(error.getHttpStatus()).isSameAs(HttpStatus.BAD_REQUEST);
|
||||||
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalidRequestWhenInvalidMessageGivenThenDefaultBearerTokenErrorReturned() {
|
||||||
|
String message = "has \"invalid\" chars";
|
||||||
|
BearerTokenError error = BearerTokenErrors.invalidRequest(message);
|
||||||
|
assertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_REQUEST);
|
||||||
|
assertThat(error.getDescription()).isEqualTo("Invalid request");
|
||||||
|
assertThat(error.getHttpStatus()).isSameAs(HttpStatus.BAD_REQUEST);
|
||||||
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalidTokenWhenMessageGivenThenBearerTokenErrorReturned() {
|
||||||
|
String message = "message";
|
||||||
|
BearerTokenError error = BearerTokenErrors.invalidToken(message);
|
||||||
|
assertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_TOKEN);
|
||||||
|
assertThat(error.getDescription()).isSameAs(message);
|
||||||
|
assertThat(error.getHttpStatus()).isSameAs(HttpStatus.UNAUTHORIZED);
|
||||||
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalidTokenWhenInvalidMessageGivenThenDefaultBearerTokenErrorReturned() {
|
||||||
|
String message = "has \"invalid\" chars";
|
||||||
|
BearerTokenError error = BearerTokenErrors.invalidToken(message);
|
||||||
|
assertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INVALID_TOKEN);
|
||||||
|
assertThat(error.getDescription()).isEqualTo("Invalid token");
|
||||||
|
assertThat(error.getHttpStatus()).isSameAs(HttpStatus.UNAUTHORIZED);
|
||||||
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insufficientScopeWhenMessageGivenThenBearerTokenErrorReturned() {
|
||||||
|
String message = "message";
|
||||||
|
String scope = "scope";
|
||||||
|
BearerTokenError error = BearerTokenErrors.insufficientScope(message, scope);
|
||||||
|
assertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INSUFFICIENT_SCOPE);
|
||||||
|
assertThat(error.getDescription()).isSameAs(message);
|
||||||
|
assertThat(error.getHttpStatus()).isSameAs(HttpStatus.FORBIDDEN);
|
||||||
|
assertThat(error.getScope()).isSameAs(scope);
|
||||||
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insufficientScopeWhenInvalidMessageGivenThenDefaultBearerTokenErrorReturned() {
|
||||||
|
String message = "has \"invalid\" chars";
|
||||||
|
BearerTokenError error = BearerTokenErrors.insufficientScope(message, "scope");
|
||||||
|
assertThat(error.getErrorCode()).isSameAs(BearerTokenErrorCodes.INSUFFICIENT_SCOPE);
|
||||||
|
assertThat(error.getDescription()).isSameAs("Insufficient scope");
|
||||||
|
assertThat(error.getHttpStatus()).isSameAs(HttpStatus.FORBIDDEN);
|
||||||
|
assertThat(error.getScope()).isNull();
|
||||||
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue