🐛 This is alpha software
📚 Documentation • 🚀 Hosted Keycloak • 🤖 Keycloak Skills • 💬 Support
Tip
Run this in our cloud, no setup needed. Phase Two hosts Keycloak with all of our extensions built in. Try it free with your own cluster →
Using a coding agent? Add the Phase Two Keycloak skills (launch post) to Claude Code, Cursor or Codex so your agent can set up passwordless login, SSO, organizations and clusters for you:
# Install the skills (connects to our hosted MCP server) claude plugin marketplace add p2-inc/keycloak-skills claude plugin install phasetwo@keycloak-skills # Or connect to the MCP server directly claude mcp add --transport http keycloak https://mcp.phasetwo.io/mcp
The Phase Two Java SDK library provides access to the Phase Two API from applications written in JVM-compatible languages.
See the API Reference and Javadoc
<dependency>
<groupId>io.phasetwo</groupId>
<artifactId>phasetwo-admin-client</artifactId>
<version>VERSION</version>
</dependency>
dependencies {
implementation 'io.phasetwo:phasetwo-admin-client:VERSION'
}
The Java SDK assumes the use of the offical Keycloak Admin API Client to create a connection to the Phase Two API.
import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.KeycloakBuilder; import io.phasetwo.client.PhaseTwo; import java.util.Optional; // Configure the API connection // use they Keycloak "authServerUrl" that contains the relative path String serverUrl = "https://my-phasetwo-host/auth"; String realm = "my_realm_name"; Keycloak keycloak = KeycloakBuilder.builder() .serverUrl(serverUrl) .realm(realm) .grantType(OAuth2Constants.CLIENT_CREDENTIALS) .clientId("my_client_id") .clientSecret("my_client_secret") .build(); PhaseTwo phaseTwo = new PhaseTwo(keycloak, serverUrl); // Create an Organization OrganizationRepresentation org = new OrganizationRepresentation().name("example"); String orgId = phaseTwo.organizations(realm).create(org); // Create an Admin Portal link for the Organization phaseTwo.organizations(realm).organization(orgId).portalLink(Optional.empty()); // Create and publish an Audit Event phaseTwo.events(realm).send(new EventRepresentation() .type("foo.bar") .organizationId(orgId) .time(System.currentTimeMillis()));
- create an empty directory
local - copy the current
openapi.ymlfrom thephasetwo-docsrepo to that directory - run the generator in the
localdirectory
docker run --rm --user $(id -u):$(id -g) -v $PWD:/local openapitools/openapi-generator-cli generate -i /local/openapi.yaml -g jaxrs-spec -o /local/java-sdk --additional-properties=groupId=io.phasetwo,artifactId=phasetwo-admin-client,apiPackage=io.phasetwo.client.openapi.api,modelPackage=io.phasetwo.client.openapi.model,useSwaggerAnnotations=false,useBeanValidation=false,useTags=true,interfaceOnly=true,useJakartaEe=true,hideGenerationTimestamp=true
- copy the relevant files
cp java-sdk/src/gen/java/io/phasetwo/client/openapi/*.java $PWD/../src/gen/java/io/phasetwo/client/openapi/
cp java-sdk/src/gen/java/io/phasetwo/client/openapi/api/*.java $PWD/../src/gen/java/io/phasetwo/client/openapi/api/
cp java-sdk/src/gen/java/io/phasetwo/client/openapi/model/*.java $PWD/../src/gen/java/io/phasetwo/client/openapi/model/
cp java-sdk/src/main/openapi/openapi.yaml $PWD/../src/main/openapi/openapi.yaml
- resolve all of the
Responseissues in the*Apiclasses. This is because we need the response object in order to get the status code andLocationheader, but openapi and this generator doesn't give us a good way to specify that or do it by default. - push a pr and merge to main. auto releases using
.github/workflows/release.yml
All documentation, source code and other files in this repository are Copyright 2025 Phase Two, Inc.