/* * Copyright (c) 2013, 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.application.authenticator.openid.manager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openid4java.association.AssociationException; import org.openid4java.consumer.ConsumerException; import org.openid4java.consumer.ConsumerManager; import org.openid4java.consumer.VerificationResult; import org.openid4java.discovery.DiscoveryException; import org.openid4java.discovery.DiscoveryInformation; import org.openid4java.discovery.Identifier; import org.openid4java.discovery.yadis.YadisException; import org.openid4java.message.AuthRequest; import org.openid4java.message.AuthSuccess; import org.openid4java.message.MessageException; import org.openid4java.message.ParameterList; import org.openid4java.message.ax.AxMessage; import org.openid4java.message.ax.FetchRequest; import org.openid4java.message.ax.FetchResponse; import org.wso2.carbon.identity.application.authentication.framework.config.builder.FileBasedConfigurationBuilder; import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants; import org.wso2.carbon.identity.application.authenticator.openid.exception.OpenIDException; import org.wso2.carbon.identity.application.common.model.Claim; import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants; import org.wso2.carbon.identity.core.util.IdentityUtil; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.List; import java.util.Map; public class DefaultOpenIDManager implements OpenIDManager { // Smart OpenID Consumer Manager private static ConsumerManager consumerManager = new ConsumerManager(); private static Log log = LogFactory.getLog(DefaultOpenIDManager.class); @Override public String doOpenIDLogin(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws OpenIDException { String claimed_id = request.getParameter("claimed_id"); if (claimed_id == null) { claimed_id = context.getAuthenticatorProperties().get( IdentityApplicationConstants.Authenticator.OpenID.OPEN_ID_URL); } try { // Discovery on the user supplied ID List discoveries = consumerManager.discover(claimed_id); // Associate with the OP and share a secret DiscoveryInformation discovered = consumerManager.associate(discoveries); // Keeping necessary parameters to verify the AuthResponse request.getSession().setAttribute("openid-disc", discovered); String realm = IdentityUtil.getServerURL(FrameworkConstants.COMMONAUTH, true, true); String returnToURL = realm + "?sessionDataKey=" + context.getContextIdentifier(); AuthRequest authReq = consumerManager.authenticate(discovered, returnToURL); authReq.setRealm(realm); // Request subject attributes using Attribute Exchange extension specification AttributesRequestor attributesRequestor = getAttributeRequestor(); String[] requestedAttributes = attributesRequestor.getRequestedAttributes(claimed_id); // Getting required attributes using FetchRequest FetchRequest fetchRequest = FetchRequest.createFetchRequest(); for (String requestedAttribute : requestedAttributes) { fetchRequest.addAttribute(requestedAttribute, attributesRequestor.getTypeURI(claimed_id, requestedAttribute), attributesRequestor.isRequired(claimed_id, requestedAttribute), attributesRequestor.getCount(claimed_id, requestedAttribute)); } // Adding the AX extension to the AuthRequest message authReq.addExtension(fetchRequest); // Returning OP Url return authReq.getDestinationUrl(true); } catch (YadisException e) { if (e.getErrorCode() == 1796) { throw new OpenIDException(e.getMessage(), e); } throw new OpenIDException("Error while creating FetchRequest", e); } catch (MessageException e) { throw new OpenIDException("Error while creating FetchRequest", e); } catch (DiscoveryException e) { throw new OpenIDException("Error while doing OpenID Discovery", e); } catch (ConsumerException e) { throw new OpenIDException("Error while doing OpenID Authentication", e); } } @Override public void processOpenIDLoginResponse(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws OpenIDException { String contextIdentifier = context.getContextIdentifier(); try { // Getting all parameters in request including AuthResponse ParameterList authResponseParams = new ParameterList(request.getParameterMap()); // Previously discovered information DiscoveryInformation discovered = (DiscoveryInformation) request.getSession().getAttribute("openid-disc"); String returnToURL = IdentityUtil.getServerURL(FrameworkConstants.COMMONAUTH, true, true) + "?sessionDataKey=" + contextIdentifier; // Verify return-to, discoveries, nonce & signature // Signature will be verified using the shared secret VerificationResult verificationResult = consumerManager.verify(returnToURL, authResponseParams, discovered); Identifier verified = verificationResult.getVerifiedId(); // Identifier will be NULL if verification failed if (verified != null) { if (log.isDebugEnabled()) { log.debug("OpenID Response verification successfull. Verified ID: " + verified.getIdentifier()); } AuthSuccess authSuccess = (AuthSuccess) verificationResult.getAuthResponse(); AttributesRequestor attributesRequestor = getAttributeRequestor(); AuthenticatedUser authenticatedSubject = new AuthenticatedUser(); // Get requested attributes using AX extension if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) { Map<ClaimMapping, String> externalIDPClaims = new HashMap<ClaimMapping, String>(); String[] attrArray = attributesRequestor.getRequestedAttributes(authSuccess.getIdentity()); FetchResponse fetchResp; try { fetchResp = (FetchResponse) authSuccess.getExtension(AxMessage.OPENID_NS_AX); } catch (MessageException e) { //this is done because of response validation fails for yahoo response. fetchResp = new YahooFetchResponse(authSuccess.getParameterMap()); } for (String attr : attrArray) { String claimUri = attributesRequestor.getTypeURI(authSuccess.getIdentity(), attr); List attributeValues = fetchResp.getAttributeValuesByTypeUri(claimUri); if (attributeValues.get(0) instanceof String && ((String) attributeValues.get(0)).split(",").length > 1) { String[] splitString = ((String) attributeValues.get(0)).split(","); for (String part : splitString) { attributeValues.add(part); } } if (attributeValues.get(0) != null) { Claim claim = new Claim(); claim.setClaimUri(claimUri); ClaimMapping claimMapping = new ClaimMapping(); claimMapping.setRemoteClaim(claim); externalIDPClaims.put(claimMapping, getCommaSeperatedValue(attributeValues)); } } authenticatedSubject.setUserAttributes(externalIDPClaims); } authenticatedSubject.setAuthenticatedSubjectIdentifier(authSuccess.getClaimed()); context.setSubject(authenticatedSubject); } else { throw new OpenIDException("OpenID verification failed"); } } catch (AssociationException e) { throw new OpenIDException("Error while verifying OpenID response", e); } catch (MessageException e) { throw new OpenIDException("Error while verifying OpenID response", e); } catch (DiscoveryException e) { throw new OpenIDException("Error while verifying OpenID response", e); } } private AttributesRequestor getAttributeRequestor() { String attribRequestorClassName = FileBasedConfigurationBuilder.getInstance() .getAuthenticatorBean("OpenIDAuthenticator").getParameterMap() .get("AttributesRequestor"); AttributesRequestor attribRequestor = null; if (attribRequestorClassName != null) { try { // Bundle class loader will cache the loaded class and returned // the already loaded instance, hence calling this method // multiple times doesn't cost. Class clazz = Thread.currentThread().getContextClassLoader() .loadClass(attribRequestorClassName); attribRequestor = (AttributesRequestor) clazz.newInstance(); } catch (ClassNotFoundException e) { log.error("Error while instantiating the OpenIDManager ", e); } catch (InstantiationException e) { log.error("Error while instantiating the OpenIDManager ", e); } catch (IllegalAccessException e) { log.error("Error while instantiating the OpenIDManager ", e); } } else { attribRequestor = new SampleAttributesRequestor(); } if (attribRequestor != null) { attribRequestor.init(); } return attribRequestor; } private String getCommaSeperatedValue(List<String> values) { StringBuilder returnValue = null; for (String value : values) { if (returnValue == null) { returnValue = new StringBuilder(value); } else { returnValue.append("," + value); } } return returnValue != null ? returnValue.toString() : null; } }