diff --git a/mitre-local b/mitre-local
index f1f301046..b81dd3091 160000
--- a/mitre-local
+++ b/mitre-local
@@ -1 +1 @@
-Subproject commit f1f301046b6c6f397bc4d40a25dd407ca6f2b757
+Subproject commit b81dd3091e53be285bbfd9f0886b5a88b2cab19c
diff --git a/pom.xml b/pom.xml
index 1a0d6dc47..0de433283 100644
--- a/pom.xml
+++ b/pom.xml
@@ -194,7 +194,7 @@
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
-            <version>1.7.1</version>
+            <version>2.0</version>
         </dependency>
         <dependency>
             <groupId>org.eclipse.persistence</groupId>
@@ -214,7 +214,7 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>r09</version>
+            <version>10.0.1</version>
         </dependency>
         <dependency>
             <groupId>org.apache.httpcomponents</groupId>
@@ -229,7 +229,12 @@
         <dependency>
         	<groupId>commons-codec</groupId>
         	<artifactId>commons-codec</artifactId>
-        	<version>1.3</version>
+        	<version>1.6</version>
+        </dependency>
+        <dependency>
+        	<groupId>org.bouncycastle</groupId>
+        	<artifactId>bcprov-jdk16</artifactId>
+        	<version>1.46</version>
         </dependency>
     </dependencies>
     <repositories>
diff --git a/src/main/java/org/mitre/jwt/Jwt.java b/src/main/java/org/mitre/jwt/Jwt.java
new file mode 100644
index 000000000..fa47a2605
--- /dev/null
+++ b/src/main/java/org/mitre/jwt/Jwt.java
@@ -0,0 +1,118 @@
+package org.mitre.jwt;
+
+import java.util.List;
+
+import org.apache.commons.codec.binary.Base64;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class Jwt {
+
+	private JwtHeader header = new JwtHeader();
+	
+	private JwtClaims claims = new JwtClaims();
+	
+	private String signature;
+
+	
+	
+
+	/**
+     * @return the header
+     */
+    public JwtHeader getHeader() {
+    	return header;
+    }
+
+
+
+	/**
+     * @param header the header to set
+     */
+    public void setHeader(JwtHeader header) {
+    	this.header = header;
+    }
+
+
+
+	/**
+     * @return the claims
+     */
+    public JwtClaims getClaims() {
+    	return claims;
+    }
+
+
+
+	/**
+     * @param claims the claims to set
+     */
+    public void setClaims(JwtClaims claims) {
+    	this.claims = claims;
+    }
+
+
+
+	/**
+     * @return the signature
+     */
+    public String getSignature() {
+    	return signature;
+    }
+
+
+
+	/**
+     * @param signature the signature to set
+     */
+    public void setSignature(String signature) {
+    	this.signature = signature;
+    }
+	
+	/**
+	 * Return the canonical encoded string of this JWT
+	 */
+	public String toString() {
+		JsonObject h = header.getAsJsonObject();
+		JsonObject o = claims.getAsJsonObject();
+
+		String h64 = new String(Base64.encodeBase64URLSafe(h.toString().getBytes()));
+		String o64 = new String(Base64.encodeBase64(o.toString().getBytes()));
+		
+		return h64 + "." + o64 + "." + Strings.nullToEmpty(this.signature);		
+	}
+
+
+	/**
+	 * Parse a wire-encoded JWT
+	 */
+	public static Jwt parse(String s) {
+		
+		// split on the dots
+		List<String> parts = Lists.newArrayList(Splitter.on(".").split(s));
+		
+		if (parts.size() != 3) {
+			throw new IllegalArgumentException("Invalid JWT format.");
+		}
+		
+		String h64 = parts.get(0);
+		String o64 = parts.get(1);
+		String s64 = parts.get(2);
+		
+		JsonParser parser = new JsonParser();
+		
+		
+		
+		// shuttle for return value
+		Jwt jwt = new Jwt();
+		
+		return jwt;
+		
+	}
+	
+}
diff --git a/src/main/java/org/mitre/jwt/JwtClaims.java b/src/main/java/org/mitre/jwt/JwtClaims.java
new file mode 100644
index 000000000..dd4124db4
--- /dev/null
+++ b/src/main/java/org/mitre/jwt/JwtClaims.java
@@ -0,0 +1,236 @@
+package org.mitre.jwt;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gson.JsonObject;
+
+public class JwtClaims {
+	
+	/**
+	 * ISO8601 / RFC3339 Date Format
+	 */
+	public static DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-DD'T'HH:mm:ssz");
+
+	private Date expiration;
+	
+	private Date notBefore;
+	
+	private Date issuedAt;
+	
+	private String issuer;
+	
+	private String audience;
+	
+	private String principal;
+	
+	private String jwtId;
+	
+	private String type;
+	
+	private Map<String, Object> claims = new HashMap<String, Object>();
+
+	public JwtClaims() {
+		
+	}
+	
+	/**
+     * @return the expiration
+     */
+    public Date getExpiration() {
+    	return expiration;
+    }
+
+	/**
+     * @param expiration the expiration to set
+     */
+    public void setExpiration(Date expiration) {
+    	this.expiration = expiration;
+    }
+
+	/**
+     * @return the notBefore
+     */
+    public Date getNotBefore() {
+    	return notBefore;
+    }
+
+	/**
+     * @param notBefore the notBefore to set
+     */
+    public void setNotBefore(Date notBefore) {
+    	this.notBefore = notBefore;
+    }
+
+	/**
+     * @return the issuedAt
+     */
+    public Date getIssuedAt() {
+    	return issuedAt;
+    }
+
+	/**
+     * @param issuedAt the issuedAt to set
+     */
+    public void setIssuedAt(Date issuedAt) {
+    	this.issuedAt = issuedAt;
+    }
+
+	/**
+     * @return the issuer
+     */
+    public String getIssuer() {
+    	return issuer;
+    }
+
+	/**
+     * @param issuer the issuer to set
+     */
+    public void setIssuer(String issuer) {
+    	this.issuer = issuer;
+    }
+
+	/**
+     * @return the audience
+     */
+    public String getAudience() {
+    	return audience;
+    }
+
+	/**
+     * @param audience the audience to set
+     */
+    public void setAudience(String audience) {
+    	this.audience = audience;
+    }
+
+	/**
+     * @return the principal
+     */
+    public String getPrincipal() {
+    	return principal;
+    }
+
+	/**
+     * @param principal the principal to set
+     */
+    public void setPrincipal(String principal) {
+    	this.principal = principal;
+    }
+
+	/**
+     * @return the jwtId
+     */
+    public String getJwtId() {
+    	return jwtId;
+    }
+
+	/**
+     * @param jwtId the jwtId to set
+     */
+    public void setJwtId(String jwtId) {
+    	this.jwtId = jwtId;
+    }
+
+	/**
+     * @return the type
+     */
+    public String getType() {
+    	return type;
+    }
+
+	/**
+     * @param type the type to set
+     */
+    public void setType(String type) {
+    	this.type = type;
+    }
+	
+    /**
+     * Get an extension claim
+     */
+    public Object getClaim(String key) {
+    	return claims.get(key);
+    }
+    
+    /**
+     * Set an extension claim
+     */
+    public void setClaim(String key, Object value) {
+    	claims.put(key, value);
+    }
+
+    /**
+     * Remove an extension claim
+     */
+    public Object removeClaim(String key) {
+    	return claims.remove(key);
+    }
+    
+	/**
+	 * Get a copy of this header as a JsonObject. The JsonObject is not
+	 * backed by a live copy of this JwtHeader.
+	 * @return a copy of the data in this header in a JsonObject
+	 */
+	public JsonObject getAsJsonObject() {
+		JsonObject o = new JsonObject();
+
+		if (this.expiration != null) {
+			o.addProperty("exp", dateFormat.format(this.expiration));
+		}
+		
+		if (this.notBefore != null) {
+			o.addProperty("nbf", dateFormat.format(this.notBefore));
+		}
+		
+		if (this.issuedAt != null) {
+			o.addProperty("iat", dateFormat.format(this.issuedAt));
+		}
+		
+		if (this.issuer != null) {
+			o.addProperty("iss", this.issuer);
+		}
+		
+		if (this.audience != null) {
+			o.addProperty("aud", this.audience);
+		}
+		
+		if (this.principal != null) {
+			o.addProperty("prn", this.principal);
+		}
+		
+		if (this.jwtId != null) {
+			o.addProperty("jti", this.jwtId);
+		}
+		
+		if (this.type != null) {
+			o.addProperty("typ", this.type);
+		}
+		
+		if (this.claims != null) {
+			for (Map.Entry<String, Object> claim : this.claims.entrySet()) {
+				if (claim.getValue() instanceof String) {
+					o.addProperty(claim.getKey(), (String)claim.getValue());
+				} else if (claim.getValue() instanceof Number) {
+					o.addProperty(claim.getKey(), (Number)claim.getValue());
+				} else if (claim.getValue() instanceof Boolean) {
+					o.addProperty(claim.getKey(), (Boolean)claim.getValue());
+				} else if (claim.getValue() instanceof Character) {
+					o.addProperty(claim.getKey(), (Character)claim.getValue());
+				} else if (claim.getValue() != null) {
+					// try to put it in as a string
+					o.addProperty(claim.getKey(), claim.getValue().toString());
+				} else {
+					// otherwise add in as a null
+					o.add(claim.getKey(), null);
+				}
+	        }
+		}
+		
+		return o;
+	}
+
+}
diff --git a/src/main/java/org/mitre/jwt/JwtHeader.java b/src/main/java/org/mitre/jwt/JwtHeader.java
new file mode 100644
index 000000000..fd7560769
--- /dev/null
+++ b/src/main/java/org/mitre/jwt/JwtHeader.java
@@ -0,0 +1,133 @@
+package org.mitre.jwt;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gson.JsonObject;
+
+public class JwtHeader {
+
+	private String type;
+	
+	private String algorithm;
+	
+	private String encryptionMethod;
+	
+	private Map<String, Object> claims = new HashMap<String, Object>();
+
+	
+	public JwtHeader() {
+		
+    }
+
+
+	/**
+     * @return the type
+     */
+    public String getType() {
+    	return type;
+    }
+
+
+	/**
+     * @param type the type to set
+     */
+    public void setType(String type) {
+    	this.type = type;
+    }
+
+
+	/**
+     * @return the algorithm
+     */
+    public String getAlgorithm() {
+    	return algorithm;
+    }
+
+
+	/**
+     * @param algorithm the algorithm to set
+     */
+    public void setAlgorithm(String algorithm) {
+    	this.algorithm = algorithm;
+    }
+
+
+	/**
+     * @return the encryptionMethod
+     */
+    public String getEncryptionMethod() {
+    	return encryptionMethod;
+    }
+
+
+	/**
+     * @param encryptionMethod the encryptionMethod to set
+     */
+    public void setEncryptionMethod(String encryptionMethod) {
+    	this.encryptionMethod = encryptionMethod;
+    }
+
+    /**
+     * Get an extension claim
+     */
+    public Object getClaim(String key) {
+    	return claims.get(key);
+    }
+    
+    /**
+     * Set an extension claim
+     */
+    public void setClaim(String key, Object value) {
+    	claims.put(key, value);
+    }
+
+    /**
+     * Remove an extension claim
+     */
+    public Object removeClaim(String key) {
+    	return claims.remove(key);
+    }
+    
+	/**
+	 * Get a copy of this header as a JsonObject. The JsonObject is not
+	 * backed by a live copy of this JwtHeader.
+	 * @return a copy of the data in this header in a JsonObject
+	 */
+	public JsonObject getAsJsonObject() {
+		JsonObject o = new JsonObject();
+		
+		o.addProperty("typ", this.type);
+		if (this.algorithm != null) {
+			o.addProperty("alg", this.algorithm);
+		}
+		
+		if (this.encryptionMethod != null) {
+			o.addProperty("enc", this.encryptionMethod);
+		}
+		
+		if (this.claims != null) {
+			for (Map.Entry<String, Object> claim : this.claims.entrySet()) {
+				if (claim.getValue() instanceof String) {
+					o.addProperty(claim.getKey(), (String)claim.getValue());
+				} else if (claim.getValue() instanceof Number) {
+					o.addProperty(claim.getKey(), (Number)claim.getValue());
+				} else if (claim.getValue() instanceof Boolean) {
+					o.addProperty(claim.getKey(), (Boolean)claim.getValue());
+				} else if (claim.getValue() instanceof Character) {
+					o.addProperty(claim.getKey(), (Character)claim.getValue());
+				} else if (claim.getValue() != null) {
+					// try to put it in as a string
+					o.addProperty(claim.getKey(), claim.getValue().toString());
+				} else {
+					// otherwise add in as a null
+					o.add(claim.getKey(), null);
+				}
+	        }
+		}
+		
+		return o;
+	}
+
+
+}
diff --git a/src/main/java/org/mitre/jwt/JwtSigner.java b/src/main/java/org/mitre/jwt/JwtSigner.java
new file mode 100644
index 000000000..3d2f3c797
--- /dev/null
+++ b/src/main/java/org/mitre/jwt/JwtSigner.java
@@ -0,0 +1,7 @@
+package org.mitre.jwt;
+
+public interface JwtSigner {
+
+	public void sign(Jwt jwt);
+	
+}
diff --git a/src/main/java/org/mitre/openid/connect/model/IdToken.java b/src/main/java/org/mitre/openid/connect/model/IdToken.java
index 734f6bd9e..7e55800b9 100644
--- a/src/main/java/org/mitre/openid/connect/model/IdToken.java
+++ b/src/main/java/org/mitre/openid/connect/model/IdToken.java
@@ -2,11 +2,13 @@ package org.mitre.openid.connect.model;
 
 import javax.persistence.Entity;
 
+import org.mitre.jwt.Jwt;
+
 /*
  * TODO: This class needs to be encoded as a JWT
  */
 @Entity
-public class IdToken {
+public class IdToken extends Jwt {
 
 	private String iss;
 	private String user_id;
diff --git a/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml b/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
index ca62af15a..0ee38fc01 100644
--- a/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
+++ b/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
@@ -30,7 +30,7 @@
 	</beans:bean>
 
 	<!-- JSON views for each type of model object -->
-	<beans:bean id="jsonUserInfoView" class="org.mitre.openid.connect.model.serializer.JSONUserInfoView"/>
-	<beans:bean id="jsonIdTokenView" class="org.mitre.openid.connect.model.serializer.JSONIdTokenView"/>
+<!-- 	<beans:bean id="jsonUserInfoView" class="org.mitre.openid.connect.model.serializer.JSONUserInfoView"/> -->
+<!-- 	<beans:bean id="jsonIdTokenView" class="org.mitre.openid.connect.model.serializer.JSONIdTokenView"/> -->
 	
 </beans:beans>