From 8d4ae5462f8a6b7233b19121af41abb8eb5a3a87 Mon Sep 17 00:00:00 2001 From: Steve Hb Date: Sat, 28 Mar 2020 01:23:32 +0100 Subject: [PATCH 1/6] Removed jwtRequest from Schema, fixed Playground bug related to Cookies --- src/main/resources/application.properties | 3 ++- src/main/resources/schema.graphqls | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f6348fc..b90b377 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,2 +1,3 @@ -security.basic.enable:false +security.basic.enable=false server.port=80 +graphql.playground.settings.request.credentials=Same-Origin \ No newline at end of file diff --git a/src/main/resources/schema.graphqls b/src/main/resources/schema.graphqls index ba88de5..dcc8722 100644 --- a/src/main/resources/schema.graphqls +++ b/src/main/resources/schema.graphqls @@ -38,7 +38,7 @@ type Mutation { authenticate(userAuth: UserAuth): UserAuthResult - jwt(jwtRequest: JwtRequest): JwtResult + jwt: JwtResult } @@ -126,7 +126,6 @@ enum UserAuthResultType { # HTTP Secure Cookie: refresh_token # HTTP Secure Cookie: device_id -input JwtRequest type JwtResult { token: String # JWT; ONLY stored in the frontend state manager! From 716e67cd9f4771cee9923d97f195c510614bca71 Mon Sep 17 00:00:00 2001 From: Steve Hb Date: Sat, 28 Mar 2020 03:01:40 +0100 Subject: [PATCH 2/6] Rewrote cookie handling --- .../mutation/user/AuthenticationResolver.java | 40 ++++++-------- .../graphql/resolver/util/HeaderUtil.java | 53 ++++++++----------- 2 files changed, 38 insertions(+), 55 deletions(-) diff --git a/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/mutation/user/AuthenticationResolver.java b/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/mutation/user/AuthenticationResolver.java index 8407e37..be11844 100644 --- a/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/mutation/user/AuthenticationResolver.java +++ b/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/mutation/user/AuthenticationResolver.java @@ -1,16 +1,15 @@ package de.themorpheus.edu.gateway.graphql.resolver.mutation.user; -import de.themorpheus.edu.gateway.graphql.dto.user.JwtRequestDTO; import de.themorpheus.edu.gateway.graphql.dto.user.JwtResultDTO; import de.themorpheus.edu.gateway.graphql.dto.user.UserAuthDTO; import de.themorpheus.edu.gateway.graphql.dto.user.UserAuthResultDTO; import org.springframework.stereotype.Component; +import javax.servlet.http.Cookie; import javax.validation.Valid; import de.themorpheus.edu.gateway.graphql.resolver.util.HeaderUtil; import de.themorpheus.edu.gateway.graphql.resolver.util.JsonWebToken; import de.themorpheus.edu.gateway.graphql.resolver.util.RefreshToken; -import java.util.Map; -import java.util.Optional; +import java.time.Duration; import java.util.UUID; import graphql.kickstart.tools.GraphQLMutationResolver; import graphql.schema.DataFetchingEnvironment; @@ -19,19 +18,14 @@ public class AuthenticationResolver implements GraphQLMutationResolver { public UserAuthResultDTO authenticate(@Valid UserAuthDTO userAuth, DataFetchingEnvironment environment) { - Optional optionalDeviceId = HeaderUtil.findHeader(environment, HeaderUtil.DEVICE_ID); + String deviceId = HeaderUtil.getCookie(environment, HeaderUtil.DEVICE_ID); deviceId: { - HeaderUtil.setCookie( - environment, - Map.of( - HeaderUtil.DEVICE_ID, optionalDeviceId.orElse("EXAMPLE_DEVICE_COOKIE"), - //HeaderUtil.CookieOption.SAME_SITE.getValue(), "Strict" //TODO: Activate - HeaderUtil.CookieOption.SAME_SITE.getValue(), "None" - ), - HeaderUtil.CookieOption.SECURE, - HeaderUtil.CookieOption.HTTP_ONLY - ); + Cookie cookie = new Cookie(HeaderUtil.DEVICE_ID, deviceId == null ? "EXAMPLE_DEVICE_COOKIE" : deviceId); + cookie.setHttpOnly(true); + cookie.setMaxAge((int) Duration.ofDays(365).toSeconds()); + //cookie.setSecure(true); //TODO: Only in productive + HeaderUtil.addCookie(environment, cookie); } //TODO: Backend authentication @@ -40,22 +34,20 @@ public UserAuthResultDTO authenticate(@Valid UserAuthDTO userAuth, DataFetchingE if (resultType == UserAuthResultDTO.UserAuthResultType.SUCCESS) { refreshToken: { - HeaderUtil.setCookie( - environment, - Map.of( - HeaderUtil.REFRESH_TOKEN, RefreshToken.generate(userId), - HeaderUtil.CookieOption.SAME_SITE.getValue(), "None" //TODO: Strict - ), - HeaderUtil.CookieOption.SECURE, - HeaderUtil.CookieOption.HTTP_ONLY - ); + Cookie cookie = new Cookie(HeaderUtil.REFRESH_TOKEN, RefreshToken.generate(userId)); + cookie.setHttpOnly(true); + cookie.setMaxAge((int) Duration.ofDays(365).toSeconds()); + //cookie.setSecure(true); //TODO: Only in productive + + HeaderUtil.addCookie(environment, cookie); + //TODO: Same-Site? } } return new UserAuthResultDTO(resultType); } - public JwtResultDTO jwt(@Valid JwtRequestDTO requestDTO, DataFetchingEnvironment environment) { + public JwtResultDTO jwt(DataFetchingEnvironment environment) { // Get cookie String refreshTokenCookie = HeaderUtil.getCookie(environment, HeaderUtil.REFRESH_TOKEN); if (refreshTokenCookie == null) return new JwtResultDTO(null, JwtResultDTO.JwtStatus.INVALID); //TODO: 403 Forbidden diff --git a/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java b/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java index 54f1369..0e3dba7 100644 --- a/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java +++ b/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java @@ -2,11 +2,12 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; import java.util.Enumeration; -import java.util.Map; -import java.util.Optional; +import java.util.List; import graphql.schema.DataFetchingEnvironment; import graphql.servlet.context.DefaultGraphQLServletContext; @@ -15,19 +16,20 @@ public class HeaderUtil { public static final String DEVICE_ID = "device_id"; public static final String REFRESH_TOKEN = "refresh_token"; - public static final String SET_COOKIE = "set-cookie"; public static final String COOKIE = "cookie"; - public static Optional findHeader(DataFetchingEnvironment environment, String header) { + public static List findRequestHeader(DataFetchingEnvironment environment, String header) { DefaultGraphQLServletContext context = environment.getContext(); HttpServletRequest request = context.getHttpServletRequest(); - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String currentHeader = headerNames.nextElement(); - if (currentHeader.equalsIgnoreCase(header)) return Optional.of(request.getHeader(currentHeader)); + Enumeration headers = request.getHeaders(header); + + List result = new ArrayList<>(); + while (headers.hasMoreElements()) { + String currentHeader = headers.nextElement(); + result.add(currentHeader); } - return Optional.empty(); + return result; } public static void setHeader(DataFetchingEnvironment environment, String header, String value) { @@ -36,35 +38,24 @@ public static void setHeader(DataFetchingEnvironment environment, String header, response.setHeader(header, value); } - public static void setCookie(DataFetchingEnvironment environment, Map cookies, CookieOption... options) { + public static void addCookie(DataFetchingEnvironment environment, Cookie cookie) { DefaultGraphQLServletContext context = environment.getContext(); HttpServletResponse response = context.getHttpServletResponse(); - StringBuilder value = new StringBuilder(); - cookies.forEach((cookieKey, cookieValue) -> { - value.append(cookieKey); - value.append('='); - value.append(cookieValue); - value.append(';'); - }); - - for (CookieOption option : options) { - if (option.isValueRequired()) throw new IllegalArgumentException("Consider passing this option via 'cookies': " + option); - value.append(option.getValue()); - value.append(';'); - } - - response.setHeader(SET_COOKIE, value.toString()); + response.addCookie(cookie); } public static String getCookie(DataFetchingEnvironment environment, String key) { - Optional cookieOptional = HeaderUtil.findHeader(environment, COOKIE); - if (!cookieOptional.isPresent()) return null; + List cookiesList = HeaderUtil.findRequestHeader(environment, COOKIE); + if (cookiesList.isEmpty()) return null; - String[] parts = cookieOptional.get().split(";"); + for (String cookies : cookiesList) { + for (String cookie : cookies.split(";")) { + cookie = cookie.trim(); - for (String part : parts) - if (part.startsWith(key) && part.contains("=")) - return part.split("=")[1]; + if (cookie.startsWith(key) && cookie.contains("=")) + return cookie.split("=")[1]; + } + } return null; } From 13e142124b5e2006e13284452c6cf145c7ac16c7 Mon Sep 17 00:00:00 2001 From: Steve Hb Date: Sat, 28 Mar 2020 03:02:00 +0100 Subject: [PATCH 3/6] Removed JwtRequest --- .../edu/gateway/graphql/dto/user/JwtRequestDTO.java | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/main/java/de/themorpheus/edu/gateway/graphql/dto/user/JwtRequestDTO.java diff --git a/src/main/java/de/themorpheus/edu/gateway/graphql/dto/user/JwtRequestDTO.java b/src/main/java/de/themorpheus/edu/gateway/graphql/dto/user/JwtRequestDTO.java deleted file mode 100644 index 1286add..0000000 --- a/src/main/java/de/themorpheus/edu/gateway/graphql/dto/user/JwtRequestDTO.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.themorpheus.edu.gateway.graphql.dto.user; - -public class JwtRequestDTO { - -} From 493bef49ec76b70ae19c2addc57cbdc775a9c28d Mon Sep 17 00:00:00 2001 From: Steve Hb Date: Sat, 28 Mar 2020 03:04:35 +0100 Subject: [PATCH 4/6] Removed unused CORS handling --- .../edu/gateway/GatewayApplication.java | 20 ------------------- src/main/resources/application.properties | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java b/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java index 616f905..5d90319 100644 --- a/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java +++ b/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java @@ -1,33 +1,13 @@ package de.themorpheus.edu.gateway; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @SpringBootApplication public class GatewayApplication { - @Value("${graphql.url:graphql}") - private static final String GRAPHQL_URL = "/graphql"; - public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } - @Bean - public WebMvcConfigurer webConfiguration() { - return new WebMvcConfigurer() { - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping(GRAPHQL_URL).allowedOrigins( - "http://localhost:3000", - "api.e-edu.the-morpheus.de" - ).allowedMethods("GET", "OPTION", "POST", "PATCH", "PUT", "DELETE"); - } - }; - } - } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b90b377..d090f14 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,3 @@ security.basic.enable=false server.port=80 -graphql.playground.settings.request.credentials=Same-Origin \ No newline at end of file +graphql.playground.settings.request.credentials=Same-Origin From 7ef280d22f4632498246c0d15f46d38ac8ac1c22 Mon Sep 17 00:00:00 2001 From: Steve Hb Date: Sat, 28 Mar 2020 03:08:10 +0100 Subject: [PATCH 5/6] Re-added CORS handling --- .../edu/gateway/GatewayApplication.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java b/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java index 5d90319..a2a5ce7 100644 --- a/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java +++ b/src/main/java/de/themorpheus/edu/gateway/GatewayApplication.java @@ -2,6 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @SpringBootApplication public class GatewayApplication { @@ -10,4 +13,19 @@ public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } + @Bean + public WebMvcConfigurer webConfiguration() { + return new WebMvcConfigurer() { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry + .addMapping("/graphql") + .allowedOrigins( + "*" //TODO: Not in production + ) + .allowedMethods("GET", "OPTION", "POST", "PATCH", "PUT", "DELETE"); + } + }; + } + } From de681fbe4b72e0acf652204de02645df0d33a68d Mon Sep 17 00:00:00 2001 From: Steve Hb Date: Sat, 28 Mar 2020 03:11:36 +0100 Subject: [PATCH 6/6] Fixed checkstyle --- .../edu/gateway/graphql/resolver/util/HeaderUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java b/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java index 0e3dba7..c0a325e 100644 --- a/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java +++ b/src/main/java/de/themorpheus/edu/gateway/graphql/resolver/util/HeaderUtil.java @@ -49,8 +49,8 @@ public static String getCookie(DataFetchingEnvironment environment, String key) if (cookiesList.isEmpty()) return null; for (String cookies : cookiesList) { - for (String cookie : cookies.split(";")) { - cookie = cookie.trim(); + for (String c : cookies.split(";")) { + String cookie = c.trim(); if (cookie.startsWith(key) && cookie.contains("=")) return cookie.split("=")[1];