From c87b9d73eaf66c88749ae69959de2b6994483569 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Sun, 12 Oct 2025 21:17:12 +0200 Subject: feat: add authorize redirection --- .../java/moe/nea/prickly/auth/Authorizations.java | 5 ++++ .../moe/nea/prickly/model/AuthorizeAction.java | 7 ++++++ src/main/java/moe/nea/prickly/server/Server.java | 23 ++++++++++++++++++ src/main/java/moe/nea/prickly/util/JsonHelper.java | 6 +++++ .../moe/nea/prickly/util/QueryParameterHelper.java | 28 ++++++++++++++++++++++ 5 files changed, 69 insertions(+) create mode 100644 src/main/java/moe/nea/prickly/model/AuthorizeAction.java create mode 100644 src/main/java/moe/nea/prickly/util/QueryParameterHelper.java (limited to 'src/main/java/moe') diff --git a/src/main/java/moe/nea/prickly/auth/Authorizations.java b/src/main/java/moe/nea/prickly/auth/Authorizations.java index 8b720a5..0106e74 100644 --- a/src/main/java/moe/nea/prickly/auth/Authorizations.java +++ b/src/main/java/moe/nea/prickly/auth/Authorizations.java @@ -1,9 +1,14 @@ /* (C) 2025 Linnea Gräf - Licensed to everyone under the BSD 3 Clause License */ package moe.nea.prickly.auth; +import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import moe.nea.prickly.config.Config; public class Authorizations { public static final Algorithm JWT_ALGORITHM = Algorithm.HMAC256(Config.SECRET); + + public static String createToken(String username) { + return JWT.create().withSubject(username).sign(JWT_ALGORITHM); + } } diff --git a/src/main/java/moe/nea/prickly/model/AuthorizeAction.java b/src/main/java/moe/nea/prickly/model/AuthorizeAction.java new file mode 100644 index 0000000..196437f --- /dev/null +++ b/src/main/java/moe/nea/prickly/model/AuthorizeAction.java @@ -0,0 +1,7 @@ +/* (C) 2025 Linnea Gräf - Licensed to everyone under the BSD 3 Clause License */ +package moe.nea.prickly.model; + +public enum AuthorizeAction { + ACCEPT, + DENY +} diff --git a/src/main/java/moe/nea/prickly/server/Server.java b/src/main/java/moe/nea/prickly/server/Server.java index dbea0be..120078a 100644 --- a/src/main/java/moe/nea/prickly/server/Server.java +++ b/src/main/java/moe/nea/prickly/server/Server.java @@ -9,13 +9,19 @@ import io.javalin.Javalin; import io.javalin.config.JavalinConfig; import io.javalin.rendering.template.JavalinJte; import java.nio.file.Path; +import java.util.HashMap; import java.util.Map; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import moe.nea.prickly.auth.Authorizations; import moe.nea.prickly.config.Config; import moe.nea.prickly.model.AuthorizationRequest; +import moe.nea.prickly.model.AuthorizeAction; import moe.nea.prickly.util.BundleUtil; +import moe.nea.prickly.util.JsonHelper; import moe.nea.prickly.util.OAuthUtil; +import moe.nea.prickly.util.QueryParameterHelper; +import org.jspecify.annotations.Nullable; @Slf4j public class Server { @@ -56,6 +62,23 @@ public class Server { "authorizationRequest", new AuthorizationRequest(responseType, redirectUri, state, scope))); }); + javalin.post(prefix + "/authorize", ctx -> { + var authRequest = JsonHelper.parseFormParam(ctx.formParam("authRequest"), AuthorizationRequest.class); + var action = AuthorizeAction.valueOf(ctx.formParam("action")); + var map = new HashMap(); + map.put("state", authRequest.state()); + switch (action) { + case ACCEPT: // TODO: differentiate authRequest.responseType() + map.put("code", Authorizations.createToken(Objects.requireNonNull(ctx.formParam("username")))); + break; + case DENY: + map.put("error", "access_denied"); + map.put("error_description", "You have denied access to " + application.NAME); + break; + } + ctx.redirect(QueryParameterHelper.appendQuery(authRequest.redirectUri(), map) + .toString()); + }); } protected void configure(JavalinConfig config) { diff --git a/src/main/java/moe/nea/prickly/util/JsonHelper.java b/src/main/java/moe/nea/prickly/util/JsonHelper.java index 56ab175..0756213 100644 --- a/src/main/java/moe/nea/prickly/util/JsonHelper.java +++ b/src/main/java/moe/nea/prickly/util/JsonHelper.java @@ -3,6 +3,7 @@ package moe.nea.prickly.util; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; +import org.jspecify.annotations.Nullable; public class JsonHelper { public static ObjectMapper mapper = new ObjectMapper(); @@ -11,4 +12,9 @@ public class JsonHelper { public static String encode(Object object) { return mapper.writeValueAsString(object); } + + @SneakyThrows + public static T parseFormParam(@Nullable String text, Class clazz) { + return mapper.readValue(text, clazz); + } } diff --git a/src/main/java/moe/nea/prickly/util/QueryParameterHelper.java b/src/main/java/moe/nea/prickly/util/QueryParameterHelper.java new file mode 100644 index 0000000..191dae8 --- /dev/null +++ b/src/main/java/moe/nea/prickly/util/QueryParameterHelper.java @@ -0,0 +1,28 @@ +/* (C) 2025 Linnea Gräf - Licensed to everyone under the BSD 3 Clause License */ +package moe.nea.prickly.util; + +import com.google.common.net.UrlEscapers; +import java.net.URI; +import java.util.Map; +import org.jspecify.annotations.Nullable; + +public class QueryParameterHelper { + public static URI appendQuery(URI uri, Map extraArgs) { + var uriBuilder = new StringBuilder(); + uriBuilder.append(uri); + var escaper = UrlEscapers.urlFormParameterEscaper(); + var first = true; + for (var entry : extraArgs.entrySet()) { + if (entry.getValue() == null) continue; + if (first) { + char c = uriBuilder.charAt(uriBuilder.length() - 1); + if (c != '?' && c != '&') uriBuilder.append((uri.getQuery() == null) ? '?' : '&'); + first = false; + } else { + uriBuilder.append('&'); + } + uriBuilder.append(escaper.escape(entry.getKey())).append('=').append(escaper.escape(entry.getValue())); + } + return URI.create(uriBuilder.toString()); + } +} -- cgit