net.oauth.jsontoken.JsonTokenTest.java Source code

Java tutorial

Introduction

Here is the source code for net.oauth.jsontoken.JsonTokenTest.java

Source

/**
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package net.oauth.jsontoken;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

import net.oauth.jsontoken.crypto.HmacSHA256Signer;
import net.oauth.jsontoken.crypto.RsaSHA256Signer;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.joda.time.Duration;
import org.joda.time.Instant;

import java.security.SignatureException;
import java.util.regex.Pattern;

public class JsonTokenTest extends JsonTokenTestBase {

    private static final Duration SKEW = Duration.standardMinutes(1);

    public static String TOKEN_STRING = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleTIifQ.eyJpc3MiOiJnb29nbGUuY29tIiwiYmFyIjoxNSwiZm9vIjoic29tZSB2YWx1ZSIsImF1ZCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbSIsImlhdCI6MTI3NjY2OTcyMiwiZXhwIjoxMjc2NjY5NzIyfQ.jKcuP6BR_-cKpQv2XdFLguYgOxw4ahkZiqjcgrQcm70";
    public static String TOKEN_STRING_ISSUER_NULL = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleTIifQ.eyJpc3MiOm51bGwsImJhciI6MTUsImZvbyI6InNvbWUgdmFsdWUiLCJhdWQiOiJodHRwOi8vd3d3Lmdvb2dsZS5jb20iLCJpYXQiOjEyNzY2Njk3MjIsImV4cCI6MTI3NjY2OTcyMn0.jKcuP6BR_-cKpQv2XdFLguYgOxw4ahkZiqjcgrQcm70";
    public static String TOKEN_STRING_BAD_SIG = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleTIifQ.eyJpc3MiOiJnb29nbGUuY29tIiwiYmFyIjoxNSwiZm9vIjoic29tZSB2YWx1ZSIsImF1ZCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbSIsImlhdCI6MTI3NjY2OTcyMiwiZXhwIjoxMjc2NjY5NzIyfQ.jKcuP6BR_";
    public static String TOKEN_STRING_2PARTS = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleTIifQ.eyJpc3MiOiJnb29nbGUuY29tIiwiYmFyIjoxNSwiZm9vIjoic29tZSB2YWx1ZSIsImF1ZCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbSIsImlhdCI6MTI3NjY2OTcyMiwiZXhwIjoxMjc2NjY5NzIyfQ";
    public static String TOKEN_STRING_EMPTY_SIG = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleTIifQ.eyJpc3MiOiJnb29nbGUuY29tIiwiYmFyIjoxNSwiZm9vIjoic29tZSB2YWx1ZSIsImF1ZCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbSIsImlhdCI6MTI3NjY2OTcyMiwiZXhwIjoxMjc2NjY5NzIyfQ.";
    public static String TOKEN_STRING_CORRUPT_HEADER = "0yJ0bGci0iJIUzI0NiIsIm0pZCI60mtleT0ifQ.eyJpc3MiOiJnb29nbGUuY29tIiwiYmFyIjoxNSwiZm9vIjoic29tZSB2YWx1ZSIsImF1ZCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbSIsImlhdCI6MTI3NjY2OTcyMiwiZXhwIjoxMjc2NjY5NzIyfQ.jKcuP6BR_-cKpQv2XdFLguYgOxw4ahkZiqjcgrQcm70";
    public static String TOKEN_STRING_CORRUPT_PAYLOAD = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleTIifQ.eyJpc3&&&&&XtOiJnb290bGUuY20tIiwiYmFyIjoxNSwiZm9vIjoic290ZSB2YWx1ZSIsImF1ZCI6Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbSIsImlhdCI6MTI3NjY2OTcyMiwiZXhwIjoxMjc2NjY5NzIyfQ.jKcuP6BR_-cKpQv2XdFLguYgOxw4ahkZiqjcgrQcm70";
    public FakeClock clock = new FakeClock(SKEW);

    @Override
    public void setUp() throws Exception {
        super.setUp();
        clock.setNow(new Instant(1276669722000L));
    }

    public void testCreateJsonToken() throws Exception {
        HmacSHA256Signer signer = new HmacSHA256Signer("google.com", "key2", SYMMETRIC_KEY);

        JsonToken token = new JsonToken(signer, clock);
        token.setParam("bar", 15);
        token.setParam("foo", "some value");
        token.setAudience("http://www.google.com");
        token.setIssuedAt(clock.now());
        token.setExpiration(clock.now().withDurationAdded(60, 1));

        assertEquals(TOKEN_STRING, token.serializeAndSign());
    }

    private void checkTimeFrame(Instant issuedAt, Instant expiration) throws Exception {
        HmacSHA256Signer signer = new HmacSHA256Signer("google.com", "key2", SYMMETRIC_KEY);
        JsonToken token = new JsonToken(signer, clock);
        if (issuedAt != null) {
            token.setIssuedAt(issuedAt);
        }
        if (expiration != null) {
            token.setExpiration(expiration);
        }
        token.setAudience("http://www.google.com");

        JsonToken verifiedToken = new JsonTokenParser(clock, locators, new IgnoreAudience())
                .verifyAndDeserialize(token.serializeAndSign());
        assertEquals(issuedAt, verifiedToken.getIssuedAt());
        assertEquals(expiration, verifiedToken.getExpiration());
    }

    private void checkTimeFrameIllegalStateException(Instant issuedAt, Instant expiration) throws Exception {
        try {
            checkTimeFrame(issuedAt, expiration);
            fail("IllegalStateException should be thrown.");
        } catch (IllegalStateException e) {
            // Pass.
        }
    }

    public void testIssuedAtAfterExpiration() throws Exception {
        Instant issuedAt = clock.now();
        Instant expiration = issuedAt.minus(Duration.standardSeconds(1));
        checkTimeFrameIllegalStateException(issuedAt, expiration);
    }

    public void testIssueAtSkew() throws Exception {
        Instant issuedAt = clock.now().plus(SKEW.minus(Duration.standardSeconds(1)));
        Instant expiration = issuedAt.plus(Duration.standardSeconds(1));
        checkTimeFrame(issuedAt, expiration);
    }

    public void testIssueAtTooMuchSkew() throws Exception {
        Instant issuedAt = clock.now().plus(SKEW.plus(Duration.standardSeconds(1)));
        Instant expiration = issuedAt.plus(Duration.standardSeconds(1));
        checkTimeFrameIllegalStateException(issuedAt, expiration);
    }

    public void testExpirationSkew() throws Exception {
        Instant expiration = clock.now().minus(SKEW.minus(Duration.standardSeconds(1)));
        Instant issuedAt = expiration.minus(Duration.standardSeconds(1));
        checkTimeFrame(issuedAt, expiration);
    }

    public void testExpirationTooMuchSkew() throws Exception {
        Instant expiration = clock.now().minus(SKEW.plus(Duration.standardSeconds(1)));
        Instant issuedAt = expiration.minus(Duration.standardSeconds(1));
        checkTimeFrameIllegalStateException(issuedAt, expiration);
    }

    public void testIssuedAtNull() throws Exception {
        Instant expiration = clock.now().minus(SKEW.minus(Duration.standardSeconds(1)));
        checkTimeFrame(null, expiration);
    }

    public void testExpirationNull() throws Exception {
        Instant issuedAt = clock.now().plus(SKEW.minus(Duration.standardSeconds(1)));
        checkTimeFrame(issuedAt, null);
    }

    public void testIssueAtNullExpirationNull() throws Exception {
        checkTimeFrame(null, null);
    }

    public void testFutureToken() throws Exception {
        Instant issuedAt = clock.now().plus(SKEW.plus(Duration.standardSeconds(1)));
        Instant expiration = issuedAt.plus(Duration.standardDays(1));
        checkTimeFrameIllegalStateException(issuedAt, expiration);
    }

    public void testPastToken() throws Exception {
        Instant expiration = clock.now().minus(SKEW.plus(Duration.standardSeconds(1)));
        Instant issuedAt = expiration.minus(Duration.standardDays(1));
        checkTimeFrameIllegalStateException(issuedAt, expiration);
    }

    public void testJsonNull() throws Exception {
        JsonTokenParser parser = new JsonTokenParser(null, null);
        JsonToken token = parser.deserialize(TOKEN_STRING_ISSUER_NULL);
        assertNull(token.getIssuer());
    }

    public void testDeserializeInvalidToken() throws Exception {
        JsonTokenParser parser = new JsonTokenParser(clock, locators, new IgnoreAudience());
        JsonToken token1 = parser.deserialize(TOKEN_STRING_BAD_SIG);
        deserializeAndExpectIllegalArgument(parser, TOKEN_STRING_2PARTS);
        deserializeAndExpectIllegalArgument(parser, TOKEN_STRING_EMPTY_SIG);
    }

    public void testDeserializeCorruptJson() throws Exception {
        JsonTokenParser parser = new JsonTokenParser(clock, locators, new IgnoreAudience());
        try {
            parser.deserialize(TOKEN_STRING_CORRUPT_HEADER);
            fail("Expected JsonParseException");
        } catch (JsonParseException e) {
            // no-op
        }
        try {
            parser.deserialize(TOKEN_STRING_CORRUPT_PAYLOAD);
            fail("Expected JsonParseException");
        } catch (JsonParseException e) {
            // no-op
        }
    }

    private void deserializeAndExpectIllegalArgument(JsonTokenParser parser, String tokenString)
            throws SignatureException {
        try {
            parser.deserialize(tokenString);
            fail("Expected IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            // no-op
        } catch (IllegalStateException e) {
            // no-op
        }
    }

    public void testDeserialize() throws Exception {
        JsonTokenParser parser = new JsonTokenParser(clock, locators, new IgnoreAudience());
        JsonToken token = parser.deserialize(TOKEN_STRING);

        assertEquals("google.com", token.getIssuer());
        assertEquals(15, token.getParamAsPrimitive("bar").getAsLong());
        assertEquals("some value", token.getParamAsPrimitive("foo").getAsString());
    }

    public void testVerifyAndDeserialize() throws Exception {
        JsonTokenParser parser = new JsonTokenParser(clock, locators, new IgnoreAudience());
        JsonToken token = parser.verifyAndDeserialize(TOKEN_STRING);

        assertEquals("google.com", token.getIssuer());
        assertEquals(15, token.getParamAsPrimitive("bar").getAsLong());
        assertEquals("some value", token.getParamAsPrimitive("foo").getAsString());
    }

    public static String TOKEN_FROM_RUBY = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8";

    public void testVerificationOnTokenFromRuby() throws Exception {
        JsonTokenParser parser = new JsonTokenParser(clock, locatorsFromRuby, new IgnoreAudience());
        JsonToken token = parser.verifyAndDeserialize(TOKEN_FROM_RUBY);
    }

    public void testCreateAnotherJsonToken() throws Exception {
        HmacSHA256Signer signer = new HmacSHA256Signer(null, (String) null, "secret".getBytes());

        JsonToken token = new JsonToken(signer, clock);
        token.setParam("hello", "world");
        String encodedToken = token.serializeAndSign();
    }

    public void testPublicKey() throws Exception {

        RsaSHA256Signer signer = new RsaSHA256Signer("google.com", "key1", privateKey);

        JsonToken token = new JsonToken(signer, clock);
        token.setParam("bar", 15);
        token.setParam("foo", "some value");
        token.setExpiration(clock.now().withDurationAdded(60, 1));

        String tokenString = token.serializeAndSign();

        assertNotNull(token.toString());

        JsonTokenParser parser = new JsonTokenParser(clock, locators, new IgnoreAudience());
        token = parser.verifyAndDeserialize(tokenString);
        assertEquals("google.com", token.getIssuer());
        assertEquals(15, token.getParamAsPrimitive("bar").getAsLong());
        assertEquals("some value", token.getParamAsPrimitive("foo").getAsString());

        // now test what happens if we tamper with the token
        JsonObject payload = new JsonParser()
                .parse(StringUtils.newStringUtf8(Base64.decodeBase64(tokenString.split(Pattern.quote("."))[1])))
                .getAsJsonObject();
        payload.remove("bar");
        payload.addProperty("bar", 14);
        String payloadString = new Gson().toJson(payload);
        String[] parts = tokenString.split("\\.");
        parts[1] = Base64.encodeBase64URLSafeString(payloadString.getBytes());
        assertEquals(3, parts.length);

        String tamperedToken = parts[0] + "." + parts[1] + "." + parts[2];

        try {
            token = parser.verifyAndDeserialize(tamperedToken);
            fail("verification should have failed");
        } catch (SignatureException e) {
            // expected
        }
    }
}