package io.lumify.web.auth.oauth.routes; import io.lumify.core.model.user.UserRepository; import io.lumify.core.user.User; import io.lumify.core.util.LumifyLogger; import io.lumify.core.util.LumifyLoggerFactory; import io.lumify.miniweb.Handler; import io.lumify.miniweb.HandlerChain; import io.lumify.web.AuthenticationHandler; import io.lumify.web.CurrentUser; import io.lumify.web.auth.oauth.OAuthConfiguration; import org.json.JSONObject; import org.scribe.builder.ServiceBuilder; import org.scribe.builder.api.TwitterApi; import org.scribe.model.*; import org.scribe.oauth.OAuthService; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import static com.google.common.base.Preconditions.checkNotNull; public class Twitter implements Handler { private static final LumifyLogger LOGGER = LumifyLoggerFactory.getLogger(Twitter.class); private static final String OAUTH_REQUEST_TOKEN = "oauth_token"; public static final String OAUTH_TOKEN_PARAM_NAME = "oauth_token"; public static final String OAUTH_VERIFIER_PARAM_NAME = "oauth_verifier"; private final OAuthConfiguration config; private final UserRepository userRepository; public Twitter(OAuthConfiguration config, UserRepository userRepository) { this.config = config; this.userRepository = userRepository; checkNotNull(config.getKey(), "Twitter OAuth apiKey not set"); checkNotNull(config.getSecret(), "Twitter OAuth apiSecret not set"); } @Override public void handle(HttpServletRequest httpRequest, HttpServletResponse httpResponse, HandlerChain chain) throws Exception { if (httpRequest.getParameter(OAUTH_VERIFIER_PARAM_NAME) != null) { verify(httpRequest, httpResponse, chain); } else { login(httpRequest, httpResponse, chain); } } private void login(HttpServletRequest httpRequest, HttpServletResponse httpResponse, HandlerChain chain) throws IOException { OAuthService service = getOAuthService(httpRequest, true); Token requestToken = service.getRequestToken(); httpRequest.getSession().setAttribute(OAUTH_REQUEST_TOKEN, requestToken); String authUrl = service.getAuthorizationUrl(requestToken); httpResponse.sendRedirect(authUrl); } private void verify(HttpServletRequest httpRequest, HttpServletResponse httpResponse, HandlerChain chain) throws IOException { String inboundVerifier = httpRequest.getParameter(OAUTH_VERIFIER_PARAM_NAME); String inboundToken = httpRequest.getParameter(OAUTH_TOKEN_PARAM_NAME); Token storedToken = (Token) httpRequest.getSession().getAttribute(OAUTH_REQUEST_TOKEN); httpRequest.getSession().removeAttribute(OAUTH_REQUEST_TOKEN); if (storedToken == null) { LOGGER.warn("OAuth verification attempted but no stored token found in the user's session"); httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN); return; } if (!inboundToken.equals(storedToken.getToken())) { LOGGER.warn("OAuth verfication attempted, but oauth_token request param did not match token stored in user's session"); httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN); return; } Verifier verifier = new Verifier(inboundVerifier); OAuthService service = getOAuthService(httpRequest, false); // TODO: Store this token if authorization succeeds Token accessToken = service.getAccessToken(storedToken, verifier); OAuthRequest authRequest = new OAuthRequest(Verb.GET, "https://api.twitter.com/1.1/account/verify_credentials.json"); service.signRequest(accessToken, authRequest); Response authResponse = authRequest.send(); if (!authResponse.isSuccessful()) { LOGGER.warn("OAuth handshake completed, but Twitter credential verification failed: " + authResponse.getMessage()); httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN); return; } JSONObject jsonResponse = new JSONObject(authResponse.getBody()); String screenName = jsonResponse.getString("screen_name"); if (screenName == null) { LOGGER.warn("Twitter OAuth JSON authorization response did not contain a 'screen_name' value, which is required."); httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN); return; } String displayName = jsonResponse.getString("name"); if (displayName == null) { LOGGER.warn("Twitter OAuth JSON authorization response did not contain a 'name' value, which is required."); httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN); return; } String username = "twitter/" + screenName; User user = userRepository.findByUsername(username); if (user == null) { // For form based authentication, username and displayName will be the same String randomPassword = UserRepository.createRandomPassword(); user = userRepository.addUser(username, displayName, null, randomPassword, new String[0]); } userRepository.recordLogin(user, AuthenticationHandler.getRemoteAddr(httpRequest)); CurrentUser.set(httpRequest, user.getUserId(), user.getUsername()); httpResponse.sendRedirect(httpRequest.getServletContext().getContextPath() + "/"); } private OAuthService getOAuthService(HttpServletRequest request, boolean withCallback) { ServiceBuilder builder = new ServiceBuilder() .provider(TwitterApi.SSL.Authenticate.class) .apiKey(config.getKey()) .apiSecret(config.getSecret()); if (withCallback) { builder.callback(request.getRequestURL().toString()); } return builder.build(); } }