Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
164 lines (119 sloc) 4.89 KB

CLI / Desktop Applications

{project_name} supports securing desktop (e.g. Swing, JavaFX) or CLI applications via the KeycloakInstalled adapter by performing the authentication step via the system browser.

The KeycloakInstalled adapter supports a desktop and a manual variant. The desktop variant uses the system browser to gather the user credentials. The manual variant reads the user credentials from STDIN.

How it works

To authenticate a user with the desktop variant the KeycloakInstalled adapter opens a desktop browser window where a user uses the regular {project_name} login pages to login when the loginDesktop() method is called on the KeycloakInstalled object.

The login page URL is opened with redirect parameter that points to a local ServerSocket listening on a free ephemeral port on localhost which is started by the adapter.

After a succesful login the KeycloakInstalled receives the authorization code from the incoming HTTP request and performs the authorization code flow. Once the code to token exchange is completed the ServerSocket is shutdown.

If the user already has an active {project_name} session then the login form is not shown but the code to token exchange is continued, which enables a smooth Web based SSO experience.

The client eventually receives the tokens (access_token, refresh_token, id_token) which can then be used to call backend services.

The KeycloakInstalled adapter provides support for renewal of stale tokens.

Adapter Installation
Client Configuration

The application needs to be configured as a public OpenID Connect client with Standard Flow Enabled and http://localhost as an allowed Valid Redirect URI.

The KeycloakInstalled adapter supports the PKCE [RFC 7636] mechanism to provide additional protection during code to token exchanges in the OIDC protocol. PKCE can be enabled with the "enable-pkce": true setting in the adapter configuration. Enabling PKCE is highly recommended, to avoid code injection and code replay attacks.

The KeycloakInstalled adapter reads it’s configuration from META-INF/keycloak.json on the classpath. Custom configurations can be supplied with an InputStream or a KeycloakDeployment through the KeycloakInstalled constructor.

In the example below, the client configuration for desktop-app uses the following keycloak.json:

  "realm": "desktop-app-auth",
  "auth-server-url": "http://localhost:8081/auth",
  "ssl-required": "external",
  "resource": "desktop-app",
  "public-client": true,
  "use-resource-role-mappings": true,
  "enable-pkce": true

the following sketch demonstrates working with the KeycloakInstalled adapter:

// reads the configuration from classpath: META-INF/keycloak.json
KeycloakInstalled keycloak = new KeycloakInstalled();

// opens desktop browser

AccessToken token = keycloak.getToken();
// use token to send backend request

// ensure token is valid for at least 30 seconds
long minValidity = 30L;
String tokenString = keycloak.getTokenString(minValidity, TimeUnit.SECONDS);

 // when you want to logout the user.
The KeycloakInstalled class supports customization of the http responses returned by login / logout requests via the loginResponseWriter and logoutResponseWriter attributes.

The following provides an example for the configuration mentioned above.

import java.util.Locale;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.keycloak.adapters.installed.KeycloakInstalled;
import org.keycloak.representations.AccessToken;

public class DesktopApp {

	public static void main(String[] args) throws Exception {

		KeycloakInstalled keycloak = new KeycloakInstalled();

		AccessToken token = keycloak.getToken();
		Executors.newSingleThreadExecutor().submit(() -> {

			System.out.println("Logged in...");
			System.out.println("Token: " + token.getSubject());
			System.out.println("Username: " + token.getPreferredUsername());
			try {
				System.out.println("AccessToken: " + keycloak.getTokenString());
			} catch (Exception ex) {

			int timeoutSeconds = 20;
			System.out.printf("Logging out in...%d Seconds%n", timeoutSeconds);
			try {
			} catch (Exception e) {

			try {
			} catch (Exception e) {