001/* 002 * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nelson Silva <nelson.silva@inevo.pt> - initial API and implementation 016 * Nuxeo 017 */ 018 019package org.nuxeo.ecm.platform.oauth2.openid.auth; 020 021import static org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants.LOGIN_ERROR; 022 023import java.util.ArrayList; 024import java.util.List; 025import java.util.Map; 026 027import javax.servlet.http.HttpServletRequest; 028import javax.servlet.http.HttpServletResponse; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.ecm.core.api.NuxeoException; 033import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo; 034import org.nuxeo.ecm.platform.oauth2.openid.OpenIDConnectProvider; 035import org.nuxeo.ecm.platform.oauth2.openid.OpenIDConnectProviderRegistry; 036import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin; 037import org.nuxeo.runtime.api.Framework; 038 039/** 040 * Authenticator using OpenID to retrieve user identity. 041 * 042 * @author Nelson Silva <nelson.silva@inevo.pt> 043 */ 044public class OpenIDConnectAuthenticator implements NuxeoAuthenticationPlugin { 045 046 private static final Log log = LogFactory.getLog(OpenIDConnectAuthenticator.class); 047 048 public static final String STATE_URL_PARAM_NAME = "state"; 049 050 public static final String STATE_SESSION_ATTRIBUTE = STATE_URL_PARAM_NAME; 051 052 public static final String CODE_URL_PARAM_NAME = "code"; 053 054 public static final String ERROR_URL_PARAM_NAME = "error"; 055 056 public static final String PROVIDER_URL_PARAM_NAME = "provider"; 057 058 public static final String USERINFO_KEY = "OPENID_USERINFO"; 059 060 public static final String PROPERTY_OAUTH_CREATE_USER = "nuxeo.oauth.auth.create.user"; 061 062 public static final String PROPERTY_SKIP_OAUTH_TOKEN = "nuxeo.skip.oauth.token.state.check"; 063 064 protected void sendError(HttpServletRequest req, String msg) { 065 req.setAttribute(LOGIN_ERROR, msg); 066 } 067 068 public UserIdentificationInfo retrieveIdentityFromOAuth(HttpServletRequest req, HttpServletResponse resp) { 069 070 // Getting the "error" URL parameter 071 String error = req.getParameter(ERROR_URL_PARAM_NAME); 072 073 // / Checking if there was an error such as the user denied access 074 if (error != null && error.length() > 0) { 075 sendError(req, "There was an error: \"" + error + "\"."); 076 return null; 077 } 078 079 // Getting the "code" URL parameter 080 String code = req.getParameter(CODE_URL_PARAM_NAME); 081 082 // Checking conditions on the "code" URL parameter 083 if (code == null || code.isEmpty()) { 084 sendError(req, "There was an error: \"" + code + "\"."); 085 return null; 086 } 087 088 // Getting the "provider" URL parameter 089 String serviceProviderName = req.getParameter(PROVIDER_URL_PARAM_NAME); 090 091 // Checking conditions on the "provider" URL parameter 092 if (serviceProviderName == null || serviceProviderName.isEmpty()) { 093 sendError(req, "Missing OpenID Connect Provider ID."); 094 return null; 095 } 096 097 try { 098 OpenIDConnectProviderRegistry registry = Framework.getLocalService(OpenIDConnectProviderRegistry.class); 099 OpenIDConnectProvider provider = registry.getProvider(serviceProviderName); 100 101 if (provider == null) { 102 sendError(req, "No service provider called: \"" + serviceProviderName + "\"."); 103 return null; 104 } 105 106 // Check the state token 107 108 if (!Framework.isBooleanPropertyTrue(PROPERTY_SKIP_OAUTH_TOKEN) && !provider.verifyStateToken(req)) { 109 sendError(req, "Invalid state parameter."); 110 } 111 112 // Validate the token 113 String accessToken = provider.getAccessToken(req, code); 114 115 if (accessToken == null) { 116 return null; 117 } 118 119 OpenIDUserInfo info = provider.getUserInfo(accessToken); 120 121 // Store the user info as a key in the request so apps can use it 122 // later in the chain 123 req.setAttribute(USERINFO_KEY, info); 124 125 UserResolver userResolver = provider.getUserResolver(); 126 127 String userId; 128 if (Framework.isBooleanPropertyTrue(PROPERTY_OAUTH_CREATE_USER)) { 129 userId = userResolver.findOrCreateNuxeoUser(info); 130 } else { 131 userId = userResolver.findNuxeoUser(info); 132 } 133 134 if (userId == null) { 135 136 sendError(req, "No user found with email: \"" + info.getEmail() + "\"."); 137 return null; 138 } 139 140 return new UserIdentificationInfo(userId, userId); 141 142 } catch (NuxeoException e) { 143 log.error("Error while retrieve Identity From OAuth", e); 144 } 145 146 return null; 147 } 148 149 @Override 150 public List<String> getUnAuthenticatedURLPrefix() { 151 return new ArrayList<String>(); 152 } 153 154 @Override 155 public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest, 156 HttpServletResponse httpResponse) { 157 String error = httpRequest.getParameter(ERROR_URL_PARAM_NAME); 158 String code = httpRequest.getParameter(CODE_URL_PARAM_NAME); 159 String serviceProviderName = httpRequest.getParameter(PROVIDER_URL_PARAM_NAME); 160 if (serviceProviderName == null) { 161 return null; 162 } 163 if (code == null && error == null) { 164 return null; 165 } 166 UserIdentificationInfo userIdent = retrieveIdentityFromOAuth(httpRequest, httpResponse); 167 if (userIdent != null) { 168 userIdent.setAuthPluginName("TRUSTED_LM"); 169 } 170 return userIdent; 171 } 172 173 @Override 174 public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) { 175 return false; 176 } 177 178 @Override 179 public Boolean needLoginPrompt(HttpServletRequest httpRequest) { 180 return false; 181 } 182 183 @Override 184 public void initPlugin(Map<String, String> parameters) { 185 } 186}