mirror of
https://github.com/ysoftdevs/oauth-playground-server.git
synced 2026-01-18 09:38:09 +01:00
DAG authorization pending - now correctly in JSON with standard code
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.ysoft.geecon;
|
||||
|
||||
import com.ysoft.geecon.dto.*;
|
||||
import com.ysoft.geecon.error.ErrorResponse;
|
||||
import com.ysoft.geecon.error.OAuthException;
|
||||
import com.ysoft.geecon.repo.ClientsRepo;
|
||||
import com.ysoft.geecon.repo.SessionsRepo;
|
||||
@@ -121,7 +122,7 @@ public class OAuthResource {
|
||||
return switch (params.getGrantType()) {
|
||||
case "authorization_code" -> redeemAuthorizationCode(params);
|
||||
case "urn:ietf:params:oauth:grant-type:device_code" -> redeemDeviceCode(params);
|
||||
default -> throw new OAuthException("Unsupported grant type");
|
||||
default -> throw new OAuthException(ErrorResponse.Error.invalid_request, "Unsupported grant type");
|
||||
};
|
||||
}
|
||||
|
||||
@@ -149,23 +150,23 @@ public class OAuthResource {
|
||||
|
||||
private AccessTokenResponse redeemAuthorizationCode(TokenParams params) {
|
||||
var session = sessionsRepo.redeemAuthorizationCode(params.getCode())
|
||||
.orElseThrow(() -> new OAuthException("Invalid code"));
|
||||
.orElseThrow(() -> new OAuthException(ErrorResponse.Error.access_denied, "Invalid code"));
|
||||
validateClient(params, session);
|
||||
if (!session.validateCodeChallenge(params.getCodeVerifier())) {
|
||||
throw new OAuthException("Invalid code verifier");
|
||||
throw new OAuthException(ErrorResponse.Error.access_denied, "Invalid code verifier");
|
||||
}
|
||||
return session.tokens();
|
||||
}
|
||||
|
||||
private AccessTokenResponse redeemDeviceCode(TokenParams params) {
|
||||
var session = sessionsRepo.getByAuthorizationCode(params.getDeviceCode())
|
||||
.orElseThrow(() -> new OAuthException("Invalid code"));
|
||||
.orElseThrow(() -> new OAuthException(ErrorResponse.Error.access_denied, "Invalid device code"));
|
||||
validateClient(params, session);
|
||||
if (session.tokens() != null) {
|
||||
sessionsRepo.redeemAuthorizationCode(params.getDeviceCode());
|
||||
return session.tokens();
|
||||
} else {
|
||||
throw new OAuthException("Authorization pending");
|
||||
throw new OAuthException(ErrorResponse.Error.authorization_pending, "Authorization pending");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,24 +178,24 @@ public class OAuthResource {
|
||||
|
||||
private OAuthClient validateClient(AuthParams params) {
|
||||
var client = clientsRepo.getClient(params.getClientId())
|
||||
.orElseThrow(() -> new RuntimeException("Not a valid client"));
|
||||
.orElseThrow(() -> new OAuthException(ErrorResponse.Error.invalid_request, "Not a valid client"));
|
||||
if (!client.validateRedirectUri(params.getRedirectUri())) {
|
||||
throw new RuntimeException("Invalid redirect URI");
|
||||
throw new OAuthException(ErrorResponse.Error.invalid_request, "Invalid redirect URI");
|
||||
}
|
||||
if (StringUtil.isNullOrEmpty(params.getState())) {
|
||||
throw new RuntimeException("Invalid state");
|
||||
throw new OAuthException(ErrorResponse.Error.invalid_request, "Invalid state");
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
private OAuthClient validateClient(TokenParams params, AuthorizationSession session) {
|
||||
var client = clientsRepo.getClient(params.getClientId())
|
||||
.orElseThrow(() -> new RuntimeException("Not a valid client"));
|
||||
.orElseThrow(() -> new OAuthException(ErrorResponse.Error.invalid_request, "Not a valid client"));
|
||||
if (!session.validateRedirectUri(params.getRedirectUri())) {
|
||||
throw new RuntimeException("Invalid redirect URI");
|
||||
throw new OAuthException(ErrorResponse.Error.invalid_request, "Invalid redirect URI");
|
||||
}
|
||||
if (!client.validateSecret(params.getClientSecret())) {
|
||||
throw new RuntimeException("Invalid secret");
|
||||
throw new OAuthException(ErrorResponse.Error.unauthorized_client, "Invalid secret");
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
10
src/main/java/com/ysoft/geecon/error/ErrorResponse.java
Normal file
10
src/main/java/com/ysoft/geecon/error/ErrorResponse.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.ysoft.geecon.error;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public record ErrorResponse(@JsonProperty("error") Error error,
|
||||
@JsonProperty("error_description") String description) {
|
||||
public enum Error {
|
||||
invalid_request, unauthorized_client, access_denied, invalid_scope, server_error, temporarily_unavailable, authorization_pending
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,6 @@ class ExceptionMappers {
|
||||
|
||||
@CheckedTemplate
|
||||
public static class Templates {
|
||||
public static native TemplateInstance error(OAuthException.ErrorResponse response);
|
||||
public static native TemplateInstance error(ErrorResponse response);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,24 @@
|
||||
package com.ysoft.geecon.error;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class OAuthException extends RuntimeException {
|
||||
private final ErrorResponse response;
|
||||
|
||||
// https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-09.html#name-error-response-2
|
||||
public OAuthException(ErrorResponse response) {
|
||||
super("OAuth error: " + response.error() + " " + response.description());
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public OAuthException(String error, String description) {
|
||||
public OAuthException(ErrorResponse.Error error, String description) {
|
||||
this(new ErrorResponse(error, description));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public OAuthException(String message) {
|
||||
this(message, message);
|
||||
this(ErrorResponse.Error.server_error, message);
|
||||
}
|
||||
|
||||
public ErrorResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public record ErrorResponse(@JsonProperty("error") String error,
|
||||
@JsonProperty("error_description") String description) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.ysoft.geecon;
|
||||
import com.ysoft.geecon.dto.DeviceResponse;
|
||||
import com.ysoft.geecon.dto.OAuthClient;
|
||||
import com.ysoft.geecon.dto.User;
|
||||
import com.ysoft.geecon.error.ErrorResponse;
|
||||
import com.ysoft.geecon.helpers.ConsentScreen;
|
||||
import com.ysoft.geecon.helpers.DeviceAuthorizationGrantFlow;
|
||||
import com.ysoft.geecon.helpers.DeviceCodeScreen;
|
||||
@@ -71,6 +72,7 @@ public class DeviceAuthGrantTest {
|
||||
public void deviceAuthGrant_authorizationPending() throws IOException {
|
||||
DeviceAuthorizationGrantFlow flow = new DeviceAuthorizationGrantFlow(deviceUri, CLIENT);
|
||||
flow.start();
|
||||
flow.exchangeDeviceCodeError();
|
||||
ErrorResponse errorResponse = flow.exchangeDeviceCodeError();
|
||||
assertThat(errorResponse.error(), is(ErrorResponse.Error.authorization_pending));
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package com.ysoft.geecon.helpers;
|
||||
import com.ysoft.geecon.dto.AccessTokenResponse;
|
||||
import com.ysoft.geecon.dto.DeviceResponse;
|
||||
import com.ysoft.geecon.dto.OAuthClient;
|
||||
import com.ysoft.geecon.error.OAuthException;
|
||||
import com.ysoft.geecon.error.ErrorResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class DeviceAuthorizationGrantFlow {
|
||||
.extract().as(AccessTokenResponse.class);
|
||||
}
|
||||
|
||||
public OAuthException.ErrorResponse exchangeDeviceCodeError() {
|
||||
public ErrorResponse exchangeDeviceCodeError() {
|
||||
return given()
|
||||
.formParam("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
|
||||
.formParam("client_id", client.clientId())
|
||||
@@ -68,6 +68,6 @@ public class DeviceAuthorizationGrantFlow {
|
||||
.contentType(JSON)
|
||||
.body("error", is(notNullValue()))
|
||||
.body("error_description", is(notNullValue()))
|
||||
.extract().as(OAuthException.ErrorResponse.class);
|
||||
.extract().as(ErrorResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user