diff --git a/pom.xml b/pom.xml
index 5549ff8..fba2dad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,6 +40,10 @@
io.quarkus
quarkus-resteasy-reactive-qute
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson
+
io.quarkus
quarkus-junit5
diff --git a/src/main/java/com/ysoft/geecon/OAuthResource.java b/src/main/java/com/ysoft/geecon/OAuthResource.java
index d811871..62a022b 100644
--- a/src/main/java/com/ysoft/geecon/OAuthResource.java
+++ b/src/main/java/com/ysoft/geecon/OAuthResource.java
@@ -1,10 +1,9 @@
package com.ysoft.geecon;
-import com.ysoft.geecon.dto.AuthParams;
-import com.ysoft.geecon.dto.OAuthClient;
-import com.ysoft.geecon.dto.User;
+import com.ysoft.geecon.dto.*;
import com.ysoft.geecon.error.OAuthException;
import com.ysoft.geecon.repo.ClientsRepo;
+import com.ysoft.geecon.repo.SecureRandomStrings;
import com.ysoft.geecon.repo.SessionsRepo;
import com.ysoft.geecon.repo.UsersRepo;
import io.quarkus.qute.CheckedTemplate;
@@ -17,6 +16,7 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import java.util.List;
+import java.util.Optional;
@Path("/auth")
public class OAuthResource {
@@ -40,7 +40,7 @@ public class OAuthResource {
public TemplateInstance get(AuthParams params) {
var client = validateClient(params);
String sessionId = sessionsRepo.newAuthorizationSession(params, client);
- return Templates.login(params.getLoginHint(), sessionId, "");
+ return Templates.login(params.getLoginHint(), sessionId, "");
}
@POST
@@ -75,7 +75,34 @@ public class OAuthResource {
.queryParam("state", params.getState())
.build())
.build();
- }
+ }
+
+ @POST
+ @Path("/token")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public AccessTokenResponse token(TokenParams params) {
+ return switch (params.getGrantType()) {
+ case "authorization_code" -> redeemAuthorizationCode(params);
+ default -> throw new OAuthException("Unsupported grant type");
+ };
+ }
+
+ private AccessTokenResponse redeemAuthorizationCode(TokenParams params) {
+ validateClient(params);
+ var session = sessionsRepo.redeemAuthorizationCode(params.getCode())
+ .orElseThrow(() -> new OAuthException("Invalid code"));
+
+ String idToken = null;
+
+ return new AccessTokenResponse("Bearer",
+ 8400,
+ SecureRandomStrings.alphanumeric(50),
+ session.scope(),
+ SecureRandomStrings.alphanumeric(50),
+ idToken
+ );
+ }
private User validateUser(String username, String password) {
return usersRepo.getUser(username)
@@ -95,6 +122,17 @@ public class OAuthResource {
return client;
}
+ private OAuthClient validateClient(TokenParams params) {
+ var client = clientsRepo.getClient(params.getClientId())
+ .orElseThrow(() -> new RuntimeException("Not a valid client"));
+ if (!client.validateRedirectUri(params.getRedirectUri())) {
+ throw new RuntimeException("Invalid redirect URI");
+ }
+ if (!client.validateSecret(params.getClientSecret())) {
+ throw new RuntimeException("Invalid secret");
+ }
+ return client;
+ }
}
diff --git a/src/main/java/com/ysoft/geecon/dto/AccessTokenResponse.java b/src/main/java/com/ysoft/geecon/dto/AccessTokenResponse.java
index 8b7ce51..f74d636 100644
--- a/src/main/java/com/ysoft/geecon/dto/AccessTokenResponse.java
+++ b/src/main/java/com/ysoft/geecon/dto/AccessTokenResponse.java
@@ -1,4 +1,4 @@
package com.ysoft.geecon.dto;
-public record AccessTokenResponse(String token, String scope, String idToken, long expiresIn) {
-}
+public record AccessTokenResponse(String tokenType, long expiresIn, String accessToken, String scope, String refreshToken, String idToken) {
+}
\ No newline at end of file
diff --git a/src/main/java/com/ysoft/geecon/dto/AuthParams.java b/src/main/java/com/ysoft/geecon/dto/AuthParams.java
index 0017160..d2099c4 100644
--- a/src/main/java/com/ysoft/geecon/dto/AuthParams.java
+++ b/src/main/java/com/ysoft/geecon/dto/AuthParams.java
@@ -6,14 +6,10 @@ import java.util.Arrays;
import java.util.List;
public class AuthParams {
- public enum ResponseType {
- code
- }
-
@RestQuery("login_hint")
String loginHint;
@RestQuery("response_type")
- ResponseType responseType;
+ String responseType;
@RestQuery("client_id")
String clientId;
@RestQuery("redirect_uri")
@@ -31,11 +27,11 @@ public class AuthParams {
this.loginHint = loginHint;
}
- public ResponseType getResponseType() {
+ public String getResponseType() {
return responseType;
}
- public void setResponseType(ResponseType responseType) {
+ public void setResponseType(String responseType) {
this.responseType = responseType;
}
diff --git a/src/main/java/com/ysoft/geecon/dto/AuthorizationSession.java b/src/main/java/com/ysoft/geecon/dto/AuthorizationSession.java
index 0924fec..547bb43 100644
--- a/src/main/java/com/ysoft/geecon/dto/AuthorizationSession.java
+++ b/src/main/java/com/ysoft/geecon/dto/AuthorizationSession.java
@@ -14,4 +14,8 @@ public record AuthorizationSession(AuthParams params, OAuthClient client, User u
public AuthorizationSession withScopes(List acceptedScopes) {
return new AuthorizationSession(params, client, user, acceptedScopes);
}
+
+ public String scope() {
+ return acceptedScopes == null ? null : String.join(" ", acceptedScopes);
+ }
}
diff --git a/src/main/java/com/ysoft/geecon/dto/OAuthClient.java b/src/main/java/com/ysoft/geecon/dto/OAuthClient.java
index 88f0eee..c025309 100644
--- a/src/main/java/com/ysoft/geecon/dto/OAuthClient.java
+++ b/src/main/java/com/ysoft/geecon/dto/OAuthClient.java
@@ -1,7 +1,13 @@
package com.ysoft.geecon.dto;
+import java.util.Objects;
+
public record OAuthClient(String clientId, String description, String clientSecret, String redirectUri) {
public boolean validateRedirectUri(String redirectUri) {
return this.redirectUri != null && this.redirectUri.equals(redirectUri);
}
+
+ public boolean validateSecret(String clientSecret) {
+ return Objects.equals(clientSecret, this.clientSecret);
+ }
}
diff --git a/src/main/java/com/ysoft/geecon/dto/TokenParams.java b/src/main/java/com/ysoft/geecon/dto/TokenParams.java
new file mode 100644
index 0000000..a1a2e04
--- /dev/null
+++ b/src/main/java/com/ysoft/geecon/dto/TokenParams.java
@@ -0,0 +1,65 @@
+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 {
+
+ @FormParam("grant_type")
+ private String grantType;
+
+ @FormParam("client_id")
+ private String clientId;
+
+ @FormParam("client_secret")
+ private String clientSecret;
+
+ @FormParam("redirect_uri")
+ private String redirectUri;
+
+ @FormParam("code")
+ private String code;
+
+ public String getGrantType() {
+ return grantType;
+ }
+
+ public void setGrantType(String grantType) {
+ this.grantType = grantType;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public void setClientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ }
+
+ public String getRedirectUri() {
+ return redirectUri;
+ }
+
+ public void setRedirectUri(String redirectUri) {
+ this.redirectUri = redirectUri;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+}
diff --git a/src/main/java/com/ysoft/geecon/repo/ClientsRepo.java b/src/main/java/com/ysoft/geecon/repo/ClientsRepo.java
index 88d33f7..6d8ab0a 100644
--- a/src/main/java/com/ysoft/geecon/repo/ClientsRepo.java
+++ b/src/main/java/com/ysoft/geecon/repo/ClientsRepo.java
@@ -14,6 +14,7 @@ public class ClientsRepo {
public ClientsRepo() {
register(new OAuthClient("my-public-client", "Example public client", null, "https://localhost:8888/oauth_success"));
+ register(new OAuthClient("oauthdebugger", "Example public client", null, "https://oauthdebugger.com/debug"));
}
public Optional getClient(String clientId) {