AuthzEndpointParParser.java
/*
* Copyright 2021 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.protocol.oidc.par.endpoints.request;
import java.util.Map;
import java.util.Set;
import org.jboss.logging.Logger;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.SingleUseObjectProvider;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.endpoints.request.AuthorizationEndpointRequest;
import org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestParser;
import org.keycloak.protocol.oidc.par.endpoints.ParEndpoint;
import static org.keycloak.protocol.oidc.par.endpoints.ParEndpoint.PAR_CREATED_TIME;
/**
* Parse the parameters from PAR
*
*/
public class AuthzEndpointParParser extends AuthzEndpointRequestParser {
private static final Logger logger = Logger.getLogger(AuthzEndpointParParser.class);
private final KeycloakSession session;
private final ClientModel client;
private Map<String, String> requestParams;
private String invalidRequestMessage = null;
public AuthzEndpointParParser(KeycloakSession session, ClientModel client, String requestUri) {
this.session = session;
this.client = client;
SingleUseObjectProvider singleUseStore = session.singleUseObjects();
String key;
try {
key = requestUri.substring(ParEndpoint.REQUEST_URI_PREFIX_LENGTH);
} catch (RuntimeException re) {
logger.warnf(re,"Unable to parse request_uri: %s", requestUri);
throw new RuntimeException("Unable to parse request_uri");
}
Map<String, String> retrievedRequest = singleUseStore.remove(key);
if (retrievedRequest == null) {
throw new RuntimeException("PAR not found. not issued or used multiple times.");
}
RealmModel realm = session.getContext().getRealm();
int expiresIn = realm.getParPolicy().getRequestUriLifespan();
long created = Long.parseLong(retrievedRequest.get(PAR_CREATED_TIME));
if (System.currentTimeMillis() - created < (expiresIn * 1000)) {
requestParams = retrievedRequest;
} else {
throw new RuntimeException("PAR expired.");
}
}
@Override
public void parseRequest(AuthorizationEndpointRequest request) {
String requestParam = requestParams.get(OIDCLoginProtocol.REQUEST_PARAM);
if (requestParam != null) {
// parses the request object if PAR was registered using JAR
// parameters from requets object have precedence over those sent directly in the request
new ParEndpointRequestObjectParser(session, requestParam, client).parseRequest(request);
} else {
super.parseRequest(request);
}
}
@Override
protected String getParameter(String paramName) {
return requestParams.get(paramName);
}
@Override
protected Integer getIntParameter(String paramName) {
String paramVal = requestParams.get(paramName);
return paramVal == null ? null : Integer.valueOf(paramVal);
}
public String getInvalidRequestMessage() {
return invalidRequestMessage;
}
@Override
protected Set<String> keySet() {
return requestParams.keySet();
}
}