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