package org.keycloak.protocol;
import org.codehaus.jackson.annotate.JsonProperty;
import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.HMACProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.util.CookieHelper;
import javax.crypto.SecretKey;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.UriInfo;
import java.util.HashMap;
import java.util.Map;
/**
* This is an an encoded token that is stored as a cookie so that if there is a client timeout, then the client session
* can be restarted.
*
* @author Bill Burke
* @version $Revision: 1 $
*/
public class RestartLoginCookie {
private static final Logger logger = Logger.getLogger(RestartLoginCookie.class);
public static final String KC_RESTART = "KC_RESTART";
@JsonProperty("cs")
protected String clientSession;
@JsonProperty("cid")
protected String clientId;
@JsonProperty("pty")
protected String authMethod;
@JsonProperty("ruri")
protected String redirectUri;
@JsonProperty("act")
protected String action;
@JsonProperty("notes")
protected Map notes = new HashMap<>();
public String getClientSession() {
return clientSession;
}
public void setClientSession(String clientSession) {
this.clientSession = clientSession;
}
public Map getNotes() {
return notes;
}
public void setNotes(Map notes) {
this.notes = notes;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getAuthMethod() {
return authMethod;
}
public void setAuthMethod(String authMethod) {
this.authMethod = authMethod;
}
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public String encode(RealmModel realm) {
JWSBuilder builder = new JWSBuilder();
return builder.jsonContent(this)
.hmac256((SecretKey)realm.getCodeSecretKey());
//.rsa256(realm.getPrivateKey());
}
public RestartLoginCookie() {
}
public RestartLoginCookie(ClientSessionModel clientSession) {
this.action = clientSession.getAction();
this.clientId = clientSession.getClient().getClientId();
this.authMethod = clientSession.getAuthMethod();
this.redirectUri = clientSession.getRedirectUri();
this.clientSession = clientSession.getId();
for (Map.Entry entry : clientSession.getNotes().entrySet()) {
notes.put(entry.getKey(), entry.getValue());
}
}
public static void setRestartCookie(RealmModel realm, ClientConnection connection, UriInfo uriInfo, ClientSessionModel clientSession) {
RestartLoginCookie restart = new RestartLoginCookie(clientSession);
String encoded = restart.encode(realm);
int keySize = realm.getCodeSecret().length();
int size = encoded.length();
String path = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
boolean secureOnly = realm.getSslRequired().isRequired(connection);
CookieHelper.addCookie(KC_RESTART, encoded, path, null, null, -1, secureOnly, true);
}
public static void expireRestartCookie(RealmModel realm, ClientConnection connection, UriInfo uriInfo) {
String path = AuthenticationManager.getRealmCookiePath(realm, uriInfo);
boolean secureOnly = realm.getSslRequired().isRequired(connection);
CookieHelper.addCookie(KC_RESTART, "", path, null, null, 0, secureOnly, true);
}
public static ClientSessionModel restartSession(KeycloakSession session, RealmModel realm, String code) throws Exception {
Cookie cook = session.getContext().getRequestHeaders().getCookies().get(KC_RESTART);
if (cook == null) {
logger.debug("KC_RESTART cookie doesn't exist");
return null;
}
String encodedCookie = cook.getValue();
JWSInput input = new JWSInput(encodedCookie);
/*
if (!RSAProvider.verify(input, realm.getPublicKey())) {
logger.debug("Failed to verify encoded RestartLoginCookie");
return null;
}
*/
if (!HMACProvider.verify(input, (SecretKey)realm.getCodeSecretKey())) {
logger.debug("Failed to verify encoded RestartLoginCookie");
return null;
}
RestartLoginCookie cookie = input.readJsonContent(RestartLoginCookie.class);
String[] parts = code.split("\\.");
String clientSessionId = parts[1];
if (!clientSessionId.equals(cookie.getClientSession())) {
logger.debug("RestartLoginCookie clientSession does not match code's clientSession");
return null;
}
ClientModel client = realm.getClientByClientId(cookie.getClientId());
if (client == null) return null;
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(cookie.getAuthMethod());
clientSession.setRedirectUri(cookie.getRedirectUri());
clientSession.setAction(cookie.getAction());
for (Map.Entry entry : cookie.getNotes().entrySet()) {
clientSession.setNote(entry.getKey(), entry.getValue());
}
return clientSession;
}
}