diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..16823b1e1 --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,5 @@ +#Wed Jan 25 13:44:10 EST 2012 +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/.settings/org.maven.ide.eclipse.prefs b/.settings/org.maven.ide.eclipse.prefs index 46d0bb234..8741d2cfe 100644 --- a/.settings/org.maven.ide.eclipse.prefs +++ b/.settings/org.maven.ide.eclipse.prefs @@ -1,4 +1,4 @@ -#Wed Jan 04 13:38:09 EST 2012 +#Wed Jan 25 13:44:09 EST 2012 activeProfiles= eclipse.preferences.version=1 fullBuildGoals=process-test-resources diff --git a/server/.settings/com.springsource.sts.maven.prefs b/server/.settings/com.springsource.sts.maven.prefs index ca1b6dc30..096f543b1 100644 --- a/server/.settings/com.springsource.sts.maven.prefs +++ b/server/.settings/com.springsource.sts.maven.prefs @@ -1,3 +1,3 @@ -#Wed Jan 04 13:45:00 EST 2012 +#Wed Jan 25 13:44:10 EST 2012 com.springsource.sts.maven.maven.automatically.update=true eclipse.preferences.version=1 diff --git a/server/.settings/org.eclipse.m2e.core.prefs b/server/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..16823b1e1 --- /dev/null +++ b/server/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,5 @@ +#Wed Jan 25 13:44:10 EST 2012 +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/server/.settings/org.eclipse.wst.common.component b/server/.settings/org.eclipse.wst.common.component index 7297c8ed1..9ccb357b3 100644 --- a/server/.settings/org.eclipse.wst.common.component +++ b/server/.settings/org.eclipse.wst.common.component @@ -3,8 +3,8 @@ - + uses diff --git a/server/pom.xml b/server/pom.xml index 99e461b06..dc40a4dc9 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -2,11 +2,9 @@ 4.0.0 - org.mitre openid-connect-server OpenIdConnect Server war - 0.1 1.6 3.1.0.RELEASE @@ -392,7 +390,7 @@ - + com.google.code.maven-replacer-plugin maven-replacer-plugin diff --git a/server/src/main/java/org/mitre/jwt/model/ClaimSet.java b/server/src/main/java/org/mitre/jwt/model/ClaimSet.java index badd967da..5e0686424 100644 --- a/server/src/main/java/org/mitre/jwt/model/ClaimSet.java +++ b/server/src/main/java/org/mitre/jwt/model/ClaimSet.java @@ -1,11 +1,17 @@ package org.mitre.jwt.model; +import java.io.ByteArrayInputStream; +import java.io.InputStreamReader; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.codec.binary.Base64; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; /** @@ -20,7 +26,19 @@ public class ClaimSet { // the LinkedHashMap preserves insertion order private Map claims = new LinkedHashMap(); - /** + public ClaimSet() { + + } + + public ClaimSet(JsonObject json) { + loadFromJsonObject(json); + } + + public ClaimSet(String b64) { + loadFromBase64JsonObjectString(b64); + } + + /** * Get an extension claim */ public Object getClaim(String key) { @@ -86,6 +104,14 @@ public class ClaimSet { /** + * Clear all claims from this ClaimSet + * @see java.util.Map#clear() + */ + public void clear() { + claims.clear(); + } + + /** * Get a copy of this claim set as a JsonObject. The JsonObject is not * backed by a live copy of this ClaimSet. * @return a copy of the data in this header in a JsonObject @@ -128,5 +154,32 @@ public class ClaimSet { return o; } + /** + * Load new claims from the given json object. Will replace any existing claims, but does not clear claim set. + * @param json + */ + public void loadFromJsonObject(JsonObject json) { + for (Entry element : json.entrySet()) { + if (element.getValue().isJsonPrimitive()){ + // we handle all primitives in here + JsonPrimitive prim = element.getValue().getAsJsonPrimitive(); + setClaim(element.getKey(), prim); + } else { + setClaim(element.getKey(), element.getValue()); + } + } + } + + /** + * Load a new claims set from a Base64 encoded JSON Object string + */ + public void loadFromBase64JsonObjectString(String b64) { + byte[] b64decoded = Base64.decodeBase64(b64); + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(new InputStreamReader(new ByteArrayInputStream(b64decoded))).getAsJsonObject(); + + loadFromJsonObject(json); + } } diff --git a/server/src/main/java/org/mitre/jwt/model/Jwt.java b/server/src/main/java/org/mitre/jwt/model/Jwt.java index 2396fdb1e..7d59ca7bd 100644 --- a/server/src/main/java/org/mitre/jwt/model/Jwt.java +++ b/server/src/main/java/org/mitre/jwt/model/Jwt.java @@ -139,12 +139,8 @@ public class Jwt { String c64 = parts.get(1); String s64 = parts.get(2); - JsonParser parser = new JsonParser(); - JsonObject hjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(h64)))).getAsJsonObject(); - JsonObject cjo = parser.parse(new InputStreamReader(new ByteArrayInputStream(Base64.decodeBase64(c64)))).getAsJsonObject(); - // shuttle for return value - Jwt jwt = new Jwt(new JwtHeader(hjo), new JwtClaims(cjo), s64); + Jwt jwt = new Jwt(new JwtHeader(h64), new JwtClaims(c64), s64); // TODO: save the wire-encoded string in the Jwt object itself? diff --git a/server/src/main/java/org/mitre/jwt/model/JwtClaims.java b/server/src/main/java/org/mitre/jwt/model/JwtClaims.java index a50dc1971..042efb5bf 100644 --- a/server/src/main/java/org/mitre/jwt/model/JwtClaims.java +++ b/server/src/main/java/org/mitre/jwt/model/JwtClaims.java @@ -29,10 +29,21 @@ public class JwtClaims extends ClaimSet { //public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"); public JwtClaims() { - + super(); } public JwtClaims(JsonObject json) { + super(json); + } + + public JwtClaims(String b64) { + super(b64); + } + + @Override + public void loadFromJsonObject(JsonObject json) { + JsonObject pass = new JsonObject(); + for (Entry element : json.entrySet()) { if (element.getKey().equals(EXPIRATION)) { setExpiration(new Date(element.getValue().getAsLong() * 1000L)); @@ -51,15 +62,12 @@ public class JwtClaims extends ClaimSet { } else if (element.getKey().equals(TYPE)) { setType(element.getValue().getAsString()); } else { - if (element.getValue().isJsonPrimitive()){ - // we handle all primitives in here - JsonPrimitive prim = element.getValue().getAsJsonPrimitive(); - setClaim(element.getKey(), prim); - } else { - setClaim(element.getKey(), element.getValue()); - } + pass.add(element.getKey(), element.getValue()); } } + + // load all the generic claims into this object + super.loadFromJsonObject(pass); } /** diff --git a/server/src/main/java/org/mitre/jwt/model/JwtHeader.java b/server/src/main/java/org/mitre/jwt/model/JwtHeader.java index 20a50a27e..99c4b5767 100644 --- a/server/src/main/java/org/mitre/jwt/model/JwtHeader.java +++ b/server/src/main/java/org/mitre/jwt/model/JwtHeader.java @@ -18,7 +18,7 @@ public class JwtHeader extends ClaimSet { * Make an empty header */ public JwtHeader() { - + super(); } /** @@ -26,7 +26,22 @@ public class JwtHeader extends ClaimSet { * @param json */ public JwtHeader(JsonObject json) { + super(json); + } + + public JwtHeader(String b64) { + super(b64); + } + + /** + * Load all claims from the given json object into this object + */ + @Override + public void loadFromJsonObject(JsonObject json) { + + JsonObject pass = new JsonObject(); + for (Entry element : json.entrySet()) { if (element.getKey().equals(TYPE)) { this.setType(json.get(TYPE).getAsString()); @@ -35,15 +50,12 @@ public class JwtHeader extends ClaimSet { } else if (element.getKey().equals(ENCRYPTION_METHOD)) { this.setEncryptionMethod(json.get(ENCRYPTION_METHOD).getAsString()); } else { - if (element.getValue().isJsonPrimitive()){ - // we handle all primitives in here - JsonPrimitive prim = element.getValue().getAsJsonPrimitive(); - setClaim(element.getKey(), prim); - } else { - setClaim(element.getKey(), element.getValue()); - } + pass.add(element.getKey(), element.getValue()); } } + + // now load all the ones we didn't handly specially + super.loadFromJsonObject(pass); } /** diff --git a/server/src/main/java/org/mitre/openid/connect/model/IdToken.java b/server/src/main/java/org/mitre/openid/connect/model/IdToken.java index c8b534e0d..7de21f7fd 100644 --- a/server/src/main/java/org/mitre/openid/connect/model/IdToken.java +++ b/server/src/main/java/org/mitre/openid/connect/model/IdToken.java @@ -1,5 +1,7 @@ package org.mitre.openid.connect.model; +import java.util.List; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -10,6 +12,11 @@ import javax.persistence.Table; import javax.persistence.Transient; import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.model.JwtClaims; +import org.mitre.jwt.model.JwtHeader; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; @Entity @Table(name="idtoken") @@ -18,6 +25,24 @@ import org.mitre.jwt.model.Jwt; }) public class IdToken extends Jwt { + /** + * Create a blank IdToken + */ + public IdToken() { + super(); + } + + /** + * Create an IdToken from the requisite pieces. + * @param header + * @param claims + * @param signature + */ + public IdToken(JwtHeader header, JwtClaims claims, String signature) { + super(header, claims, signature); + } + + private Long id; /** @@ -52,5 +77,33 @@ public class IdToken extends Jwt { } + /** + * Parse a wire-encoded IdToken. + * + */ + public static IdToken parse(String s) { + + // TODO: this code was copied nearly verbatim from Jwt.parse, and + // we should figure out how to re-use and abstract bits, likely + + // split on the dots + List parts = Lists.newArrayList(Splitter.on(".").split(s)); + + if (parts.size() != 3) { + throw new IllegalArgumentException("Invalid JWT format."); + } + + String h64 = parts.get(0); + String c64 = parts.get(1); + String s64 = parts.get(2); + + // shuttle for return value + IdToken idToken = new IdToken(new JwtHeader(h64), new IdTokenClaims(c64), s64); + + // TODO: save the wire-encoded string in the Jwt object itself? + + return idToken; + + } } diff --git a/server/src/main/java/org/mitre/openid/connect/model/IdTokenClaims.java b/server/src/main/java/org/mitre/openid/connect/model/IdTokenClaims.java index ed2d8096f..18c9ad57d 100644 --- a/server/src/main/java/org/mitre/openid/connect/model/IdTokenClaims.java +++ b/server/src/main/java/org/mitre/openid/connect/model/IdTokenClaims.java @@ -1,7 +1,9 @@ package org.mitre.openid.connect.model; import java.util.Date; +import java.util.Map.Entry; +import javax.persistence.Basic; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -11,6 +13,10 @@ import javax.persistence.Transient; import org.mitre.jwt.model.JwtClaims; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + @Entity @Table(name="idtokenclaims") @@ -23,6 +29,20 @@ public class IdTokenClaims extends JwtClaims { private Long id; + + + public IdTokenClaims() { + super(); + } + + public IdTokenClaims(JsonObject json) { + super(json); + } + + public IdTokenClaims(String b64) { + super(b64); + } + /** * @return the id */ @@ -74,4 +94,55 @@ public class IdTokenClaims extends JwtClaims { setClaim(AUTH_TIME, authTime); } + + /** + * Get the seraialized form of this claim set + */ + @Basic + public String getSerializedForm() { + // TODO Auto-generated method stub + JsonObject o = super.getAsJsonObject(); + + return o.toString(); + } + + /** + * Set up the claims in this object from the serialized form. This clears all current claims from the object. + * @param s a JSON Object string to load into this object + * @throws IllegalArgumentException if s is not a valid JSON object string + */ + public void setSerializedForm(String s) { + JsonParser parser = new JsonParser(); + JsonElement json = parser.parse(s); + if (json != null && json.isJsonObject()) { + loadFromJsonObject(json.getAsJsonObject()); + } else { + throw new IllegalArgumentException("Could not parse: " + s); + } + } + + + /** + * Load this IdToken from a JSON Object + */ + @Override + public void loadFromJsonObject(JsonObject json) { + JsonObject pass = new JsonObject(); + + for (Entry element : json.entrySet()) { + if (element.getKey().equals(USER_ID)) { + setUserId(element.getValue().getAsString()); + } else if (element.getKey().equals(AUTHENTICATION_CONTEXT_CLASS_REFERENCE)) { + setAuthContext(element.getValue().getAsString()); + } else if (element.getKey().equals(NONCE)) { + setNonce(element.getValue().getAsString()); + } else if (element.getKey().equals(AUTH_TIME)) { + setAuthTime(new Date(element.getValue().getAsLong() * 1000L)); + } else { + pass.add(element.getKey(), element.getValue()); + } + } + + super.loadFromJsonObject(pass); + } } diff --git a/server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java b/server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java index 1430acef9..3e778add3 100644 --- a/server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java +++ b/server/src/main/java/org/mitre/openid/connect/web/CheckIDEndpoint.java @@ -1,5 +1,6 @@ package org.mitre.openid.connect.web; +import org.mitre.openid.connect.model.IdToken; import org.mitre.openid.connect.model.IdTokenClaims; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -7,15 +8,17 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; @Controller -@RequestMapping("/checkid") public class CheckIDEndpoint { - @RequestMapping("/") - public ModelAndView checkID(@RequestParam("id_token") String idToken, ModelAndView mav) { + + + @RequestMapping("/checkid") + public ModelAndView checkID(@RequestParam("id_token") String tokenString, ModelAndView mav) { + + IdToken token = IdToken.parse(tokenString); + - IdTokenClaims token = new IdTokenClaims(); - //TODO: Set claims return new ModelAndView("jsonIdTokenView", "checkId", token); } diff --git a/server/src/main/java/org/mitre/swd/web/SimpleWebDiscoveryEndpoint.java b/server/src/main/java/org/mitre/swd/web/SimpleWebDiscoveryEndpoint.java new file mode 100644 index 000000000..6b9481e1b --- /dev/null +++ b/server/src/main/java/org/mitre/swd/web/SimpleWebDiscoveryEndpoint.java @@ -0,0 +1,55 @@ +package org.mitre.swd.web; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.ModelAndView; + +@Controller +public class SimpleWebDiscoveryEndpoint { + + @RequestMapping(value="/.well-known/simple-web-discovery", + params={"principal", "service=http://openid.net/specs/connect/1.0/issuer"}) + public ModelAndView openIdConnectIssuerDiscovery(@RequestParam("principal") String principal, ModelAndView modelAndView) { + return modelAndView; + } + + + /* + * version string Version of the provider response. "3.0" is the default. + * issuer string The https: URL with no path component that the OP asserts as its Issuer Identifier + * authorization_endpoint string URL of the OP's Authentication and Authorization Endpoint [OpenID.Messages] + * token_endpoint string URL of the OP's OAuth 2.0 Token Endpoint [OpenID.Messages] + * userinfo_endpoint string URL of the OP's UserInfo Endpoint [OpenID.Messages] + * check_id_endpoint string URL of the OP's Check ID Endpoint [OpenID.Messages] + * refresh_session_endpoint string URL of the OP's Refresh Session Endpoint [OpenID.Session] + * end_session_endpoint string URL of the OP's End Session Endpoint [OpenID.Session] + * jwk_url string URL of the OP's JSON Web Key [JWK] document. Server's signing Key + * jwk_encryption_url string URL of the OP's JSON Web Key [JWK] document. Server's Encryption Key, if not present, its value is the same as the URL provided by jwk_url + * x509_url string URL of the OP's X.509 certificates in PEM format. + * x509_encryption_url string URL of the OP's X.509 certificates in PEM format. Server's Encryption Key, if not present its value is the same as the URL provided by x509_url + * registration_endpoint string URL of the OP's Dynamic Client Registration Endpoint [OpenID.Registration] + * scopes_supported array A JSON array containing a list of the OAuth 2.0 [OAuth2.0] scope values that this server supports. The server MUST support the openid scope value. + * response_types_supported array A JSON array containing a list of the OAuth 2.0 response_type that this server supports. The server MUST support the code response_type. + * acrs_supported array A JSON array containing a list of the Authentication Context Class References that this server supports. + * user_id_types_supported array A JSON array containing a list of the user identifier types that this server supports. Valid types include pairwise and public. + * userinfo_algs_supported array A JSON array containing a list of the JWS [JWS] and JWE [JWE] signing and encryption algorithms supported by the UserInfo Endpoint to encode the JWT [JWT]. + * id_token_algs_supported array A JSON array containing a list of the JWS [JWS] and JWE [JWE] signing and encryption algorithms supported by the Authorization Server for the ID Token to encode the JWT [JWT]. + * request_object_algs_supported array A JSON array containing a list of the JWS [JWS] and JWE [JWE] signing and encryption algorithms supported by the Authorization Server for the OpenID Request Object described in Section 2.1.2.1 of OpenID Connect Messages 1.0 [OpenID.Messages] to encode the JWT [JWT]. Servers SHOULD support HS256. + * token_endpoint_auth_types_supported array A JSON array containing a list of authentication types supported by this Token Endpoint. The options are client_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 2.2.1 of OpenID Connect Messages 1.0 [OpenID.Messages]. Other Authentication types may be defined by extension. If unspecified or omitted, the default is client_secret_basic HTTP Basic Authentication Scheme as specified in section 2.3.1 of OAuth 2.0 [OAuth2.0]. + * token_endpoint_auth_algs_supported array A JSON array containing a list of the JWS [JWS] signing algorithms supported by the Token Endpoint for the private_key_jwt method to encode the JWT [JWT]. Servers SHOULD support RS256. + */ + @RequestMapping("/.well-known/openid-configuration") + public ModelAndView providerConfiguration(ModelAndView modelAndView) { + + Map m = modelAndView.getModel(); + m.put("version", "3.0"); + // TODO: everything in the list up there + + return modelAndView; + } + +}