KEYCLOAK-9551 KEYCLOAK-16159 Make refresh_token generation for client_credentials optional. Support for revocation of access tokens.

Co-authored-by: mposolda <mposolda@gmail.com>
This commit is contained in:
Thomas Darimont
2019-07-31 17:50:04 +02:00
committed by Marek Posolda
parent 1281f28bb8
commit de20830412
44 changed files with 1019 additions and 186 deletions

View File

@@ -18,6 +18,7 @@ package org.keycloak.client.admin.cli.commands;
import org.jboss.aesh.cl.Option;
import org.jboss.aesh.console.command.invocation.CommandInvocation;
import org.keycloak.OAuth2Constants;
import org.keycloak.client.admin.cli.config.ConfigData;
import org.keycloak.client.admin.cli.config.ConfigHandler;
import org.keycloak.client.admin.cli.config.FileConfigHandler;
@@ -264,6 +265,8 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
rdata.setClientId(clientId);
if (secret != null)
rdata.setSecret(secret);
String grantTypeForAuthentication = user == null ? OAuth2Constants.CLIENT_CREDENTIALS : OAuth2Constants.PASSWORD;
rdata.setGrantTypeForAuthentication(grantTypeForAuthentication);
}
protected void checkUnsupportedOptions(String ... options) {

View File

@@ -20,6 +20,7 @@ import org.jboss.aesh.cl.CommandDefinition;
import org.jboss.aesh.console.command.CommandException;
import org.jboss.aesh.console.command.CommandResult;
import org.jboss.aesh.console.command.invocation.CommandInvocation;
import org.keycloak.OAuth2Constants;
import org.keycloak.client.admin.cli.config.ConfigData;
import org.keycloak.client.admin.cli.config.RealmConfigData;
import org.keycloak.client.admin.cli.util.AuthUtil;
@@ -120,8 +121,10 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd {
boolean clientSet = clientId != null;
applyDefaultOptionValues();
String grantTypeForAuthentication = null;
if (user != null) {
grantTypeForAuthentication = OAuth2Constants.PASSWORD;
printErr("Logging into " + server + " as user " + user + " of realm " + realm);
// if user was set there needs to be a password so we can authenticate
@@ -133,6 +136,7 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd {
secret = readSecret("Enter client secret: ", commandInvocation);
}
} else if (keystore != null || secret != null || clientSet) {
grantTypeForAuthentication = OAuth2Constants.CLIENT_CREDENTIALS;
printErr("Logging into " + server + " as " + "service-account-" + clientId + " of realm " + realm);
if (keystore == null) {
if (secret == null) {
@@ -190,7 +194,7 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd {
Long sigExpiresAt = signedRequestToken == null ? null : System.currentTimeMillis() + sigLifetime * 1000;
// save tokens to config file
saveTokens(tokens, server, realm, clientId, signedRequestToken, sigExpiresAt, secret);
saveTokens(tokens, server, realm, clientId, signedRequestToken, sigExpiresAt, secret, grantTypeForAuthentication);
return CommandResult.SUCCESS;
}

View File

@@ -39,6 +39,8 @@ public class RealmConfigData {
private String secret;
private String grantTypeForAuthentication;
private Long expiresAt;
private Long refreshExpiresAt;
@@ -102,6 +104,14 @@ public class RealmConfigData {
this.secret = secret;
}
public String getGrantTypeForAuthentication() {
return grantTypeForAuthentication;
}
public void setGrantTypeForAuthentication(String grantTypeForAuthentication) {
this.grantTypeForAuthentication = grantTypeForAuthentication;
}
public Long getExpiresAt() {
return expiresAt;
}
@@ -134,6 +144,7 @@ public class RealmConfigData {
refreshToken = source.refreshToken;
signingToken = source.signingToken;
secret = source.secret;
grantTypeForAuthentication = source.grantTypeForAuthentication;
expiresAt = source.expiresAt;
refreshExpiresAt = source.refreshExpiresAt;
sigExpiresAt = source.sigExpiresAt;
@@ -164,6 +175,7 @@ public class RealmConfigData {
data.refreshToken = refreshToken;
data.signingToken = signingToken;
data.secret = secret;
data.grantTypeForAuthentication = grantTypeForAuthentication;
data.expiresAt = expiresAt;
data.refreshExpiresAt = refreshExpiresAt;
data.sigExpiresAt = sigExpiresAt;

View File

@@ -62,7 +62,7 @@ public class AuthUtil {
// check refresh_token against expiry time
// if it's less than 5s to expiry, fail with credentials expired
if (realmConfig.getRefreshExpiresAt() - now < 5000) {
if (realmConfig.getRefreshExpiresAt() != null && realmConfig.getRefreshExpiresAt() - now < 5000) {
throw new RuntimeException("Session has expired. Login again with '" + OsUtil.CMD + " config credentials'");
}
@@ -72,10 +72,15 @@ public class AuthUtil {
try {
String authorization = null;
StringBuilder body = new StringBuilder();
if (realmConfig.getRefreshToken() != null) {
body.append("grant_type=refresh_token")
.append("&refresh_token=").append(realmConfig.getRefreshToken());
} else {
body.append("grant_type=").append(realmConfig.getGrantTypeForAuthentication());
}
StringBuilder body = new StringBuilder("grant_type=refresh_token")
.append("&refresh_token=").append(realmConfig.getRefreshToken())
.append("&client_id=").append(urlencode(realmConfig.getClientId()));
body.append("&client_id=").append(urlencode(realmConfig.getClientId()));
if (realmConfig.getSigningToken() != null) {
body.append("&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
@@ -94,7 +99,9 @@ public class AuthUtil {
realmData.setToken(token.getToken());
realmData.setRefreshToken(token.getRefreshToken());
realmData.setExpiresAt(currentTimeMillis() + token.getExpiresIn() * 1000);
realmData.setRefreshExpiresAt(currentTimeMillis() + token.getRefreshExpiresIn() * 1000);
if (token.getRefreshToken() != null) {
realmData.setRefreshExpiresAt(currentTimeMillis() + token.getRefreshExpiresIn() * 1000);
}
});
return token.getToken();

View File

@@ -16,6 +16,7 @@
*/
package org.keycloak.client.admin.cli.util;
import org.keycloak.OAuth2Constants;
import org.keycloak.client.admin.cli.config.ConfigData;
import org.keycloak.client.admin.cli.config.ConfigHandler;
import org.keycloak.client.admin.cli.config.ConfigUpdateOperation;
@@ -44,7 +45,8 @@ public class ConfigUtil {
ConfigUtil.handler = handler;
}
public static void saveTokens(AccessTokenResponse tokens, String endpoint, String realm, String clientId, String signKey, Long sigExpiresAt, String secret) {
public static void saveTokens(AccessTokenResponse tokens, String endpoint, String realm, String clientId, String signKey, Long sigExpiresAt, String secret,
String grantTypeForAuthentication) {
handler.saveMergeConfig(config -> {
config.setServerUrl(endpoint);
config.setRealm(realm);
@@ -55,10 +57,13 @@ public class ConfigUtil {
realmConfig.setSigningToken(signKey);
realmConfig.setSecret(secret);
realmConfig.setExpiresAt(System.currentTimeMillis() + tokens.getExpiresIn() * 1000);
realmConfig.setRefreshExpiresAt(tokens.getRefreshExpiresIn() == 0 ?
Long.MAX_VALUE : System.currentTimeMillis() + tokens.getRefreshExpiresIn() * 1000);
if (realmConfig.getRefreshToken() != null) {
realmConfig.setRefreshExpiresAt(tokens.getRefreshExpiresIn() == 0 ?
Long.MAX_VALUE : System.currentTimeMillis() + tokens.getRefreshExpiresIn() * 1000);
}
realmConfig.setSigExpiresAt(sigExpiresAt);
realmConfig.setClientId(clientId);
realmConfig.setGrantTypeForAuthentication(grantTypeForAuthentication);
});
}
@@ -76,8 +81,12 @@ public class ConfigUtil {
}
public static boolean credentialsAvailable(ConfigData config) {
return config.getServerUrl() != null && (config.getExternalToken() != null || (config.getRealm() != null
&& config.sessionRealmConfigData() != null && config.sessionRealmConfigData().getRefreshToken() != null));
// Just supporting "client_credentials" grant type for the case when refresh token is missing
boolean credsAvailable = config.getServerUrl() != null && (config.getExternalToken() != null || (config.getRealm() != null
&& config.sessionRealmConfigData() != null &&
(config.sessionRealmConfigData().getRefreshToken() != null || (config.sessionRealmConfigData().getToken() != null && OAuth2Constants.CLIENT_CREDENTIALS.equals(config.sessionRealmConfigData().getGrantTypeForAuthentication())))
));
return credsAvailable;
}
public static ConfigData loadConfig() {