/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.teiid.jboss.oauth;
import java.io.IOException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.Signature;
import java.text.MessageFormat;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.rs.security.oauth2.client.Consumer;
import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.grants.jwt.JwtBearerGrant;
import org.jboss.security.JBossJSSESecurityDomain;
import org.teiid.core.util.Base64;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
public class JWTBearerTokenLoginModule extends OAuth20LoginModule {
private String scope;
private String issuer;
private String audience;
private String subject;
private String keystoreType;
private String keystorePassword;
private String keystoreURL;
private String certificateAlias;
private String certificatePassword;
private String algorithamName;
private static JBossJSSESecurityDomain securityDomain;
@Override
public void initialize(Subject subject, CallbackHandler handler, Map<String, ?> sharedState, Map<String, ?> options) {
super.initialize(subject, handler, sharedState, options);
this.scope = (String) options.get("scope"); //$NON-NLS-1$
this.issuer = (String) options.get("jwt-issuer"); //$NON-NLS-1$
this.audience = (String) options.get("jwt-audience"); //$NON-NLS-1$
this.subject= (String) options.get("jwt-subject"); //$NON-NLS-1$
this.keystoreType = (String) options.get("keystore-type"); //$NON-NLS-1$
this.keystorePassword = (String) options.get("keystore-password"); //$NON-NLS-1$
this.keystoreURL = (String) options.get("keystore-url"); //$NON-NLS-1$
this.certificateAlias = (String) options.get("certificate-alias"); //$NON-NLS-1$
this.certificatePassword = (String) options.get("certificate-password"); //$NON-NLS-1$
this.algorithamName = (String) options.get("signature-algorithm-name"); //$NON-NLS-1$
}
@Override
public boolean login() throws LoginException {
this.callerSubject = getSubject();
this.callerPrincipal = getPrincipal();
final String assertion = getJWTAssertion();
if (assertion == null) {
return false;
}
OAuth20CredentialImpl cred = new OAuth20CredentialImpl() {
protected ClientAccessToken getAccessToken() {
Consumer consumer = new Consumer(getClientId(), getClientSecret());
WebClient client = WebClient.create(getAccessTokenURI());
JwtBearerGrant grant = null;
if (scope != null) {
grant = new JwtBearerGrant(assertion, true, scope);
}
else {
grant = new JwtBearerGrant(assertion, true);
}
return OAuthClientUtils.getAccessToken(client, consumer, grant, null, false);
}
};
cred.setClientId(getClientId());
cred.setClientSecret(getClientSecret());
cred.setAccessTokenURI(getAccessTokenURI());
setCredential(cred);
return super.login();
}
/**
* override if needed
* @return
*/
protected String getJWTAssertion() throws LoginException {
String header = "{\"alg\":\"RS256\"}";
String claimTemplate = "'{'\"iss\": \"{0}\", \"sub\": \"{1}\", \"aud\": \"{2}\", \"exp\": \"{3}\"'}'";
StringBuffer token = new StringBuffer();
try {
// Encode the JWT Header and add it to our string to sign
token.append(Base64.encodeUrlSafe(header.getBytes("UTF-8")));
// Separate with a period
token.append(".");
// Create the JWT Claims Object
String[] claimArray = new String[4];
claimArray[0] = this.issuer == null ? getClientId() : this.issuer;
claimArray[1] = this.subject == null ? this.callerPrincipal.getName(): this.subject;
claimArray[2] = this.audience;
claimArray[3] = Long.toString((System.currentTimeMillis() / 1000) + 120);
MessageFormat claims = new MessageFormat(claimTemplate);
String payload = claims.format(claimArray);
// Add the encoded claims object
token.append(Base64.encodeUrlSafe(payload.getBytes("UTF-8")));
String password = this.certificatePassword == null ? this.keystorePassword
: this.certificatePassword;
loadKeystore(this.keystoreURL, this.keystorePassword, this.keystoreType, password);
// Sign the JWT Header + "." + JWT Claims Object
Key key = securityDomain.getKey(this.certificateAlias, password);
Signature signature = Signature.getInstance(this.algorithamName == null?"SHA256withRSA":this.algorithamName);
signature.initSign((PrivateKey) key);
signature.update(token.toString().getBytes("UTF-8"));
String signedPayload = Base64.encodeUrlSafe(signature.sign());
// Separate with a period
token.append(".");
// Add the encoded signature
token.append(signedPayload);
return token.toString();
} catch (Exception e) {
LogManager.logDetail(LogConstants.CTX_SECURITY, e);
throw new LoginException(e.getMessage());
}
}
private static void loadKeystore(String keystoreURL,
String keystorePassword, String keystoreType, String password)
throws Exception, IOException {
if (securityDomain == null) {
securityDomain = new JBossJSSESecurityDomain("JWTBearer");
securityDomain.setKeyStorePassword(keystorePassword);
securityDomain.setKeyStoreType(keystoreType == null ? "JKS": keystoreType);
securityDomain.setKeyStoreURL(keystoreURL);
securityDomain.setServiceAuthToken(password);
securityDomain.reloadKeyAndTrustStore();
}
}
}