This commit is contained in:
Dusan Jakub
2023-09-15 17:12:38 +02:00
parent bcba2d00b3
commit 47cc55d87f
9 changed files with 94 additions and 4 deletions

View File

@@ -59,6 +59,11 @@
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
</dependencies>
<build>
<plugins>

View File

@@ -103,6 +103,9 @@ public class OAuthResource {
validateClient(params);
var session = sessionsRepo.redeemAuthorizationCode(params.getCode())
.orElseThrow(() -> new OAuthException("Invalid code"));
if (!session.validateCodeChallenge(params.getCodeVerifier())) {
throw new OAuthException("Invalid code verifier");
}
String idToken = null;

View File

@@ -24,6 +24,12 @@ public class AuthParams {
String scope;
@RestQuery("state")
String state;
@RestQuery("code_challenge_method")
String codeChallengeMethod;
@RestQuery("code_challenge")
String codeChallenge;
@RestQuery("nonce")
String nonce;
public String getLoginHint() {
return loginHint;
@@ -76,4 +82,28 @@ public class AuthParams {
public void setState(String state) {
this.state = state;
}
public String getCodeChallengeMethod() {
return codeChallengeMethod;
}
public void setCodeChallengeMethod(String codeChallengeMethod) {
this.codeChallengeMethod = codeChallengeMethod;
}
public String getCodeChallenge() {
return codeChallenge;
}
public void setCodeChallenge(String codeChallenge) {
this.codeChallenge = codeChallenge;
}
public String getNonce() {
return nonce;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
}

View File

@@ -36,4 +36,15 @@ public record AuthorizationSession(AuthParams params,
public String scope() {
return acceptedScopes == null ? null : String.join(" ", acceptedScopes);
}
public boolean validateCodeChallenge(String codeVerifier) {
if (params.codeChallengeMethod == null) {
return true;
}
if (codeVerifier == null) {
return false;
}
return Pkce.validate(params.codeChallengeMethod, params.codeChallenge, codeVerifier);
}
}

View File

@@ -0,0 +1,18 @@
package com.ysoft.geecon.dto;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
public class Pkce {
static boolean validate(String challengeMethod, String codeChallenge, String codeVerifier) {
return switch (challengeMethod) {
case "plain" -> codeVerifier.equals(codeChallenge);
case "S256" -> codeChallenge.equals(s256(codeVerifier));
default -> false;
};
}
static String s256(String codeVerifier) {
return Base64.encodeBase64URLSafeString(DigestUtils.sha256(codeVerifier));
}
}

View File

@@ -1,10 +1,6 @@
package com.ysoft.geecon.dto;
import jakarta.ws.rs.FormParam;
import org.jboss.resteasy.reactive.RestQuery;
import java.util.Arrays;
import java.util.List;
public class TokenParams {
@@ -23,6 +19,9 @@ public class TokenParams {
@FormParam("code")
private String code;
@FormParam("code_verifier")
private String codeVerifier;
public String getGrantType() {
return grantType;
}
@@ -62,4 +61,12 @@ public class TokenParams {
public void setCode(String code) {
this.code = code;
}
public String getCodeVerifier() {
return codeVerifier;
}
public void setCodeVerifier(String codeVerifier) {
this.codeVerifier = codeVerifier;
}
}

View File

@@ -1,6 +1,7 @@
package com.ysoft.geecon.error;
public class OAuthException extends RuntimeException {
// https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-09.html#name-error-response-2
public OAuthException(String message) {
super(message);
}

View File

@@ -0,0 +1,2 @@
quarkus.http.cors=true
quarkus.http.cors.origins=/.*/

View File

@@ -0,0 +1,13 @@
package com.ysoft.geecon.dto;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PkceTest {
@Test
void validate() {
assertTrue(Pkce.validate("S256", "W2ap_IuB0HJkMIkuxaXV2l8_Gx5mVmMStG_HQrAnQxA", "7AmAEXcl2Km9LQMwtUhif7GQ97HZy9RT72KZBwmxBRI"));
}
}