Merge pull request #206 from thomasdarimont/issue/doc-keycloak-installed-adapter
Document KeycloakInstalled adapter
This commit is contained in:
commit
8137fab2bf
2 changed files with 165 additions and 0 deletions
|
@ -28,6 +28,9 @@ ifeval::[{project_community}==true]
|
|||
include::topics/oidc/java/servlet-filter-adapter.adoc[]
|
||||
include::topics/oidc/java/jaas.adoc[]
|
||||
endif::[]
|
||||
ifeval::[{project_community}==true]
|
||||
include::topics/oidc/java/installed-adapter.adoc[]
|
||||
endif::[]
|
||||
include::topics/oidc/java/adapter-context.adoc[]
|
||||
include::topics/oidc/java/adapter_error_handling.adoc[]
|
||||
include::topics/oidc/java/logout.adoc[]
|
||||
|
|
162
securing_apps/topics/oidc/java/installed-adapter.adoc
Normal file
162
securing_apps/topics/oidc/java/installed-adapter.adoc
Normal file
|
@ -0,0 +1,162 @@
|
|||
[[_installed_adapter]]
|
||||
==== 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`.
|
||||
|
||||
Tip: Google provides some more information about this approach on at
|
||||
https://developers.google.com/identity/protocols/OAuth2InstalledApp[OAuth2InstalledApp].
|
||||
|
||||
===== 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.
|
||||
|
||||
TIP: 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 reneval of stale tokens.
|
||||
|
||||
[[_installed_adapter_installation]]
|
||||
===== Adapter Installation
|
||||
|
||||
[source,xml,subs="attributes+"]
|
||||
----
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-installed-adapter</artifactId>
|
||||
<version>{project_versionMvn}</version>
|
||||
</dependency>
|
||||
|
||||
----
|
||||
|
||||
|
||||
===== Client Configuration
|
||||
|
||||
The application needs to be configured as a `public` openid connect client with
|
||||
`Standard Flow Enabled` and pass:[http://localhost:*] as an allowed `Valid Redirect URI`.
|
||||
|
||||
===== Usage
|
||||
|
||||
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`:
|
||||
|
||||
[source]
|
||||
----
|
||||
|
||||
{
|
||||
"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
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
the following sketch demonstrates working with the `KeycloakInstalled` adapter:
|
||||
[source]
|
||||
----
|
||||
|
||||
// reads the configuration from classpath: META-INF/keycloak.json
|
||||
KeycloakInstalled keycloak = new KeycloakInstalled();
|
||||
|
||||
// opens desktop browser
|
||||
keycloak.loginDesktop();
|
||||
|
||||
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.
|
||||
keycloak.logout();
|
||||
|
||||
----
|
||||
|
||||
TIP: The `KeycloakInstalled` class supports customization of the http responses returned by
|
||||
login / logout requests via the `loginResponseWriter` and `logoutResponseWriter` attributes.
|
||||
|
||||
===== Example
|
||||
|
||||
The following provides an example for the configuration mentioned above.
|
||||
|
||||
[source]
|
||||
----
|
||||
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();
|
||||
keycloak.setLocale(Locale.ENGLISH);
|
||||
keycloak.loginDesktop();
|
||||
|
||||
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) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
int timeoutSeconds = 20;
|
||||
System.out.printf("Logging out in...%d Seconds%n", timeoutSeconds);
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(timeoutSeconds);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
keycloak.logout();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("Exiting...");
|
||||
System.exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
----
|
Loading…
Reference in a new issue