/*
* Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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.wso2.carbon.identity.authenticator.signedjwt;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.SignedJWT;
import org.apache.axiom.util.base64.Base64Utils;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.core.security.AuthenticatorsConfiguration;
import org.wso2.carbon.core.services.authentication.CarbonServerAuthenticator;
import org.wso2.carbon.core.services.util.CarbonAuthenticationUtil;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.identity.authenticator.signedjwt.internal.SignedJWTAuthenticatorServiceComponent;
import org.wso2.carbon.user.api.TenantManager;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.utils.AuthenticationObserver;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import javax.servlet.http.HttpServletRequest;
import java.security.interfaces.RSAPublicKey;
/**
* SignedJWTAuthenticator Authenticate a user with a signed JWT.
*/
public class SignedJWTAuthenticator implements CarbonServerAuthenticator {
public static final String SIGNED_JWT_AUTH_USERNAME = "Username";
private static final int DEFAULT_PRIORITY_LEVEL = 20;
private static final String AUTHENTICATOR_NAME = "SignedJWTAuthenticator";
private static final String AUTHORIZATION_HEADER_TYPE = "Bearer";
private static final Log log = LogFactory.getLog(SignedJWTAuthenticator.class);
@Override
public int getPriority() {
AuthenticatorsConfiguration authenticatorsConfiguration =
AuthenticatorsConfiguration.getInstance();
AuthenticatorsConfiguration.AuthenticatorConfig authenticatorConfig =
authenticatorsConfiguration.getAuthenticatorConfig(AUTHENTICATOR_NAME);
if (authenticatorConfig != null && authenticatorConfig.getPriority() > 0) {
return authenticatorConfig.getPriority();
}
return DEFAULT_PRIORITY_LEVEL;
}
@Override
public boolean isDisabled() {
AuthenticatorsConfiguration authenticatorsConfiguration =
AuthenticatorsConfiguration.getInstance();
AuthenticatorsConfiguration.AuthenticatorConfig authenticatorConfig =
authenticatorsConfiguration.getAuthenticatorConfig(AUTHENTICATOR_NAME);
return authenticatorConfig != null && authenticatorConfig.isDisabled();
}
@Override
public boolean authenticateWithRememberMe(MessageContext msgCxt) {
return false;
}
@Override
public String getAuthenticatorName() {
return AUTHENTICATOR_NAME;
}
@Override
public boolean isAuthenticated(MessageContext msgCxt) {
boolean isAuthenticated = false;
HttpServletRequest request =
(HttpServletRequest) msgCxt.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
try {
//Get the filesystem keystore default primary certificate
KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(
MultitenantConstants.SUPER_TENANT_ID);
keyStoreManager.getDefaultPrimaryCertificate();
String authorizationHeader = request.getHeader(HTTPConstants.HEADER_AUTHORIZATION);
String headerData = decodeAuthorizationHeader(authorizationHeader);
JWSVerifier verifier =
new RSASSAVerifier((RSAPublicKey) keyStoreManager.getDefaultPublicKey());
SignedJWT jwsObject = SignedJWT.parse(headerData);
if (jwsObject.verify(verifier)) {
String userName = jwsObject.getJWTClaimsSet().getStringClaim(SIGNED_JWT_AUTH_USERNAME);
String tenantDomain = MultitenantUtils.getTenantDomain(userName);
userName = MultitenantUtils.getTenantAwareUsername(userName);
TenantManager tenantManager = SignedJWTAuthenticatorServiceComponent
.getRealmService().getTenantManager();
int tenantId = tenantManager.getTenantId(tenantDomain);
if (tenantId == -1) {
log.error("tenantDomain is not valid. username : " + userName + ", tenantDomain : " + tenantDomain);
return false;
}
handleAuthenticationStarted(tenantId);
UserStoreManager userStore = SignedJWTAuthenticatorServiceComponent
.getRealmService().getTenantUserRealm(tenantId).getUserStoreManager();
if (userStore.isExistingUser(userName)) {
isAuthenticated = true;
}
if (isAuthenticated) {
CarbonAuthenticationUtil.onSuccessAdminLogin(request.getSession(), userName,
tenantId, tenantDomain,
"Signed JWT Authentication");
handleAuthenticationCompleted(tenantId, true);
return true;
} else {
log.error("Authentication Request is rejected. User : " + userName
+ " does not exists in tenant : " + tenantDomain + " 's UserStore");
CarbonAuthenticationUtil
.onFailedAdminLogin(request.getSession(), userName, tenantId,
"Signed JWT Authentication",
"User does not exists in UserStore");
handleAuthenticationCompleted(tenantId, false);
return false;
}
}
} catch (Exception e) {
log.error("Error authenticating the user " + e.getMessage(), e);
}
return isAuthenticated;
}
@Override
public boolean isHandle(MessageContext msgCxt) {
HttpServletRequest request =
(HttpServletRequest) msgCxt.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
String authorizationHeader = request.getHeader(HTTPConstants.HEADER_AUTHORIZATION);
if (log.isDebugEnabled() && (authorizationHeader != null)) {
log.debug("Authorization header found in the request");
}
if (authorizationHeader != null) {
String authType = getAuthType(authorizationHeader);
if (log.isDebugEnabled()) {
log.debug("Authorization header type is : " + authType);
}
if (authType != null && authType.equalsIgnoreCase(AUTHORIZATION_HEADER_TYPE)) {
if (log.isDebugEnabled()) {
log.debug("Request can be handled using this authenticator, so returning true");
}
return true;
}
}
return false;
}
/**
* Gets the authentication type in authorization header.
*
* @param authorizationHeader The authorization header - Authorization: Bearer QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
* @return The authentication type mentioned in authorization header.
*/
private String getAuthType(String authorizationHeader) {
String[] splitValues = null;
if (authorizationHeader != null) {
splitValues = authorizationHeader.trim().split(" ");
}
if (splitValues == null || splitValues.length == 0) {
if (log.isDebugEnabled()) {
log.debug("Authorization Type is not defined. Hence returning null");
}
return null;
}
return splitValues[0].trim();
}
private String decodeAuthorizationHeader(String authorizationHeader) {
String[] splitValues = authorizationHeader.trim().split(" ");
byte[] decodedBytes = Base64Utils.decode(splitValues[1].trim());
if (decodedBytes != null) {
return new String(decodedBytes);
} else {
if (log.isDebugEnabled()) {
log.debug("Error decoding authorization header.");
}
return null;
}
}
private void handleAuthenticationStarted(int tenantId) {
BundleContext bundleContext = SignedJWTAuthenticatorServiceComponent.getBundleContext();
if (bundleContext != null) {
ServiceTracker tracker =
new ServiceTracker(bundleContext,
AuthenticationObserver.class.getName(), null);
tracker.open();
Object[] services = tracker.getServices();
if (services != null) {
for (Object service : services) {
((AuthenticationObserver) service).startedAuthentication(tenantId);
}
}
tracker.close();
}
}
private void handleAuthenticationCompleted(int tenantId, boolean isSuccessful) {
BundleContext bundleContext = SignedJWTAuthenticatorServiceComponent.getBundleContext();
if (bundleContext != null) {
ServiceTracker tracker =
new ServiceTracker(bundleContext,
AuthenticationObserver.class.getName(), null);
tracker.open();
Object[] services = tracker.getServices();
if (services != null) {
for (Object service : services) {
((AuthenticationObserver) service).completedAuthentication(
tenantId, isSuccessful);
}
}
tracker.close();
}
}
}