Started installed application adapter
This commit is contained in:
1 parent
cec948ffb4
commit
2ebc32793a
10 files changed
+493
-249
No files matched your search
@@ -17,6 +17,8 @@ public class KeycloakDeployment {
|
||||
protected KeycloakUriBuilder authUrl;
|
||||
protected String codeUrl;
|
||||
protected String refreshUrl;
|
||||
protected KeycloakUriBuilder logoutUrl;
|
||||
protected String accountUrl;
|
||||
|
||||
protected String resourceName;
|
||||
protected boolean bearerOnly;
|
||||
@@ -194,4 +196,20 @@ public class KeycloakDeployment {
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public KeycloakUriBuilder getLogoutUrl() {
|
||||
return logoutUrl;
|
||||
}
|
||||
|
||||
public void setLogoutUrl(KeycloakUriBuilder logoutUrl) {
|
||||
this.logoutUrl = logoutUrl;
|
||||
}
|
||||
|
||||
public String getAccountUrl() {
|
||||
return accountUrl;
|
||||
}
|
||||
|
||||
public void setAccountUrl(String accountUrl) {
|
||||
this.accountUrl = accountUrl;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -63,14 +63,14 @@ public class KeycloakDeploymentBuilder {
|
||||
String authUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGIN_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String tokenUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_ACCESS_CODE_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String refreshUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_REFRESH_PATH).build(adapterConfig.getRealm()).toString();
|
||||
|
||||
|
||||
String logoutUrl = serverBuilder.clone().path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH).build(adapterConfig.getRealm()).toString();
|
||||
String accountUrl = serverBuilder.clone().path(ServiceUrlConstants.ACCOUNT_SERVICE_PATH).build(adapterConfig.getRealm()).toString();
|
||||
|
||||
deployment.setAuthUrl(KeycloakUriBuilder.fromUri(authUrl).queryParam("client_id", deployment.getResourceName()));
|
||||
deployment.setCodeUrl(tokenUrl);
|
||||
deployment.setRefreshUrl(refreshUrl);
|
||||
|
||||
|
||||
deployment.setLogoutUrl(KeycloakUriBuilder.fromUri(logoutUrl));
|
||||
deployment.setAccountUrl(accountUrl);
|
||||
|
||||
return deployment;
|
||||
}
|
||||
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-installed</artifactId>
|
||||
<name>Keycloak Installed Application</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-adapter-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${keycloak.apache.httpcomponents.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.iharder</groupId>
|
||||
<artifactId>base64</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
package org.keycloak.adapters.installed;
|
||||
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class KeycloakInstalled {
|
||||
|
||||
private static final String KEYCLOAK_JSON = "META-INF/keycloak.json";
|
||||
|
||||
private KeycloakDeployment deployment;
|
||||
|
||||
private enum Status {
|
||||
LOGGED_MANUAL, LOGGED_DESKTOP
|
||||
}
|
||||
|
||||
private String tokenString;
|
||||
private String idTokenString;
|
||||
private IDToken idToken;
|
||||
private AccessToken token;
|
||||
private String refreshToken;
|
||||
private Status status;
|
||||
|
||||
public KeycloakInstalled() {
|
||||
InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(KEYCLOAK_JSON);
|
||||
deployment = KeycloakDeploymentBuilder.build(config);
|
||||
}
|
||||
|
||||
public KeycloakInstalled(InputStream config) {
|
||||
deployment = KeycloakDeploymentBuilder.build(config);
|
||||
}
|
||||
|
||||
public void login() throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException {
|
||||
if (isDesktopSupported()) {
|
||||
loginDesktop();
|
||||
} else {
|
||||
loginManual();
|
||||
}
|
||||
}
|
||||
|
||||
public void login(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException, InterruptedException, OAuthErrorException, URISyntaxException {
|
||||
if (isDesktopSupported()) {
|
||||
loginDesktop();
|
||||
} else {
|
||||
loginManual(printer, reader);
|
||||
}
|
||||
}
|
||||
|
||||
public void logout() throws IOException, InterruptedException, URISyntaxException {
|
||||
if (status == Status.LOGGED_DESKTOP) {
|
||||
logoutDesktop();
|
||||
}
|
||||
|
||||
tokenString = null;
|
||||
token = null;
|
||||
|
||||
idTokenString = null;
|
||||
idToken = null;
|
||||
|
||||
refreshToken = null;
|
||||
|
||||
status = null;
|
||||
}
|
||||
|
||||
public void loginDesktop() throws IOException, VerificationException, OAuthErrorException, URISyntaxException, ServerRequest.HttpFailure, InterruptedException {
|
||||
CallbackListener callback = new CallbackListener();
|
||||
callback.start();
|
||||
|
||||
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
||||
String state = UUID.randomUUID().toString();
|
||||
|
||||
String authUrl = deployment.getAuthUrl().clone()
|
||||
.queryParam("client_id", deployment.getResourceName())
|
||||
.queryParam("redirect_uri", redirectUri)
|
||||
.queryParam("state", state)
|
||||
.queryParam("login", "true")
|
||||
.build().toString();
|
||||
|
||||
Desktop.getDesktop().browse(new URI(authUrl));
|
||||
|
||||
callback.join();
|
||||
|
||||
if (!state.equals(callback.state)) {
|
||||
throw new VerificationException("Invalid state");
|
||||
}
|
||||
|
||||
if (callback.error != null) {
|
||||
throw new OAuthErrorException(callback.error, callback.errorDescription);
|
||||
}
|
||||
|
||||
if (callback.errorException != null) {
|
||||
throw callback.errorException;
|
||||
}
|
||||
|
||||
processCode(callback.code, redirectUri);
|
||||
|
||||
status = Status.LOGGED_DESKTOP;
|
||||
}
|
||||
|
||||
private void logoutDesktop() throws IOException, URISyntaxException, InterruptedException {
|
||||
CallbackListener callback = new CallbackListener();
|
||||
callback.start();
|
||||
|
||||
String redirectUri = "http://localhost:" + callback.server.getLocalPort();
|
||||
|
||||
String logoutUrl = deployment.getLogoutUrl()
|
||||
.queryParam("redirect_uri", redirectUri)
|
||||
.build().toString();
|
||||
|
||||
Desktop.getDesktop().browse(new URI(logoutUrl));
|
||||
|
||||
callback.join();
|
||||
|
||||
if (callback.errorException != null) {
|
||||
throw callback.errorException;
|
||||
}
|
||||
}
|
||||
|
||||
public void loginManual() throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||
loginManual(System.out, new InputStreamReader(System.in));
|
||||
}
|
||||
|
||||
public void loginManual(PrintStream printer, Reader reader) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||
CallbackListener callback = new CallbackListener();
|
||||
callback.start();
|
||||
|
||||
String redirectUri = "urn:ietf:wg:oauth:2.0:oob";
|
||||
|
||||
String authUrl = deployment.getAuthUrl().clone()
|
||||
.queryParam("client_id", deployment.getResourceName())
|
||||
.queryParam("redirect_uri", redirectUri)
|
||||
.queryParam("login", "true")
|
||||
.build().toString();
|
||||
|
||||
printer.println("Open the following URL in a browser. After login copy/paste the code back and press <enter>");
|
||||
printer.println(authUrl);
|
||||
printer.println();
|
||||
printer.print("Code: ");
|
||||
|
||||
String code = readCode(reader);
|
||||
processCode(code, redirectUri);
|
||||
|
||||
status = Status.LOGGED_MANUAL;
|
||||
}
|
||||
|
||||
public String getTokenString() throws VerificationException, IOException, ServerRequest.HttpFailure {
|
||||
return tokenString;
|
||||
}
|
||||
|
||||
public String getTokenString(long minValidity, TimeUnit unit) throws VerificationException, IOException, ServerRequest.HttpFailure {
|
||||
long expires = token.getExpiration() * 1000 - unit.toMillis(minValidity);
|
||||
if (expires < System.currentTimeMillis()) {
|
||||
refreshToken();
|
||||
}
|
||||
|
||||
return tokenString;
|
||||
}
|
||||
|
||||
public void refreshToken() throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
|
||||
parseAccessToken(tokenResponse);
|
||||
}
|
||||
|
||||
private void parseAccessToken(AccessTokenResponse tokenResponse) throws VerificationException {
|
||||
tokenString = tokenResponse.getToken();
|
||||
refreshToken = tokenResponse.getRefreshToken();
|
||||
idTokenString = tokenResponse.getIdToken();
|
||||
|
||||
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealm());
|
||||
if (idTokenString != null) {
|
||||
JWSInput input = new JWSInput(idTokenString);
|
||||
try {
|
||||
idToken = input.readJsonContent(IDToken.class);
|
||||
} catch (IOException e) {
|
||||
throw new VerificationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AccessToken getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public IDToken getIdToken() {
|
||||
return idToken;
|
||||
}
|
||||
|
||||
public String getIdTokenString() {
|
||||
return idTokenString;
|
||||
}
|
||||
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
public boolean isDesktopSupported() {
|
||||
return Desktop.isDesktopSupported();
|
||||
}
|
||||
|
||||
public KeycloakDeployment getDeployment() {
|
||||
return deployment;
|
||||
}
|
||||
|
||||
private void processCode(String code, String redirectUri) throws IOException, ServerRequest.HttpFailure, VerificationException {
|
||||
AccessTokenResponse tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, redirectUri);
|
||||
parseAccessToken(tokenResponse);
|
||||
}
|
||||
|
||||
private String readCode(Reader reader) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
char cb[] = new char[1];
|
||||
while (reader.read(cb) != -1) {
|
||||
char c = cb[0];
|
||||
if ((c == ' ') || (c == '\n') || (c == '\r')) {
|
||||
break;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public class CallbackListener extends Thread {
|
||||
|
||||
private ServerSocket server;
|
||||
|
||||
private String code;
|
||||
|
||||
private String error;
|
||||
|
||||
private String errorDescription;
|
||||
|
||||
private IOException errorException;
|
||||
|
||||
private String state;
|
||||
|
||||
public CallbackListener() throws IOException {
|
||||
server = new ServerSocket(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = server.accept();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
String request = br.readLine();
|
||||
|
||||
String url = request.split(" ")[1];
|
||||
if (url.indexOf('?') >= 0) {
|
||||
url = url.split("\\?")[1];
|
||||
String[] params = url.split("&");
|
||||
|
||||
for (String param : params) {
|
||||
String[] p = param.split("=");
|
||||
if (p[0].equals("code")) {
|
||||
code = p[1];
|
||||
} else if (p[0].equals("error")) {
|
||||
error = p[1];
|
||||
} else if (p[0].equals("error-description")) {
|
||||
errorDescription = p[1];
|
||||
} else if (p[0].equals("state")) {
|
||||
state = p[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||
pw.println("Please close window and return to application");
|
||||
pw.flush();
|
||||
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
errorException = e;
|
||||
}
|
||||
|
||||
try {
|
||||
server.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
<module>wildfly-subsystem</module>
|
||||
<module>as7-eap-subsystem</module>
|
||||
<module>js</module>
|
||||
<module>installed</module>
|
||||
<!-- <module>as7-eap6/jboss-modules</module> -->
|
||||
</modules>
|
||||
</project>
|
||||
Reference in new issue
Block a user