Warning This bundle is under development, do not use in production.
This bundle adds support for Sling-based applications to function as a Open ID connect relying parties or OAuth 2.0 clients. Its main objective is to simplify access to user and access tokens in a secure manner. It currently supports the OIDC Authentication Code flow.
The bundle offers the following entry points
OidcClient
service that communicates with the remote Open ID connect providerTokenStore
service that allows storage and retrieval of persisted tokens.Basic usage is as follows
import org.apache.sling.extensions.oidc_rp.*; @Component(service = { Servlet.class }) @SlingServletPaths(value = "/bin/myservlet") public class MySlingServlet { @Reference private OidcTokenStore tokenStore; @Reference private OidcClient oidcClient; public void accessRemoteResource(SlingHttpServletRequest request, SlingHttpServletResponse response) { OidcConnection connection = getConnection(); OidcToken tokenResponse = tokenStore.getAccessToken(connection, request.getResourceResolver()); switch ( tokenResponse.getState() ) { case VALID: doStuffWithToken(tokenResponse.getValue()); break; case MISSING: response.sendRedirect(oidcClient.getOidcEntryPointUri(connection, request, "/bin/myservlet").toString()); break; case EXPIRED: OidcToken refreshToken = tokenStore.getRefreshToken(connection, request.getResourceResolver()); if ( refreshToken.getState() != OidcTokenState.VALID ) response.sendRedirect(oidcClient.getOidcEntryPointUri(connection, request, "/bin/myservlet").toString()); OidcTokens oidcTokens = oidcClient.refreshTokens(connection, refreshToken.getValue()); tokenStore.persistTokens(connection, request.getResourceResolver(), oidcTokens); doStuffWithToken(tokenResponse.getValue()); break; } } }
Client registration is specific to each provider. When registering, note the following:
Validated providers:
A set of dependencies required by this bundle, on top of the Sling Starter ones, is available at src/main/features/main.json
. In addition, the following OSGi configuration must be added
"org.apache.sling.servlets.oidc_rp.impl.OidcConnectionImpl~provider": { "name": "provider", "baseUrl": "https://.example.com", "clientId": "$[secret:provider/clientId]", "clientSecret": "$[secret:provider/clientSecret]", "scopes": ["openid"] }
At this point, the OIDC process can be kicked of by navigating to http://localhost:8080/system/sling/oidc/entry-point?c=provider
The tokens are stored under the user's home, under the oidc-tokens/$PROVIDER_NAME
node.
mvn clean install
mvn feature-launcher:start feature-launcher:stop -Dfeature-launcher.waitForInput
export CLIENT_SECRET=$(cat src/test/resources/keycloak-import/sling.json | jq --raw-output '.clients[] | select (.clientId == "oidc-test") | .secret') $ curl -u admin:admin -X POST -d "apply=true" -d "propertylist=name,baseUrl,clientId,clientSecret,scopes" \ -d "name=keycloak-dev" \ -d "baseUrl=http://localhost:8081/realms/sling" \ -d "clientId=oidc-test"\ -d "clientSecret=$CLIENT_SECRET" \ -d "scopes=openid" \ -d "factoryPid=org.apache.sling.extensions.oidc_rp.impl.OidcConnectionImpl" \ http://localhost:8080/system/console/configMgr/org.apache.sling.extensions.oidc_rp.impl.OidcConnectionImpl~keycloak-dev
Now you can
Note that this imports the test setup with a single user with a redirect_uri set to http://localhost*, which can be a security issue.
$ docker run --rm --volume $(pwd)/src/test/resources/keycloak-import:/opt/keycloak/data/import -p 8081:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:20.0.3 start-dev --import-realm
$ docker run --rm --volume $(pwd)/keycloak-data:/opt/keycloak/data -p 8081:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:20.0.3 start-dev
$ docker run --rm --volume (pwd)/keycloak-data:/opt/keycloak/data -p 8081:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:20.0.3 export --realm sling --users realm_file --file /opt/keycloak/data/export/sling.json