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

com.threerings.facebook.SignedRequest Maven / Gradle / Ivy

The newest version!
package com.threerings.facebook;

import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;

import org.apache.commons.codec.binary.Base64;

import org.json.JSONException;
import org.json.JSONObject;

import com.threerings.servlet.util.Parameters;

/**
 * Decodes a signed_request from Facebook.
 */
public class SignedRequest
{
    public SignedRequest (Parameters params, String appSecret)
    {
        _present = params.has("signed_request");
        if (!_present) {
            _token = null;
            _userId = null;
            return;
        }

        String param = params.get("signed_request");
        List split = Lists.newArrayList(PERIOD_SPLITTER.split(param));
        Preconditions.checkArgument(split.size() == 2,
            "Not properly period delimited [req=%s]", param);
        String encodedJson = split.get(1);
        String json = new String(new Base64(true).decode(encodedJson), Charsets.UTF_8);
        JSONObject obj;
        try {
            obj = new JSONObject(json);
        } catch (JSONException je) {
            throw new RuntimeException("Invalid json [json=" + json + ", req=" + param + "]", je);
        }

        String algorithm = obj.optString("algorithm");
        checkArg("HMAC-SHA256".equals(algorithm), "Improper algorithm in json", param, json);

        long issuedAt = obj.optLong("issued_at");
        checkArg(System.currentTimeMillis() / 1000 - ONE_HOUR < issuedAt, "More than an hour old",
            param, json);

        SecretKey key = new SecretKeySpec(appSecret.getBytes(Charsets.UTF_8), "HMACSHA256");
        Mac mac;
        try {
            mac = Mac.getInstance("HMACSHA256");
            mac.init(key);
        } catch (GeneralSecurityException gse) {
            throw new RuntimeException("Unable to initialize HMACSHA256. JVM busted?", gse);
        }
        byte[] digest = mac.doFinal(encodedJson.getBytes(Charsets.UTF_8));

        checkArg(Arrays.equals(new Base64(true).decode(split.get(0)), digest),
            "Invalid signature", param, json);

        _token = obj.optString("oauth_token", null);
        _userId = obj.optString("user_id", null);
    }

    public String getUserId ()
    {
        if (!isAuthorized()) {
            throw new RuntimeException("No userId. Call isAuthorized before calling getUserId");
        }
        return _userId;
    }

    public String getToken ()
    {
        if (!isAuthorized()) {
            throw new RuntimeException("No token. Call isAuthorized before calling getToken");
        }
        return _token;
    }

    public boolean isAuthorized ()
    {
        return _token != null;
    }

    public boolean isPresent ()
    {
        return _present;
    }

    protected void checkArg (boolean condition, String msg, String param, String json)
    {
        Preconditions.checkArgument(condition, msg + " [req=%s, json=%s]", param, json);
    }

    protected final boolean _present;
    protected final String _token, _userId;

    protected static final int ONE_HOUR = 60 * 60;

    protected static final Splitter PERIOD_SPLITTER = Splitter.on('.');
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy