mirror of
https://github.com/ysoftdevs/oauth-playground-server.git
synced 2026-01-18 17:47:21 +01:00
PKCE
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
18
src/main/java/com/ysoft/geecon/dto/Pkce.java
Normal file
18
src/main/java/com/ysoft/geecon/dto/Pkce.java
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
quarkus.http.cors=true
|
||||
quarkus.http.cors.origins=/.*/
|
||||
13
src/test/java/com/ysoft/geecon/dto/PkceTest.java
Normal file
13
src/test/java/com/ysoft/geecon/dto/PkceTest.java
Normal 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"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user