Created
May 23, 2020 22:12
-
-
Save jcassee/8ce917387263d40b9908506af5b5c578 to your computer and use it in GitHub Desktop.
Implementation of Micronaut Security annotation for securing routes using JWT scopes (micronaut-projects/micronaut-security#254)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example; | |
import java.lang.annotation.*; | |
@Target({ElementType.METHOD, ElementType.TYPE}) | |
@Retention(RetentionPolicy.RUNTIME) | |
@Inherited | |
@Documented | |
public @interface ScopeSecured { | |
String[] value(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example; | |
import io.micronaut.http.HttpRequest; | |
import io.micronaut.security.rules.AbstractSecurityRule; | |
import io.micronaut.security.rules.SecurityRuleResult; | |
import io.micronaut.web.router.MethodBasedRouteMatch; | |
import io.micronaut.web.router.RouteMatch; | |
import javax.annotation.Nullable; | |
import javax.inject.Singleton; | |
import java.util.Arrays; | |
import java.util.Map; | |
import java.util.Optional; | |
import static java.util.Collections.emptyList; | |
@Singleton | |
public class ScopeSecuredAnnotationRule extends AbstractSecurityRule { | |
public ScopeSecuredAnnotationRule() { | |
super(claims -> emptyList()); // scopes are not roles | |
} | |
@Override | |
public SecurityRuleResult check(HttpRequest request, @Nullable RouteMatch routeMatch, @Nullable Map<String, Object> claims) { | |
var allowedScopes = getAllowedScopes(routeMatch); | |
if (allowedScopes.isPresent()) { | |
var claimScopes = getClaimScopes(claims); | |
return checkScopes(allowedScopes.get(), claimScopes); | |
} | |
return SecurityRuleResult.UNKNOWN; | |
} | |
private Optional<String[]> getAllowedScopes(@Nullable RouteMatch<?> routeMatch) { | |
if (routeMatch instanceof MethodBasedRouteMatch) { | |
var methodRoute = ((MethodBasedRouteMatch<?, ?>) routeMatch); | |
if (methodRoute.hasAnnotation(ScopeSecured.class)) { | |
return methodRoute.getValue(ScopeSecured.class, String[].class); | |
} | |
} | |
return Optional.empty(); | |
} | |
private String[] getClaimScopes(@Nullable Map<String, Object> claims) { | |
if (claims != null) { | |
var claim = claims.get("scope"); | |
if (claim instanceof String) { | |
return ((String) claim).strip().split("\\s+"); | |
} | |
} | |
return new String[0]; | |
} | |
private SecurityRuleResult checkScopes(String[] allowedScopes, String[] claimScopes) { | |
for (String scope : allowedScopes) { | |
if (Arrays.asList(claimScopes).contains(scope)) { | |
return SecurityRuleResult.ALLOWED; | |
} | |
} | |
return SecurityRuleResult.UNKNOWN; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment