# PAC4J for JaxRs Whiteboards This bundle enables PAC4J security ([https://www.pac4j.org/](https://www.pac4j.org/)) for JaxRs Whiteboards. PAC4J provides a lot of different Authentication Methods. Right now only OpenID Connect is configurable in two flavors by this bundle: Bearer ID Token and the usual login and callback process. ## Configuration The feature is always configured with a client ID property. In your OSGi configuration resource, client ID can be configured in one of two ways: 1. via `oid.clientId` field – this must correspond to client name configured in Keycloak, or 2. via `pac4j.clientId` field – this can be any valid name, in case you wish to use a different identifier than client name configured in Keycloak (e.g. you test against different environments, in which Keycloak client names differ, but since client ID configured via `org.pac4j.jax.rs.annotations.Pac4JSecurity` annotation must be a compile time constant, you can use same value in `pac4j.clientId` across environments, and use different values in `oid.clientId` to specify client name configured in Keycloak, corresponding to specific environment) In order to secure a Jax-Rs Resource, the Resource itself or its methods MUST be annotated with `org.pac4j.jax.rs.annotations.Pac4JSecurity` annotation with ` clients` attribute whose value corresponds to either `oid.clientId` (see **#1** above), i.e.: ``` @Pac4JSecurity(clients = "value of `oid.clientId`") ``` or `pac4j.clientId` (see **#2** above), i.e.: ``` @Pac4JSecurity(clients = "value of `pac4j.clientId`") ``` ## Login with callback The following configuration will configure a security client, that uses OpenIDConnect. The Pac4J Feature needs to know about the Client. If no `clients.target` is given, the features knows about all security clients and will support all of them. In our case we specifically inject only the one client that is configured as well. The Pac4JFeature is a JaxRs Feature and can have whiteboard target and/or application select filter properties. ``` { ":configurator:resource-version": 1, "Pac4JFeature~login": { "clients.target" : "(client.id=login)", }, "KeycloackOidcClient": { "oid.baseUri" : "http://localhost:8080/auth", "oid.realm" : "test", "oid.clientId" : "keycloak_login_client", "oid.secret" : "d75fd8f0-193d-47ab-825b-96458f5fc74f", "client.id" : "login", "client.callbackUrl" : "http://localhost:8185/login/callback" } } ``` Example for a Login Resource: ```java @Component(service = LoginResource.class, scope = ServiceScope.PROTOTYPE) @JaxrsResource @Consumes(MediaType.WILDCARD) @Produces(MediaType.WILDCARD) @Path("/") public class LoginResource { /* * The Method is secured by a Pac4J Client with the client id `keycloak_login_client`. If an unauthenticated uses comes by, he will * be redirected the the configured OIDC Server, where he needs to login. He then will be redirected to the callback endpoint. */ @GET @Path("login") @Pac4JSecurity(clients = "keycloak_login_client") public Response login(@Pac4JProfile CommonProfile profile) { return Response.ok("Welcome " + profile.getFirstName() + " " + profile.getFamilyName()).build(); } /* * The Annotation shows this to be the callback endpoint. This is just a marker, because the method body is actually never called. * The user will be forwarded to its orginal requested resource, when he returns from the login page. */ @GET @Pac4JCallback() @Path("callback") public Response callback () { return Response.ok("Thx").build(); } } ``` ## Bearer Token Login This example configures a client, that expects a Authorization HTTP header that carries a bearer token. At the moment this token needs to be the JWT id token, so a backend can impersonate the caller. Configuration: ``` { ":configurator:resource-version": 1, "Pac4JFeature~bearer": { "clients.target" : "(client.id=bearer)", }, "BearerTokenClient": { "oid.baseUri" : "http://localhost:8080/auth", "oid.realm" : "test", "oid.clientId" : "keycloak_login_client", "oid.secret" : "8c38714d-4879-4013-b464-cc50a4002e5c", "client.id" : "bearer" } } ``` The Secured Resource: ```java @Component(service = BarerTestResource.class, scope = ServiceScope.PROTOTYPE) @JaxrsResource @Consumes(MediaType.WILDCARD) @Produces(MediaType.WILDCARD) @Path("/") public class BarerTestResource { @GET @Path("backend") @Pac4JSecurity(clients = "keycloak_login_client") public Response doSomething(@Pac4JProfile CommonProfile profile) { return Response.ok("Returend " + profile.getFirstName() + " " + profile.getFamilyName()).build(); } } ``` For details look into the `org.gecko.util.rest.pac4j.feature.test` bundle. This configures a full test cases with both cases. ## How to use different methods of authentication? Pac4J has all of its logic in the security clients. If another implementation like LDAP, Facebook or whatever is needed, register a fully configured `org.pac4j.core.client.Client` under this interface and modify your `clients.target` for the feature if needed.