diff --git a/pom.xml b/pom.xml
index c4a279a..c8ff076 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,7 @@
scope
auth
util
+ spring-auth
diff --git a/spring-auth/pom.xml b/spring-auth/pom.xml
new file mode 100644
index 0000000..96526e7
--- /dev/null
+++ b/spring-auth/pom.xml
@@ -0,0 +1,77 @@
+
+
+
+ gewia-common
+ com.gewia.common
+ 1.0
+
+ 4.0.0
+ spring-auth
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ 2.1.10.RELEASE
+ pom
+ import
+
+
+
+
+
+
+ com.gewia.common
+ auth
+ ${project.parent.version}
+ compile
+
+
+ com.gewia.common
+ scope
+ ${project.parent.version}
+ compile
+
+
+
+ com.auth0
+ java-jwt
+ 3.10.3
+ compile
+
+
+
+ javax.validation
+ validation-api
+ 2.0.1.Final
+ compile
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-auth/src/main/java/com/gewia/common/spring/auth/AuthScope.java b/spring-auth/src/main/java/com/gewia/common/spring/auth/AuthScope.java
new file mode 100644
index 0000000..8a1116e
--- /dev/null
+++ b/spring-auth/src/main/java/com/gewia/common/spring/auth/AuthScope.java
@@ -0,0 +1,18 @@
+package com.gewia.common.spring.auth;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Repeatable(Authentication.class)
+public @interface AuthScope {
+
+ String value() default "";
+
+ String scope() default "";
+
+}
diff --git a/spring-auth/src/main/java/com/gewia/common/spring/auth/Authentication.java b/spring-auth/src/main/java/com/gewia/common/spring/auth/Authentication.java
new file mode 100644
index 0000000..cb596e9
--- /dev/null
+++ b/spring-auth/src/main/java/com/gewia/common/spring/auth/Authentication.java
@@ -0,0 +1,14 @@
+package com.gewia.common.spring.auth;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Authentication {
+
+ AuthScope[] value();
+
+}
diff --git a/spring-auth/src/main/java/com/gewia/common/spring/auth/IgnoreServiceToken.java b/spring-auth/src/main/java/com/gewia/common/spring/auth/IgnoreServiceToken.java
new file mode 100644
index 0000000..3216d4c
--- /dev/null
+++ b/spring-auth/src/main/java/com/gewia/common/spring/auth/IgnoreServiceToken.java
@@ -0,0 +1,12 @@
+package com.gewia.common.spring.auth;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface IgnoreServiceToken {
+
+}
diff --git a/spring-auth/src/main/java/com/gewia/common/spring/auth/SpringAuthentication.java b/spring-auth/src/main/java/com/gewia/common/spring/auth/SpringAuthentication.java
new file mode 100644
index 0000000..594a060
--- /dev/null
+++ b/spring-auth/src/main/java/com/gewia/common/spring/auth/SpringAuthentication.java
@@ -0,0 +1,23 @@
+package com.gewia.common.spring.auth;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.AccessLevel;
+import lombok.Getter;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+@ComponentScan("com.gewia.common.spring.auth")
+public abstract class SpringAuthentication implements InitializingBean {
+
+ @Getter(AccessLevel.PACKAGE) private static List interceptors = new ArrayList<>();
+
+ @Override
+ public void afterPropertiesSet() {
+ interceptors = this.addAuthenticationInterceptors(interceptors);
+ }
+
+ abstract public List addAuthenticationInterceptors(List authenticationInterceptors);
+
+}
diff --git a/spring-auth/src/main/java/com/gewia/common/spring/auth/SpringAuthenticationWebConfig.java b/spring-auth/src/main/java/com/gewia/common/spring/auth/SpringAuthenticationWebConfig.java
new file mode 100644
index 0000000..4daa459
--- /dev/null
+++ b/spring-auth/src/main/java/com/gewia/common/spring/auth/SpringAuthenticationWebConfig.java
@@ -0,0 +1,42 @@
+package com.gewia.common.spring.auth;
+
+import com.auth0.jwt.interfaces.DecodedJWT;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+@Configuration
+@EnableWebMvc
+public class SpringAuthenticationWebConfig implements WebMvcConfigurer, HandlerMethodArgumentResolver {
+
+ @Override
+ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+ return ((HttpServletRequest) webRequest.getNativeRequest()).getAttribute("accessToken");
+ }
+
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ return parameter.getParameterType().equals(DecodedJWT.class);
+ }
+
+ @Override
+ public void addArgumentResolvers(List resolvers) {
+ resolvers.add(this);
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ for (HandlerInterceptorAdapter interceptors : SpringAuthentication.getInterceptors())
+ registry.addInterceptor(interceptors).addPathPatterns("/**/*");
+ }
+
+}
diff --git a/spring-auth/src/main/java/com/gewia/common/spring/auth/interceptor/ScopeInterceptor.java b/spring-auth/src/main/java/com/gewia/common/spring/auth/interceptor/ScopeInterceptor.java
new file mode 100644
index 0000000..e89c751
--- /dev/null
+++ b/spring-auth/src/main/java/com/gewia/common/spring/auth/interceptor/ScopeInterceptor.java
@@ -0,0 +1,78 @@
+package com.gewia.common.spring.auth.interceptor;
+
+import com.auth0.jwt.interfaces.Claim;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.gewia.common.auth.jwt.JwtUtil;
+import com.gewia.common.spring.auth.AuthScope;
+import com.gewia.common.spring.auth.Authentication;
+import com.gewia.common.util.Pair;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+@AllArgsConstructor
+public class ScopeInterceptor extends HandlerInterceptorAdapter {
+
+ private final JwtUtil jwtUtil;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+ HandlerMethod method = (HandlerMethod) handler;
+
+ AuthScope[] authScopes;
+ Authentication auth = method.getMethodAnnotation(Authentication.class);
+ AuthScope methodAuthScope = method.getMethodAnnotation(AuthScope.class);
+ if (auth != null) authScopes = auth.value();
+ else {
+ if (methodAuthScope == null) return true;
+ authScopes = new AuthScope[]{methodAuthScope};
+ }
+
+
+ String jwt = request.getHeader("Authorization");
+ if (jwt == null || jwt.isBlank()) return false;
+
+ Pair result = this.jwtUtil.verify(jwt);
+ switch (result.getRight()) {
+ case EXPIRED:
+ response.setStatus(HttpStatus.UNAUTHORIZED.value());
+ return false;
+ case INVALID:
+ response.setStatus(HttpStatus.NOT_ACCEPTABLE.value());
+ return false;
+ case FAILED:
+ response.setStatus(HttpStatus.EXPECTATION_FAILED.value());
+ return false;
+ case UNKNOWN:
+ response.setStatus(HttpStatus.FORBIDDEN.value());
+ return false;
+ default:
+ response.setStatus(HttpStatus.OK.value());
+ }
+
+ Claim claim = result.getLeft().getClaim("scopes");
+ List userScopes = claim.asList(String.class);
+ for (AuthScope authScope : authScopes) {
+ String scope = authScope.scope();
+ if (scope.isBlank()) scope = authScope.value();
+ if (!scope.isBlank()) {
+ boolean isPresent = false;
+ for (String userScope : userScopes)
+ if (userScope.equalsIgnoreCase(scope)) {
+ isPresent = true;
+ break;
+ }
+ if (!isPresent) return false;
+ }
+ }
+
+ request.setAttribute("accessToken", result.getLeft());
+
+ return true;
+ }
+
+}
diff --git a/spring-auth/src/main/java/com/gewia/common/spring/auth/interceptor/ServiceTokenInterceptor.java b/spring-auth/src/main/java/com/gewia/common/spring/auth/interceptor/ServiceTokenInterceptor.java
new file mode 100644
index 0000000..42212a4
--- /dev/null
+++ b/spring-auth/src/main/java/com/gewia/common/spring/auth/interceptor/ServiceTokenInterceptor.java
@@ -0,0 +1,36 @@
+package com.gewia.common.spring.auth.interceptor;
+
+import com.gewia.common.spring.auth.IgnoreServiceToken;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+@AllArgsConstructor
+public class ServiceTokenInterceptor extends HandlerInterceptorAdapter {
+
+ private final String serviceToken;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ response.setStatus(HttpStatus.FORBIDDEN.value());
+
+ HandlerMethod method = (HandlerMethod) handler;
+ if (method.hasMethodAnnotation(IgnoreServiceToken.class) ||
+ method.getMethod().getDeclaringClass().getAnnotation(IgnoreServiceToken.class) != null) {
+ response.setStatus(HttpStatus.OK.value());
+ return true;
+ }
+
+ String serviceToken = request.getHeader("X-ServiceToken");
+
+ if (serviceToken == null) return false;
+ if (!this.serviceToken.equals(serviceToken)) return false;
+
+ response.setStatus(HttpStatus.OK.value());
+ return true;
+ }
+
+}