/*
* 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.apache.log4j.Logger;
import org.xdi.oxauth.model.common.AuthenticationMethod;
import org.xdi.oxauth.model.common.GrantType;
import org.xdi.oxauth.model.crypto.AbstractCryptoProvider;
import org.xdi.oxauth.model.crypto.signature.ECDSAPrivateKey;
import org.xdi.oxauth.model.crypto.signature.RSAPrivateKey;
import org.xdi.oxauth.model.crypto.signature.SignatureAlgorithm;
import org.xdi.oxauth.model.exception.InvalidJwtException;
import org.xdi.oxauth.model.jwt.Jwt;
import org.xdi.oxauth.model.jwt.JwtType;
import org.xdi.oxauth.model.token.ClientAssertionType;
import org.xdi.oxauth.model.uma.UmaScopeType;
import javax.ws.rs.core.MediaType;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
/**
* Represents a token request to send to the authorization server.
*
* @author Javier Rojas Blum
* @version June 15, 2016
*/
public class TokenRequest extends BaseRequest {
private static final Logger LOG = Logger.getLogger(TokenRequest.class);
public static class Builder {
private GrantType grantType;
private String scope;
public Builder grantType(GrantType grantType) {
this.grantType = grantType;
return this;
}
public Builder scope(String scope) {
this.scope = scope;
return this;
}
public Builder pat(String... scopeArray) {
String scope = UmaScopeType.PROTECTION.getValue();
if (scopeArray != null && scopeArray.length > 0) {
for (String s : scopeArray) {
scope = scope + " " + s;
}
}
return scope(scope);
}
public Builder aat(String... scopeArray) {
String scope = UmaScopeType.AUTHORIZATION.getValue();
if (scopeArray != null && scopeArray.length > 0) {
for (String s : scopeArray) {
scope = scope + " " + s;
}
}
return scope(scope);
}
public TokenRequest build() {
final TokenRequest request = new TokenRequest(grantType);
request.setScope(scope);
return request;
}
}
private GrantType grantType;
private String code;
private String redirectUri;
private String username;
private String password;
private String scope;
private String assertion;
private String refreshToken;
private String oxAuthExchangeToken;
private String audience;
private String codeVerifier;
private SignatureAlgorithm algorithm;
private String sharedKey;
private RSAPrivateKey rsaPrivateKey;
private ECDSAPrivateKey ecPrivateKey;
private AbstractCryptoProvider cryptoProvider;
private String keyId;
/**
* Constructs a token request.
*
* @param grantType The grant type is mandatory and could be:
* <code>authorization_code</code>, <code>password</code>,
* <code>client_credentials</code>, <code>refresh_token</code>.
*/
public TokenRequest(GrantType grantType) {
super();
this.grantType = grantType;
setContentType(MediaType.APPLICATION_FORM_URLENCODED);
setAuthenticationMethod(AuthenticationMethod.CLIENT_SECRET_BASIC);
}
public static Builder builder() {
return new Builder();
}
public static Builder umaBuilder() {
return new Builder().grantType(GrantType.CLIENT_CREDENTIALS);
}
/**
* Returns the grant type.
*
* @return The grant type.
*/
public GrantType getGrantType() {
return grantType;
}
/**
* Sets the grant type.
*
* @param grantType The grant type.
*/
public void setGrantType(GrantType grantType) {
this.grantType = grantType;
}
/**
* Returns the authorization code.
*
* @return The authorization code.
*/
public String getCode() {
return code;
}
/**
* Sets the authorization code.
*
* @param code The authorization code.
*/
public void setCode(String code) {
this.code = code;
}
/**
* Gets PKCE code verifier.
*
* @return code verifier
*/
public String getCodeVerifier() {
return codeVerifier;
}
/**
* Sets PKCE code verifier.
*
* @param codeVerifier code verifier
*/
public void setCodeVerifier(String codeVerifier) {
this.codeVerifier = codeVerifier;
}
/**
* Returns the redirect URI.
*
* @return The redirect URI.
*/
public String getRedirectUri() {
return redirectUri;
}
/**
* Sets the redirect URI.
*
* @param redirectUri The redirect URI.
*/
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
/**
* Returns the username.
*
* @return The username.
*/
public String getUsername() {
return username;
}
/**
* Sets the username.
*
* @param username The username.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Returns the password.
*
* @return The password.
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password The password.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Returns the scope.
*
* @return The scope.
*/
public String getScope() {
return scope;
}
/**
* Sets the scope.
*
* @param scope The scope.
*/
public void setScope(String scope) {
this.scope = scope;
}
/**
* Returns the assertion.
*
* @return The assertion.
*/
public String getAssertion() {
return assertion;
}
/**
* Sets the assertion.
*
* @param assertion The assertion.
*/
public void setAssertion(String assertion) {
this.assertion = assertion;
}
/**
* Returns the refresh token.
*
* @return The refresh token.
*/
public String getRefreshToken() {
return refreshToken;
}
/**
* Sets the refresh token.
*
* @param refreshToken The refresh token.
*/
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getOxAuthExchangeToken() {
return oxAuthExchangeToken;
}
public void setOxAuthExchangeToken(String oxAuthExchangeToken) {
this.oxAuthExchangeToken = oxAuthExchangeToken;
}
public void setAudience(String audience) {
this.audience = audience;
}
public void setAlgorithm(SignatureAlgorithm algorithm) {
this.algorithm = algorithm;
}
public void setSharedKey(String sharedKey) {
this.sharedKey = sharedKey;
}
@Deprecated
public void setRsaPrivateKey(RSAPrivateKey rsaPrivateKey) {
this.rsaPrivateKey = rsaPrivateKey;
}
@Deprecated
public void setEcPrivateKey(ECDSAPrivateKey ecPrivateKey) {
this.ecPrivateKey = ecPrivateKey;
}
public void setCryptoProvider(AbstractCryptoProvider cryptoProvider) {
this.cryptoProvider = cryptoProvider;
}
public String getKeyId() {
return keyId;
}
public void setKeyId(String keyId) {
this.keyId = keyId;
}
public String getClientAssertion() {
Jwt clientAssertion = new Jwt();
if (algorithm == null) {
algorithm = SignatureAlgorithm.HS256;
}
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
Date issuedAt = calendar.getTime();
calendar.add(Calendar.MINUTE, 5);
Date expirationTime = calendar.getTime();
// Header
clientAssertion.getHeader().setType(JwtType.JWT);
clientAssertion.getHeader().setAlgorithm(algorithm);
if (StringUtils.isNotBlank(keyId)) {
clientAssertion.getHeader().setKeyId(keyId);
}
// Claims
clientAssertion.getClaims().setIssuer(getAuthUsername());
clientAssertion.getClaims().setSubjectIdentifier(getAuthUsername());
clientAssertion.getClaims().setAudience(audience);
clientAssertion.getClaims().setJwtId(UUID.randomUUID());
clientAssertion.getClaims().setExpirationTime(expirationTime);
clientAssertion.getClaims().setIssuedAt(issuedAt);
// Signature
try {
if (sharedKey == null) {
sharedKey = getAuthPassword();
}
String signature = cryptoProvider.sign(clientAssertion.getSigningInput(), keyId, sharedKey, algorithm);
clientAssertion.setEncodedSignature(signature);
} catch (InvalidJwtException e) {
LOG.error(e.getMessage(), e);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return clientAssertion.toString();
}
/**
* 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 {
if (grantType != null) {
queryStringBuilder.append("grant_type=").append(grantType.toString());
}
if (code != null && !code.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("code=").append(code);
}
if (redirectUri != null && !redirectUri.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("redirect_uri=").append(
URLEncoder.encode(redirectUri, "UTF-8"));
}
if (scope != null && !scope.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("scope=").append(
URLEncoder.encode(scope, "UTF-8"));
}
if (username != null && !username.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("username=").append(username);
}
if (password != null && !password.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("password=").append(password);
}
if (assertion != null && !assertion.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("assertion=").append(assertion);
}
if (refreshToken != null && !refreshToken.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("refresh_token=").append(refreshToken);
}
if (oxAuthExchangeToken != null && !oxAuthExchangeToken.isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("oxauth_exchange_token=").append(oxAuthExchangeToken);
}
if (getAuthenticationMethod() == AuthenticationMethod.CLIENT_SECRET_POST) {
if (getAuthUsername() != null && !getAuthUsername().isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("client_id=").append(
URLEncoder.encode(getAuthUsername(), "UTF-8"));
}
if (getAuthPassword() != null && !getAuthPassword().isEmpty()) {
queryStringBuilder.append("&");
queryStringBuilder.append("client_secret=").append(
URLEncoder.encode(getAuthPassword(), "UTF-8"));
}
} else if (getAuthenticationMethod() == AuthenticationMethod.CLIENT_SECRET_JWT ||
getAuthenticationMethod() == AuthenticationMethod.PRIVATE_KEY_JWT) {
queryStringBuilder.append("&client_assertion_type=").append(
URLEncoder.encode(ClientAssertionType.JWT_BEARER.toString(), "UTF-8"));
queryStringBuilder.append("&");
queryStringBuilder.append("client_assertion=").append(getClientAssertion());
}
for (String key : getCustomParameters().keySet()) {
queryStringBuilder.append("&");
queryStringBuilder.append(key).append("=").append(getCustomParameters().get(key));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return queryStringBuilder.toString();
}
/**
* Returns a collection of parameters of the token 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>();
if (grantType != null) {
parameters.put("grant_type", grantType.toString());
}
if (code != null && !code.isEmpty()) {
parameters.put("code", code);
}
if (redirectUri != null && !redirectUri.isEmpty()) {
parameters.put("redirect_uri", redirectUri);
}
if (username != null && !username.isEmpty()) {
parameters.put("username", username);
}
if (password != null && !password.isEmpty()) {
parameters.put("password", password);
}
if (scope != null && !scope.isEmpty()) {
parameters.put("scope", scope);
}
if (assertion != null && !assertion.isEmpty()) {
parameters.put("assertion", assertion);
}
if (refreshToken != null && !refreshToken.isEmpty()) {
parameters.put("refresh_token", refreshToken);
}
if (oxAuthExchangeToken != null && !oxAuthExchangeToken.isEmpty()) {
parameters.put("oxauth_exchange_token", oxAuthExchangeToken);
}
if (getAuthenticationMethod() == AuthenticationMethod.CLIENT_SECRET_POST) {
if (getAuthUsername() != null && !getAuthUsername().isEmpty()) {
parameters.put("client_id", getAuthUsername());
}
if (getAuthPassword() != null && !getAuthPassword().isEmpty()) {
parameters.put("client_secret", getAuthPassword());
}
} else if (getAuthenticationMethod() == AuthenticationMethod.CLIENT_SECRET_JWT ||
getAuthenticationMethod() == AuthenticationMethod.PRIVATE_KEY_JWT) {
parameters.put("client_assertion_type", ClientAssertionType.JWT_BEARER.toString());
parameters.put("client_assertion", getClientAssertion());
}
for (String key : getCustomParameters().keySet()) {
parameters.put(key, getCustomParameters().get(key));
}
return parameters;
}
}