All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.threerings.facebook.servlet.Swizzler Maven / Gradle / Ivy

The newest version!
//
// $Id$

package com.threerings.facebook.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.samskivert.util.StringUtil;
import com.samskivert.util.Tuple;

import com.samskivert.servlet.util.CookieUtil;

import com.threerings.app.client.ServiceException;
import com.threerings.app.data.AppCodes;
import com.threerings.app.server.ServletAuthUtil;
import com.threerings.app.server.ServletLogic;

import com.threerings.servlet.util.Parameters;
import com.threerings.servlet.util.QueryBuilder;
import com.threerings.user.OOOSiteIdentifier;
import static com.threerings.user.ExternalAuther.FACEBOOK;

import com.threerings.facebook.SignedRequest;
import static com.threerings.facebook.Log.log;

/**
 * Handles the fiddly business necessary to authenticate a Facebook app. This may involve
 * redirecting the user out of Facebook to set a cookie and then back to the app.
 *
 * 

One special bit of handling is done to allow linking directly to a GWT page and * preserving that page through the complicated series of redirects needed to properly * authenticate on all browsers. You can direct the user to * http://apps.facebook.com/yourapp/?token=SOMETOKEN and we will make every effort * to eventually load, inside the iframe, indexPath#SOMETOKEN.

*/ public class Swizzler { /** * Creates a swizzler for the given page request. * * @param indexUrl where to send properly authenticated users. * @param allowGuests if false, require users to accept the app before being sent to indexUrl. * @param nakedAppURL if parameters should be put into {@link AppCodes#PARAMETER_COOKIE} rather * than being left on indexUrl. */ public Swizzler (HttpServletRequest req, HttpServletResponse rsp, FacebookConfig fbconf, ServletLogic logic, String indexUrl, boolean allowGuests, boolean nakedAppURL) throws IOException { _fbconf = fbconf; _servletLogic = logic; _req = req; _rsp = rsp; _indexPath = indexUrl; _allowGuests = allowGuests; _nakedAppURL = nakedAppURL; _appURL = _fbconf.getFacebookAppURL(_req); _params = new Parameters(_req); _fbsig = new SignedRequest(_params, _fbconf.getFacebookSecret()); _swizzled = swizzle(); } /** * Returns true if this swizzle is on a facebook page. */ public boolean inFacebook () { return _fbsig.isPresent(); } /** * Returns true if this user hasn't authorized our app. */ public boolean isGuest () { return !_fbsig.isAuthorized(); } /** * Returns true if the user is already authenticated and they were redirected to the app, false * if some part of the swizzling process was performed. */ public boolean isSwizzled() { return _swizzled; } protected boolean swizzle () throws IOException { if (inFacebook()) { // if we're in facebook then... if (!isGuest()) { // ...if they've added our app, we're good return doAddedAuth(); } else if (_allowGuests) { // ...if not, either let them in as a guest return doGuestAuth(); } else { // ...or redirect them to the app login page QueryBuilder qb = new QueryBuilder(). add("client_id", _fbconf.getFacebookAppId()). add("redirect_uri", makeCleanQuery().toUrl(_appURL)); writeFrameRedirect(qb.toUrl("https://www.facebook.com/dialog/oauth")); return false; } } else { // if we're not in facebook, we must be swizzling String authtok = _params.get("swizzle"); if (StringUtil.isBlank(authtok)) { log.warning("We don't seem to be in Facebook or swizzling. WTF?", "req", _req.getRequestURI()); _rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return false; } else if (!_allowGuests && _servletLogic.getUser(authtok) == null) { log.warning("Requested to swizzle with invalid auth token", "req", _req.getRequestURI(), "authtok", authtok); _rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return false; } else { // send these cookies out with our redirect and send them back to the app addCookie(AppCodes.AUTH_COOKIE, authtok); if (_nakedAppURL) { QueryBuilder qb = makeCleanQuery(). add(AppCodes.PARAMETER_COOKIE_TIMESTAMP, System.currentTimeMillis()); addCookie(AppCodes.PARAMETER_COOKIE, qb.toString()); _rsp.sendRedirect(_appURL); } else { // we want to preserve all parameters except swizzle and fb params _rsp.sendRedirect(makeCleanQuery().toUrl(_appURL)); } return false; } } } protected boolean doGuestAuth () throws IOException { if (shouldSwizzleGuest()) { // swizzle them a faked up session key that can be associated with a newly // created account later // concatenate some stuff together and MD5 hash it String tok = new StringBuilder(). append(System.currentTimeMillis()).append(Math.random()).toString(); return swizzle(StringUtil.md5hex(tok)); } return handleSwizzledUser(); } protected boolean doAddedAuth () throws IOException { // if our external session is good to go, we're done if (!_servletLogic.refreshExternalSession(FACEBOOK, _fbsig.getUserId(), _fbsig.getToken(), _req, _rsp)) { // otherwise we need to authenticate the user and swizzle authtok into a cookie String authTok; try { authTok = _servletLogic.externalLogon( FACEBOOK, _fbsig.getUserId(), _fbsig.getToken()); } catch (ServiceException se) { log.warning("Failed to auth Facebook user", "uri", _req.getRequestURI(), "cause", se.getMessage()); _rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return false; } ServletAuthUtil.addAuthCookie(_req, _rsp, authTok, -1); return swizzle(authTok); } else { return handleSwizzledUser(); } } protected boolean handleSwizzledUser () throws IOException { if (_nakedAppURL) { for (String name : _params.names()) { if (!name.equals("signed_request")) { // they've a valid auth token, but they have URL params we'd like to get rid of return swizzle(CookieUtil.getCookieValue(_req, AppCodes.AUTH_COOKIE)); } } } // They good! Send em to the index with the GWT history token if it exists String url = _params.has("token") ? _indexPath + "#" + _params.get("token") : _indexPath; _rsp.sendRedirect(url); return true; } protected boolean shouldSwizzleGuest () { if (_servletLogic.getUser(_req) != null) { // we're dealing with a guest, and they've already been authenticated which is usually // an indication that someone else was logged in on this browser, and then a different // facebook user logged in. return true; } else if (StringUtil.isBlank(CookieUtil.getCookieValue(_req, AppCodes.AUTH_COOKIE))) { return true; // they don't have an auth cookie, which they need } else { return false; } } /** * Frame redirect the client to the rebuilt url with authTok added as a swizzle param. */ protected boolean swizzle (String authTok) throws IOException { // Kills HTTP Keep-Alive at this point, allowing Haproxy to see the next request and // add its SERVERID cookie to the swizzle so that users remain on the server they're // assigned to. _rsp.addHeader("Connection", "close"); writeFrameRedirect(makeCleanQuery().add("swizzle", authTok).toUrl(_req.getRequestURI())); return false; } public void writeFrameRedirect (String url) throws IOException { // we're in an iframe so we have to send down some JavaScript that jimmies the URL PrintWriter out = _rsp.getWriter(); out.println(""); out.close(); } protected QueryBuilder makeCleanQuery () { QueryBuilder qb = new QueryBuilder(); for (Tuple entry : _params.entries()) { if (!entry.left.equals("swizzle") && !entry.left.equals("signed_request")) { qb.add(entry.left, entry.right); } } return qb; } protected void addCookie (String key, Object value) { Cookie cookie = new Cookie(key, String.valueOf(value)); cookie.setMaxAge(-1); cookie.setPath("/"); if (!_req.getServerName().equals("localhost")) { // we'll be requested as something like 'apps.threerings.net' and we want this cookie // to be visible to billing.apps.threerings.net so we need to prefix domain with a . cookie.setDomain("." + _req.getServerName()); } _rsp.addCookie(cookie); } protected final FacebookConfig _fbconf; protected final boolean _swizzled; protected final boolean _nakedAppURL, _allowGuests; protected final Parameters _params; protected final SignedRequest _fbsig; protected final HttpServletResponse _rsp; protected final HttpServletRequest _req; protected final String _appURL, _indexPath; protected final ServletLogic _servletLogic; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy