/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.authentication;
import org.keycloak.TokenVerifier;
import org.keycloak.TokenVerifier.Predicate;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jws.*;
import org.keycloak.models.*;
import org.keycloak.services.Urls;
import org.keycloak.sessions.AuthenticationSessionModel;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
import java.util.UUID;
import javax.ws.rs.core.UriInfo;
import org.jboss.logging.Logger;
/**
* Representation of a token that represents a time-limited reset credentials action.
* <p>
* This implementation handles signature.
*
* @author hmlnarik
*/
public class ResetCredentialsActionToken extends DefaultActionToken {
private static final Logger LOG = Logger.getLogger(ResetCredentialsActionToken.class);
public static final String RESET_CREDENTIALS_TYPE = "reset-credentials";
public static final String NOTE_AUTHENTICATION_SESSION_ID = "clientSessionId";
private static final String JSON_FIELD_AUTHENTICATION_SESSION_ID = "asid";
private static final String JSON_FIELD_LAST_CHANGE_PASSWORD_TIMESTAMP = "lcpt";
@JsonIgnore
private AuthenticationSessionModel authenticationSession;
@JsonProperty(value = JSON_FIELD_LAST_CHANGE_PASSWORD_TIMESTAMP)
private Long lastChangedPasswordTimestamp;
public ResetCredentialsActionToken(String userId, int absoluteExpirationInSecs, UUID actionVerificationNonce, Long lastChangedPasswordTimestamp, String authenticationSessionId) {
super(userId, RESET_CREDENTIALS_TYPE, absoluteExpirationInSecs, actionVerificationNonce);
setNote(NOTE_AUTHENTICATION_SESSION_ID, authenticationSessionId);
this.lastChangedPasswordTimestamp = lastChangedPasswordTimestamp;
}
public ResetCredentialsActionToken(String userId, int absoluteExpirationInSecs, UUID actionVerificationNonce, Long lastChangedPasswordTimestamp, AuthenticationSessionModel authenticationSession) {
this(userId, absoluteExpirationInSecs, actionVerificationNonce, lastChangedPasswordTimestamp, authenticationSession == null ? null : authenticationSession.getId());
this.authenticationSession = authenticationSession;
}
private ResetCredentialsActionToken() {
super(null, null, -1, null);
}
public AuthenticationSessionModel getAuthenticationSession() {
return this.authenticationSession;
}
public void setAuthenticationSession(AuthenticationSessionModel authenticationSession) {
this.authenticationSession = authenticationSession;
setAuthenticationSessionId(authenticationSession == null ? null : authenticationSession.getId());
}
@JsonProperty(value = JSON_FIELD_AUTHENTICATION_SESSION_ID)
public String getAuthenticationSessionId() {
return getNote(NOTE_AUTHENTICATION_SESSION_ID);
}
public void setAuthenticationSessionId(String authenticationSessionId) {
setNote(NOTE_AUTHENTICATION_SESSION_ID, authenticationSessionId);
}
public Long getLastChangedPasswordTimestamp() {
return lastChangedPasswordTimestamp;
}
public void setLastChangedPasswordTimestamp(Long lastChangedPasswordTimestamp) {
this.lastChangedPasswordTimestamp = lastChangedPasswordTimestamp;
}
@Override
@JsonIgnore
public Map<String, String> getNotes() {
Map<String, String> res = super.getNotes();
if (this.authenticationSession != null) {
res.put(NOTE_AUTHENTICATION_SESSION_ID, getNote(NOTE_AUTHENTICATION_SESSION_ID));
}
return res;
}
public String serialize(KeycloakSession session, RealmModel realm, UriInfo uri) {
String issuerUri = getIssuer(realm, uri);
KeyManager.ActiveHmacKey keys = session.keys().getActiveHmacKey(realm);
this
.issuedAt(Time.currentTime())
.id(getActionVerificationNonce().toString())
.issuer(issuerUri)
.audience(issuerUri);
return new JWSBuilder()
.kid(keys.getKid())
.jsonContent(this)
.hmac512(keys.getSecretKey());
}
private static String getIssuer(RealmModel realm, UriInfo uri) {
return Urls.realmIssuer(uri.getBaseUri(), realm.getName());
}
/**
* Returns a {@code DefaultActionToken} instance decoded from the given string. If decoding fails, returns {@code null}
*
* @param session
* @param actionTokenString
* @return
*/
public static ResetCredentialsActionToken deserialize(String token) throws VerificationException {
return TokenVerifier.create(token, ResetCredentialsActionToken.class).getToken();
}
}