/*
* oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
*
* Copyright (c) 2014, Gluu
*/
package org.xdi.oxauth.client;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.xdi.oxauth.model.authorize.AuthorizeRequestParam;
import org.xdi.oxauth.model.authorize.CodeVerifier;
import org.xdi.oxauth.model.common.Display;
import org.xdi.oxauth.model.common.Prompt;
import org.xdi.oxauth.model.common.ResponseMode;
import org.xdi.oxauth.model.common.ResponseType;
import org.xdi.oxauth.model.util.Util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents an authorization request to send to the authorization server.
*
* @author Javier Rojas Blum
* @version December 26, 2016
*/
public class AuthorizationRequest extends BaseRequest {
private List<ResponseType> responseTypes;
private String clientId;
private List<String> scopes;
private String redirectUri;
private String state;
private ResponseMode responseMode;
private String nonce;
private Display display;
private List<Prompt> prompts;
private Integer maxAge;
private List<String> uiLocales;
private List<String> claimsLocales;
private String idTokenHint;
private String loginHint;
private List<String> acrValues;
private JSONObject claims;
private String registration;
private String request;
private String requestUri;
private boolean requestSessionState;
private String sessionState;
private String accessToken;
private boolean useNoRedirectHeader;
// code verifier according to PKCE spec
private String codeChallenge;
private String codeChallengeMethod;
private Map<String, String> customResponseHeaders;
/**
* Constructs an authorization request.
*
* @param responseTypes The response type informs the authorization server of the desired response type:
* <strong>code</strong>, <strong>token</strong>, <strong>id_token</strong>
* a combination of them. The response type parameter is mandatory.
* @param clientId The client identifier is mandatory.
* @param scopes The scope of the access request.
* @param redirectUri Redirection URI
* @param nonce A string value used to associate a user agent session with an ID Token,
* and to mitigate replay attacks.
*/
public AuthorizationRequest(List<ResponseType> responseTypes, String clientId, List<String> scopes,
String redirectUri, String nonce) {
super();
this.responseTypes = responseTypes;
this.clientId = clientId;
this.scopes = scopes;
this.redirectUri = redirectUri;
this.nonce = nonce;
prompts = new ArrayList<Prompt>();
useNoRedirectHeader = false;
}
public CodeVerifier generateAndSetCodeChallengeWithMethod() {
CodeVerifier verifier = new CodeVerifier(CodeVerifier.CodeChallengeMethod.S256);
codeChallenge = verifier.getCodeChallenge();
codeChallengeMethod = verifier.getTransformationType().getPkceString();
return verifier;
}
public String getCodeChallenge() {
return codeChallenge;
}
public String getCodeChallengeMethod() {
return codeChallengeMethod;
}
public void setCodeChallenge(String codeChallenge) {
this.codeChallenge = codeChallenge;
}
public void setCodeChallengeMethod(String codeChallengeMethod) {
this.codeChallengeMethod = codeChallengeMethod;
}
/**
* Returns the response types.
*
* @return The response types.
*/
public List<ResponseType> getResponseTypes() {
return responseTypes;
}
/**
* Sets the response types.
*
* @param responseTypes The response types.
*/
public void setResponseTypes(List<ResponseType> responseTypes) {
this.responseTypes = responseTypes;
}
/**
* Returns the client identifier.
*
* @return The client identifier.
*/
public String getClientId() {
return clientId;
}
/**
* Sets the client identifier.
*
* @param clientId The client identifier.
*/
public void setClientId(String clientId) {
this.clientId = clientId;
}
/**
* Returns the scopes of the access request. The authorization endpoint allow
* the client to specify the scope of the access request using the scope
* request parameter. In turn, the authorization server uses the scope
* response parameter to inform the client of the scope of the access token
* issued. The value of the scope parameter is expressed as a list of
* space-delimited, case sensitive strings.
*
* @return The scopes of the access request.
*/
public List<String> getScopes() {
return scopes;
}
/**
* Sets the scope of the access request. The authorization endpoint allow
* the client to specify the scope of the access request using the scope
* request parameter. In turn, the authorization server uses the scope
* response parameter to inform the client of the scope of the access token
* issued. The value of the scope parameter is expressed as a list of
* space-delimited, case sensitive strings.
*
* @param scopes The scope of the access request.
*/
public void setScopes(List<String> scopes) {
this.scopes = scopes;
}
/**
* Returns the redirection URI.
*
* @return The redirection URI.
*/
public String getRedirectUri() {
return redirectUri;
}
/**
* Sets the redirection URI.
*
* @param redirectUri The redirection URI.
*/
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
/**
* Returns the state. The state is an opaque value used by the client to
* maintain state between the request and callback. The authorization server
* includes this value when redirecting the user-agent back to the client.
* The parameter should be used for preventing cross-site request forgery.
*
* @return The state.
*/
public String getState() {
return state;
}
/**
* Sets the state. The state is an opaque value used by the client to
* maintain state between the request and callback. The authorization server
* includes this value when redirecting the user-agent back to the client.
* The parameter should be used for preventing cross-site request forgery.
*
* @param state The state.
*/
public void setState(String state) {
this.state = state;
}
public ResponseMode getResponseMode() {
return responseMode;
}
public void setResponseMode(ResponseMode responseMode) {
this.responseMode = responseMode;
}
/**
* Returns a string value used to associate a user agent session with an ID Token,
* and to mitigate replay attacks.
*
* @return The nonce value.
*/
public String getNonce() {
return nonce;
}
/**
* Sets a string value used to associate a user agent session with an ID Token,
* and to mitigate replay attacks.
*
* @param nonce The nonce value.
*/
public void setNonce(String nonce) {
this.nonce = nonce;
}
/**
* Returns an ASCII string value that specifies how the Authorization Server displays the
* authentication page to the End-User.
*
* @return The display value.
*/
public Display getDisplay() {
return display;
}
/**
* Sets an ASCII string value that specifies how the Authorization Server displays the
* authentication page to the End-User.
*
* @param display The display value.
*/
public void setDisplay(Display display) {
this.display = display;
}
/**
* Returns a space delimited list of ASCII strings that can contain the values login, consent,
* select_account, and none.
*
* @return The prompt list.
*/
public List<Prompt> getPrompts() {
return prompts;
}
public void setPrompts(List<Prompt> prompts) {
this.prompts = prompts;
}
public Integer getMaxAge() {
return maxAge;
}
public void setMaxAge(Integer maxAge) {
this.maxAge = maxAge;
}
public List<String> getUiLocales() {
return uiLocales;
}
public void setUiLocales(List<String> uiLocales) {
this.uiLocales = uiLocales;
}
public List<String> getClaimsLocales() {
return claimsLocales;
}
public void setClaimsLocales(List<String> claimsLocales) {
this.claimsLocales = claimsLocales;
}
public String getIdTokenHint() {
return idTokenHint;
}
public void setIdTokenHint(String idTokenHint) {
this.idTokenHint = idTokenHint;
}
public String getLoginHint() {
return loginHint;
}
public void setLoginHint(String loginHint) {
this.loginHint = loginHint;
}
public List<String> getAcrValues() {
return acrValues;
}
public void setAcrValues(List<String> acrValues) {
this.acrValues = acrValues;
}
public JSONObject getClaims() {
return claims;
}
public void setClaims(JSONObject claims) {
this.claims = claims;
}
public String getRegistration() {
return registration;
}
public void setRegistration(String registration) {
this.registration = registration;
}
/**
* Returns a JWT encoded OpenID Request Object.
*
* @return A JWT encoded OpenID Request Object.
*/
public String getRequest() {
return request;
}
/**
* Sets a JWT encoded OpenID Request Object.
*
* @param request A JWT encoded OpenID Request Object.
*/
public void setRequest(String request) {
this.request = request;
}
/**
* Returns an URL that points to an OpenID Request Object.
*
* @return An URL that points to an OpenID Request Object.
*/
public String getRequestUri() {
return requestUri;
}
/**
* Sets an URL that points to an OpenID Request Object.
*
* @param requestUri An URL that points to an OpenID Request Object.
*/
public void setRequestUri(String requestUri) {
this.requestUri = requestUri;
}
/**
* Returns whether session state is requested.
*
* @return whether session state is requested
*/
public boolean isRequestSessionState() {
return requestSessionState;
}
/**
* Sets whether session state should be requested.
*
* @param p_requestSessionState session state.
*/
public void setRequestSessionState(boolean p_requestSessionState) {
requestSessionState = p_requestSessionState;
}
/**
* Gets session state.
*
* @return session state.
*/
public String getSessionState() {
return sessionState;
}
/**
* Sets session state.
*
* @param p_sessionState session state
*/
public void setSessionState(String p_sessionState) {
sessionState = p_sessionState;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public boolean isUseNoRedirectHeader() {
return useNoRedirectHeader;
}
public void setUseNoRedirectHeader(boolean useNoRedirectHeader) {
this.useNoRedirectHeader = useNoRedirectHeader;
}
public String getResponseTypesAsString() {
return Util.asString(responseTypes);
}
public String getScopesAsString() {
return Util.listAsString(scopes);
}
public String getPromptsAsString() {
return Util.asString(prompts);
}
public String getUiLocalesAsString() {
return Util.listAsString(uiLocales);
}
public String getClaimsLocalesAsString() {
return Util.listAsString(claimsLocales);
}
public String getAcrValuesAsString() {
return Util.listAsString(acrValues);
}
public String getCustomResponseHeadersAsString() throws JSONException {
return Util.mapAsString(customResponseHeaders);
}
public Map<String, String> getCustomResponseHeaders() {
return customResponseHeaders;
}
public void setCustomResponseHeaders(Map<String, String> customResponseHeaders) {
this.customResponseHeaders = customResponseHeaders;
}
public String getClaimsAsString() {
if (claims != null) {
return claims.toString();
} else {
return null;
}
}
/**
* Returns a query string with the parameters of the authorization request.
* Any <code>null</code> or empty parameter will be omitted.
*
* @return A query string of parameters.
*/
@Override
public String getQueryString() {
StringBuilder queryStringBuilder = new StringBuilder();
try {
// OAuth 2.0 request parameters
final String responseTypesAsString = getResponseTypesAsString();
final String scopesAsString = getScopesAsString();
final String promptsAsString = getPromptsAsString();
final String customResponseHeadersAsString = getCustomResponseHeadersAsString();
if (StringUtils.isNotBlank(responseTypesAsString)) {
queryStringBuilder.append(AuthorizeRequestParam.RESPONSE_TYPE)
.append("=").append(URLEncoder.encode(responseTypesAsString, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(clientId)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.CLIENT_ID)
.append("=").append(URLEncoder.encode(clientId, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(scopesAsString)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.SCOPE)
.append("=").append(URLEncoder.encode(scopesAsString, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(redirectUri)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.REDIRECT_URI)
.append("=").append(URLEncoder.encode(redirectUri, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(state)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.STATE)
.append("=").append(URLEncoder.encode(state, Util.UTF8_STRING_ENCODING));
}
// OpenID Connect request parameters
final String uiLocalesAsString = getUiLocalesAsString();
final String claimLocalesAsString = getClaimsLocalesAsString();
final String acrValuesAsString = getAcrValuesAsString();
final String claimsAsString = getClaimsAsString();
if (responseMode != null) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.RESPONSE_MODE)
.append("=").append(URLEncoder.encode(responseMode.toString(), Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(nonce)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.NONCE)
.append("=").append(URLEncoder.encode(nonce, Util.UTF8_STRING_ENCODING));
}
if (display != null) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.DISPLAY)
.append("=").append(URLEncoder.encode(display.toString(), Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(promptsAsString)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.PROMPT)
.append("=").append(URLEncoder.encode(promptsAsString, Util.UTF8_STRING_ENCODING));
}
if (maxAge != null) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.MAX_AGE)
.append("=").append(maxAge);
}
if (StringUtils.isNotBlank(uiLocalesAsString)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.UI_LOCALES)
.append("=").append(URLEncoder.encode(uiLocalesAsString, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(claimLocalesAsString)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.CLAIMS_LOCALES)
.append("=").append(URLEncoder.encode(claimLocalesAsString, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(idTokenHint)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.ID_TOKEN_HINT)
.append("=").append(idTokenHint);
}
if (StringUtils.isNotBlank(loginHint)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.LOGIN_HINT)
.append("=").append(loginHint);
}
if (StringUtils.isNotBlank(acrValuesAsString)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.ACR_VALUES)
.append("=").append(URLEncoder.encode(acrValuesAsString, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(claimsAsString)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.CLAIMS)
.append("=").append(URLEncoder.encode(claimsAsString, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(registration)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.REGISTRATION)
.append("=").append(registration);
}
if (StringUtils.isNotBlank(request)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.REQUEST)
.append("=").append(URLEncoder.encode(request, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(requestUri)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.REQUEST_URI)
.append("=").append(URLEncoder.encode(requestUri, Util.UTF8_STRING_ENCODING));
}
if (requestSessionState) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.REQUEST_SESSION_STATE)
.append("=").append(URLEncoder.encode(Boolean.toString(requestSessionState), Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(sessionState)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.SESSION_STATE)
.append("=").append(URLEncoder.encode(sessionState, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(accessToken)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.ACCESS_TOKEN)
.append("=").append(URLEncoder.encode(accessToken, Util.UTF8_STRING_ENCODING));
}
if (StringUtils.isNotBlank(codeChallenge)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.CODE_CHALLENGE)
.append("=").append(codeChallenge);
}
if (StringUtils.isNotBlank(codeChallengeMethod)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.CODE_CHALLENGE_METHOD)
.append("=").append(codeChallengeMethod);
}
if (StringUtils.isNotBlank(customResponseHeadersAsString)) {
queryStringBuilder.append("&").append(AuthorizeRequestParam.CUSTOM_RESPONSE_HEADERS)
.append("=").append(URLEncoder.encode(customResponseHeadersAsString, Util.UTF8_STRING_ENCODING));
}
for (String key : getCustomParameters().keySet()) {
queryStringBuilder.append("&");
queryStringBuilder.append(key).append("=").append(getCustomParameters().get(key));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return queryStringBuilder.toString();
}
/**
* Returns a collection of parameters of the authorization request. Any
* <code>null</code> or empty parameter will be omitted.
*
* @return A collection of parameters.
*/
public Map<String, String> getParameters() {
Map<String, String> parameters = new HashMap<String, String>();
try {
// OAuth 2.0 request parameters
final String responseTypesAsString = getResponseTypesAsString();
final String scopesAsString = getScopesAsString();
final String promptsAsString = getPromptsAsString();
final String customResponseHeadersAsString = getCustomResponseHeadersAsString();
if (StringUtils.isNotBlank(responseTypesAsString)) {
parameters.put(AuthorizeRequestParam.RESPONSE_TYPE, responseTypesAsString);
}
if (StringUtils.isNotBlank(clientId)) {
parameters.put(AuthorizeRequestParam.CLIENT_ID, clientId);
}
if (StringUtils.isNotBlank(scopesAsString)) {
parameters.put(AuthorizeRequestParam.SCOPE, scopesAsString);
}
if (StringUtils.isNotBlank(redirectUri)) {
parameters.put(AuthorizeRequestParam.REDIRECT_URI, redirectUri);
}
if (StringUtils.isNotBlank(state)) {
parameters.put(AuthorizeRequestParam.STATE, state);
}
// OpenID Connect request parameters
final String uiLocalesAsString = getUiLocalesAsString();
final String claimLocalesAsString = getClaimsLocalesAsString();
final String acrValuesAsString = getAcrValuesAsString();
final String claimsAsString = getClaimsAsString();
if (responseMode != null) {
parameters.put(AuthorizeRequestParam.RESPONSE_MODE, responseMode.toString());
}
if (StringUtils.isNotBlank(nonce)) {
parameters.put(AuthorizeRequestParam.NONCE, nonce);
}
if (display != null) {
parameters.put(AuthorizeRequestParam.DISPLAY, display.toString());
}
if (StringUtils.isNotBlank(promptsAsString)) {
parameters.put(AuthorizeRequestParam.PROMPT, promptsAsString);
}
if (maxAge != null) {
parameters.put(AuthorizeRequestParam.MAX_AGE, maxAge.toString());
}
if (StringUtils.isNotBlank(uiLocalesAsString)) {
parameters.put(AuthorizeRequestParam.UI_LOCALES, uiLocalesAsString);
}
if (StringUtils.isNotBlank(claimLocalesAsString)) {
parameters.put(AuthorizeRequestParam.CLAIMS_LOCALES, claimLocalesAsString);
}
if (StringUtils.isNotBlank(idTokenHint)) {
parameters.put(AuthorizeRequestParam.ID_TOKEN_HINT, idTokenHint);
}
if (StringUtils.isNotBlank(loginHint)) {
parameters.put(AuthorizeRequestParam.LOGIN_HINT, loginHint);
}
if (StringUtils.isNotBlank(acrValuesAsString)) {
parameters.put(AuthorizeRequestParam.ACR_VALUES, acrValuesAsString);
}
if (StringUtils.isNotBlank(claimsAsString)) {
parameters.put(AuthorizeRequestParam.CLAIMS, claimsAsString);
}
if (StringUtils.isNotBlank(registration)) {
parameters.put(AuthorizeRequestParam.REGISTRATION, registration);
}
if (StringUtils.isNotBlank(request)) {
parameters.put(AuthorizeRequestParam.REQUEST, request);
}
if (StringUtils.isNotBlank(requestUri)) {
parameters.put(AuthorizeRequestParam.REQUEST_URI, requestUri);
}
if (requestSessionState) {
parameters.put(AuthorizeRequestParam.REQUEST_SESSION_STATE, Boolean.toString(requestSessionState));
}
if (StringUtils.isNotBlank(sessionState)) {
parameters.put(AuthorizeRequestParam.SESSION_STATE, sessionState);
}
if (StringUtils.isNotBlank(accessToken)) {
parameters.put(AuthorizeRequestParam.ACCESS_TOKEN, accessToken);
}
if (StringUtils.isNotBlank(codeChallenge)) {
parameters.put(AuthorizeRequestParam.CODE_CHALLENGE, codeChallenge);
}
if (StringUtils.isNotBlank(codeChallengeMethod)) {
parameters.put(AuthorizeRequestParam.CODE_CHALLENGE_METHOD, codeChallengeMethod);
}
if (StringUtils.isNotBlank(customResponseHeadersAsString)) {
parameters.put(AuthorizeRequestParam.CUSTOM_RESPONSE_HEADERS, customResponseHeadersAsString);
}
for (String key : getCustomParameters().keySet()) {
parameters.put(key, getCustomParameters().get(key));
}
} catch (JSONException e) {
e.printStackTrace();
}
return parameters;
}
}