diff --git a/src/main/java/com/ysoft/geecon/OAuthResource.java b/src/main/java/com/ysoft/geecon/OAuthResource.java index 519e724..5e3945b 100644 --- a/src/main/java/com/ysoft/geecon/OAuthResource.java +++ b/src/main/java/com/ysoft/geecon/OAuthResource.java @@ -1,10 +1,19 @@ package com.ysoft.geecon; +import com.ysoft.geecon.dto.OAuthClient; +import com.ysoft.geecon.repo.ClientsRepo; +import com.ysoft.geecon.repo.UsersRepo; import io.quarkus.qute.CheckedTemplate; import io.quarkus.qute.TemplateInstance; +import io.quarkus.runtime.util.StringUtil; +import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import org.jboss.resteasy.reactive.RestQuery; +import java.net.URI; import java.util.List; @Path("/auth") @@ -13,26 +22,128 @@ public class OAuthResource { @CheckedTemplate public static class Templates { public static native TemplateInstance login(String loginHint, String error); + public static native TemplateInstance consents(List scopes, String error); } - + @Inject + ClientsRepo clientsRepo; + @Inject + UsersRepo usersRepo; @GET @Produces(MediaType.TEXT_HTML) - public TemplateInstance get(@QueryParam("login_hint") String loginHint) { - return Templates.login(loginHint, ""); + public TemplateInstance get(AuthParams params) { + validateClient(params); + + return Templates.login(params.loginHint, ""); } @POST @Produces(MediaType.TEXT_HTML) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public TemplateInstance post(@FormParam("username") String username, - @FormParam("password") String password ) { - if ("Password1".equals(password)) { - return Templates.consents(List.of("scope1"), ""); - } else { + public Object post(AuthParams params, + @FormParam("username") String username, + @FormParam("password") String password) { + validateClient(params); + var user = usersRepo.getUser(username); + if (user.isEmpty()) { return Templates.login(username, "invalid_credentials"); } + if (!user.get().validatePassword(password)) { + return Templates.login(username, "invalid_credentials"); + } + + return Response.seeOther(UriBuilder.fromUri(params.redirectUri) + .queryParam("code", "randomCode") + .queryParam("state", params.state) + .build()) + .build(); } + + private OAuthClient validateClient(AuthParams params) { + var client = clientsRepo.getClient(params.clientId) + .orElseThrow(() -> new RuntimeException("Not a valid client")); + if (!client.validateRedirectUri(params.redirectUri)) { + throw new RuntimeException("Invalid redirect URI"); + } + if (StringUtil.isNullOrEmpty(params.state)) { + throw new RuntimeException("Invalid state"); + } + return client; + } + + public static class AuthParams { + public enum ResponseType { + code + } + + @RestQuery("login_hint") + String loginHint; + @RestQuery("response_type") + ResponseType responseType; + @RestQuery("client_id") + String clientId; + @RestQuery("redirect_uri") + String redirectUri; + @RestQuery("scope") + String scope; + @RestQuery("state") + String state; + + public String getLoginHint() { + return loginHint; + } + + public void setLoginHint(String loginHint) { + this.loginHint = loginHint; + } + + public ResponseType getResponseType() { + return responseType; + } + + public void setResponseType(ResponseType responseType) { + this.responseType = responseType; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getRedirectUri() { + return redirectUri; + } + + public void setRedirectUri(String redirectUri) { + this.redirectUri = redirectUri; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + } + } + + + + + + diff --git a/src/main/java/com/ysoft/geecon/dto/OAuthClient.java b/src/main/java/com/ysoft/geecon/dto/OAuthClient.java new file mode 100644 index 0000000..9147bb3 --- /dev/null +++ b/src/main/java/com/ysoft/geecon/dto/OAuthClient.java @@ -0,0 +1,7 @@ +package com.ysoft.geecon.dto; + +public record OAuthClient(String clientId, String clientSecret, String redirectUri) { + public boolean validateRedirectUri(String redirectUri) { + return this.redirectUri != null && this.redirectUri.equals(redirectUri); + } +} diff --git a/src/main/java/com/ysoft/geecon/dto/User.java b/src/main/java/com/ysoft/geecon/dto/User.java new file mode 100644 index 0000000..c50beb5 --- /dev/null +++ b/src/main/java/com/ysoft/geecon/dto/User.java @@ -0,0 +1,7 @@ +package com.ysoft.geecon.dto; + +public record User(String login, String password) { + public boolean validatePassword(String password) { + return this.password != null && this.password.equals(password); + } +} diff --git a/src/main/java/com/ysoft/geecon/repo/ClientsRepo.java b/src/main/java/com/ysoft/geecon/repo/ClientsRepo.java new file mode 100644 index 0000000..442bae4 --- /dev/null +++ b/src/main/java/com/ysoft/geecon/repo/ClientsRepo.java @@ -0,0 +1,26 @@ +package com.ysoft.geecon.repo; + + +import com.ysoft.geecon.dto.OAuthClient; +import jakarta.enterprise.context.ApplicationScoped; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class ClientsRepo { + private final Map clients = new HashMap<>(); + + public ClientsRepo() { + register(new OAuthClient("my-public-client", null, "https://localhost:8888/oauth_success")); + } + + public Optional getClient(String clientId) { + return Optional.ofNullable(clients.get(clientId)); + } + + private void register(OAuthClient client) { + clients.put(client.clientId(), client); + } +} diff --git a/src/main/java/com/ysoft/geecon/repo/UsersRepo.java b/src/main/java/com/ysoft/geecon/repo/UsersRepo.java new file mode 100644 index 0000000..9d9d774 --- /dev/null +++ b/src/main/java/com/ysoft/geecon/repo/UsersRepo.java @@ -0,0 +1,26 @@ +package com.ysoft.geecon.repo; + +import com.ysoft.geecon.dto.OAuthClient; +import com.ysoft.geecon.dto.User; +import jakarta.enterprise.context.ApplicationScoped; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class UsersRepo { + private final Map users = new HashMap<>(); + + public UsersRepo() { + register(new User("bob", "Password1")); + } + + public Optional getUser(String username) { + return Optional.ofNullable(users.get(username)); + } + + private void register(User user) { + users.put(user.login(), user); + } +}