Skip to content

Commit

Permalink
Protect custom resource with custom filter
Browse files Browse the repository at this point in the history
  • Loading branch information
sonOfRa committed Jul 25, 2023
1 parent d074227 commit 239cf39
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.github.thomasdarimont.keycloak.custom.endpoints.filter;

import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.NotAuthorizedException;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.ext.Provider;
import org.keycloak.authorization.util.Tokens;
import org.keycloak.common.util.Resteasy;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.AccessToken;

import java.io.IOException;
import java.util.regex.Pattern;

/**
* Custom request filter for token handling. Executed if a request method is annotated with @FilterBinding
*/
@Provider
@FilterBinding
public class Filter implements ContainerRequestFilter {

private final KeycloakSession keycloakSession = Resteasy.getContextData(KeycloakSession.class);

private final Pattern clientPattern = Pattern.compile("acme.*");

@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// For some reason, the Authorization header is missing from the headers stored in keycloakSession
String header = requestContext.getHeaderString("Authorization");
String token = null;

if (header != null) {
int index = header.indexOf("Bearer ");
if (index >= 0) {
token = header.substring(index + 7);
}
}

AccessToken accessToken = Tokens.getAccessToken(token, keycloakSession);

if (accessToken == null) {
throw new NotAuthorizedException("Invalid or missing token");
}

if (!clientPattern.matcher(accessToken.getIssuedFor()).matches()) {
throw new ForbiddenException("This resource is only accessible for acme clients");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.thomasdarimont.keycloak.custom.endpoints.filter;

import jakarta.ws.rs.NameBinding;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterBinding {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.thomasdarimont.keycloak.custom.endpoints.filter;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;

import java.util.Map;

/**
* Example for a resource protected by a request filter.
* Methods annotated with @FilterBinding execute the filter before executing the method
*/
public class FilterResource {

@GET
@Path("/")
@FilterBinding
@Produces("application/json")
public Response resource() {
return Response.ok(Map.of("secret", "opensesame")).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.thomasdarimont.keycloak.custom.endpoints.filter;

import org.keycloak.services.resource.RealmResourceProvider;

public class FilterResourceProvider implements RealmResourceProvider {

@Override
public Object getResource() {
return new FilterResource();
}

@Override
public void close() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.github.thomasdarimont.keycloak.custom.endpoints.filter;

import com.google.auto.service.AutoService;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resource.RealmResourceProviderFactory;


@AutoService(RealmResourceProviderFactory.class)
public class FilterResourceProviderFactory implements RealmResourceProviderFactory {
@Override
public RealmResourceProvider create(KeycloakSession session) {
return new FilterResourceProvider();
}

@Override
public void init(Config.Scope config) {

}

@Override
public void postInit(KeycloakSessionFactory factory) {

}

@Override
public void close() {

}

@Override
public String getId() {
return "filter";
}
}

0 comments on commit 239cf39

Please sign in to comment.