diff --git a/server/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/server/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java index cf823be5d..d3ed2d149 100644 --- a/server/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java +++ b/server/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java @@ -39,6 +39,10 @@ public class ClientDetailsEntity implements ClientDetails { } + public enum AuthType { + client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt + }; + private String clientId; private String clientSecret; private Set scope; @@ -53,6 +57,48 @@ public class ClientDetailsEntity implements ClientDetails { private String registeredRedirectUri; private Set resourceIds; + //Additional properties added by OpenID Connect Dynamic Client Registration spec + //http://openid.net/specs/openid-connect-registration-1_0.html + + /** + * List of email addresses for people allowed to administer the information for + * this Client. This is used by some providers to enable a web UI to modify the + * Client information. + */ + private Set contacts; + + private String applicationType;//native or web + private String applicationName; + private String logo_url; + private Set redirectUris; //Connect allows clients to have more than one redirectUri registered + private AuthType tokenEndpointAuthType = AuthType.client_secret_basic; + private String policyUrl; + private String jwk_url; + private String jwk_encryption_url; + private String x509Url; + private String x509EncryptionUrl; + private String sectorIdentifierUrl; + private String userIdType; + + /** + * OPTIONAL. The JWS [JWS] signature algorithm that MUST be required + * by the Authorization Server. All OpenID Request Objects from + * this client_id MUST be rejected if not signed by this algorithm. + */ + private String requireSignedRequestObject; + + private String userInfoSignedResponseAlg; + private Set userInfoEncryptedResponseAlgs; + private String idTokenSignedResponseAlg; + private Set idTokenEncryptedResponseAlgs; + + //Maximum age for any authentications + private Integer defaultMaxAge; + + private Boolean requireAuthTime; + + private String defaultACR; + // TODO: /* private boolean allowMultipleAccessTokens; // do we allow multiple access tokens, or not? @@ -480,4 +526,300 @@ public class ClientDetailsEntity implements ClientDetails { return 0; } + /** + * @return the contacts + */ + public Set getContacts() { + return contacts; + } + + /** + * @param contacts the contacts to set + */ + public void setContacts(Set contacts) { + this.contacts = contacts; + } + + /** + * @return the applicationType + */ + public String getApplicationType() { + return applicationType; + } + + /** + * @param applicationType the applicationType to set + */ + public void setApplicationType(String applicationType) { + this.applicationType = applicationType; + } + + /** + * @return the applicationName + */ + public String getApplicationName() { + return applicationName; + } + + /** + * @param applicationName the applicationName to set + */ + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } + + /** + * @return the logo_url + */ + public String getLogo_url() { + return logo_url; + } + + /** + * @param logo_url the logo_url to set + */ + public void setLogo_url(String logo_url) { + this.logo_url = logo_url; + } + + /** + * @return the redirectUris + */ + public Set getRedirectUris() { + return redirectUris; + } + + /** + * @param redirectUris the redirectUris to set + */ + public void setRedirectUris(Set redirectUris) { + this.redirectUris = redirectUris; + } + + /** + * @return the tokenEndpointAuthType + */ + public AuthType getTokenEndpointAuthType() { + return tokenEndpointAuthType; + } + + /** + * @param tokenEndpointAuthType the tokenEndpointAuthType to set + */ + public void setTokenEndpointAuthType(AuthType tokenEndpointAuthType) { + this.tokenEndpointAuthType = tokenEndpointAuthType; + } + + /** + * @return the policyUrl + */ + public String getPolicyUrl() { + return policyUrl; + } + + /** + * @param policyUrl the policyUrl to set + */ + public void setPolicyUrl(String policyUrl) { + this.policyUrl = policyUrl; + } + + /** + * @return the jwk_url + */ + public String getJwk_url() { + return jwk_url; + } + + /** + * @param jwk_url the jwk_url to set + */ + public void setJwk_url(String jwk_url) { + this.jwk_url = jwk_url; + } + + /** + * @return the jwk_encryption_url + */ + public String getJwk_encryption_url() { + return jwk_encryption_url; + } + + /** + * @param jwk_encryption_url the jwk_encryption_url to set + */ + public void setJwk_encryption_url(String jwk_encryption_url) { + this.jwk_encryption_url = jwk_encryption_url; + } + + /** + * @return the x509Url + */ + public String getX509Url() { + return x509Url; + } + + /** + * @param x509Url the x509Url to set + */ + public void setX509Url(String x509Url) { + this.x509Url = x509Url; + } + + /** + * @return the x509EncryptionUrl + */ + public String getX509EncryptionUrl() { + return x509EncryptionUrl; + } + + /** + * @param x509EncryptionUrl the x509EncryptionUrl to set + */ + public void setX509EncryptionUrl(String x509EncryptionUrl) { + this.x509EncryptionUrl = x509EncryptionUrl; + } + + /** + * @return the sectorIdentifierUrl + */ + public String getSectorIdentifierUrl() { + return sectorIdentifierUrl; + } + + /** + * @param sectorIdentifierUrl the sectorIdentifierUrl to set + */ + public void setSectorIdentifierUrl(String sectorIdentifierUrl) { + this.sectorIdentifierUrl = sectorIdentifierUrl; + } + + /** + * @return the userIdType + */ + public String getUserIdType() { + return userIdType; + } + + /** + * @param userIdType the userIdType to set + */ + public void setUserIdType(String userIdType) { + this.userIdType = userIdType; + } + + /** + * @return the requireSignedRequestObject + */ + public String getRequireSignedRequestObject() { + return requireSignedRequestObject; + } + + /** + * @param requireSignedRequestObject the requireSignedRequestObject to set + */ + public void setRequireSignedRequestObject(String requireSignedRequestObject) { + this.requireSignedRequestObject = requireSignedRequestObject; + } + + /** + * @return the userInfoSignedResponseAlg + */ + public String getUserInfoSignedResponseAlg() { + return userInfoSignedResponseAlg; + } + + /** + * @param userInfoSignedResponseAlg the userInfoSignedResponseAlg to set + */ + public void setUserInfoSignedResponseAlg(String userInfoSignedResponseAlg) { + this.userInfoSignedResponseAlg = userInfoSignedResponseAlg; + } + + /** + * @return the userInfoEncryptedResponseAlgs + */ + public Set getUserInfoEncryptedResponseAlgs() { + return userInfoEncryptedResponseAlgs; + } + + /** + * @param userInfoEncryptedResponseAlgs the userInfoEncryptedResponseAlgs to set + */ + public void setUserInfoEncryptedResponseAlgs( + Set userInfoEncryptedResponseAlgs) { + this.userInfoEncryptedResponseAlgs = userInfoEncryptedResponseAlgs; + } + + /** + * @return the idTokenEncryptedResponseAlgs + */ + public Set getIdTokenEncryptedResponseAlgs() { + return idTokenEncryptedResponseAlgs; + } + + /** + * @param idTokenEncryptedResponseAlgs the idTokenEncryptedResponseAlgs to set + */ + public void setIdTokenEncryptedResponseAlgs( + Set idTokenEncryptedResponseAlgs) { + this.idTokenEncryptedResponseAlgs = idTokenEncryptedResponseAlgs; + } + + /** + * @return the idTokenSignedResponseAlg + */ + public String getIdTokenSignedResponseAlg() { + return idTokenSignedResponseAlg; + } + + /** + * @param idTokenSignedResponseAlg the idTokenSignedResponseAlg to set + */ + public void setIdTokenSignedResponseAlg(String idTokenSignedResponseAlg) { + this.idTokenSignedResponseAlg = idTokenSignedResponseAlg; + } + + /** + * @return the defaultMaxAge + */ + public Integer getDefaultMaxAge() { + return defaultMaxAge; + } + + /** + * @param defaultMaxAge the defaultMaxAge to set + */ + public void setDefaultMaxAge(Integer defaultMaxAge) { + this.defaultMaxAge = defaultMaxAge; + } + + /** + * @return the requireAuthTime + */ + public Boolean getRequireAuthTime() { + return requireAuthTime; + } + + /** + * @param requireAuthTime the requireAuthTime to set + */ + public void setRequireAuthTime(Boolean requireAuthTime) { + this.requireAuthTime = requireAuthTime; + } + + /** + * @return the defaultACR + */ + public String getDefaultACR() { + return defaultACR; + } + + /** + * @param defaultACR the defaultACR to set + */ + public void setDefaultACR(String defaultACR) { + this.defaultACR = defaultACR; + } + } diff --git a/server/src/main/java/org/mitre/openid/connect/model/UserInfo.java b/server/src/main/java/org/mitre/openid/connect/model/UserInfo.java index e41ead75c..ff1803bd8 100644 --- a/server/src/main/java/org/mitre/openid/connect/model/UserInfo.java +++ b/server/src/main/java/org/mitre/openid/connect/model/UserInfo.java @@ -9,6 +9,8 @@ import javax.persistence.NamedQuery; import javax.persistence.OneToOne; import javax.persistence.Table; +import com.google.gson.JsonObject; + @Entity @Table(name="userinfo") @NamedQueries({ @@ -35,6 +37,38 @@ public class UserInfo { private String updatedTime; + public JsonObject toJson() { + JsonObject obj = new JsonObject(); + + obj.addProperty("user_id", getUserId()); + obj.addProperty("name", getName()); + obj.addProperty("given_name", getGivenName()); + obj.addProperty("family_name", getFamilyName()); + obj.addProperty("middle_name", getMiddleName()); + obj.addProperty("nickname", getNickname()); + obj.addProperty("profile", getProfile()); + obj.addProperty("picture", getPicture()); + obj.addProperty("website", getWebsite()); + obj.addProperty("verified", getVerified()); + obj.addProperty("gender", getGender()); + obj.addProperty("zone_info", getZoneinfo()); + obj.addProperty("locale", getLocale()); + obj.addProperty("phone_number", getPhoneNumber()); + obj.addProperty("updated_time", getUpdatedTime()); + + JsonObject addr = new JsonObject(); + addr.addProperty("formatted", getAddress().getFormatted()); + addr.addProperty("street_address", getAddress().getStreetAddress()); + addr.addProperty("locality", getAddress().getLocality()); + addr.addProperty("region", getAddress().getRegion()); + addr.addProperty("postal_code", getAddress().getPostalCode()); + addr.addProperty("country", getAddress().getCountry()); + + obj.add("address", addr); + + return obj; + } + /** * @return the userId */ diff --git a/server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java b/server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java index 6154757ba..e68d65934 100644 --- a/server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java +++ b/server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java @@ -1,26 +1,72 @@ package org.mitre.openid.connect.web; +import org.mitre.jwt.model.Jwt; +import org.mitre.jwt.model.JwtClaims; +import org.mitre.jwt.model.JwtHeader; +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.OAuth2AccessTokenEntity; +import org.mitre.oauth2.service.OAuth2TokenEntityService; import org.mitre.openid.connect.model.UserInfo; +import org.mitre.openid.connect.service.UserInfoService; +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.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; +/** + * OpenID Connect UserInfo endpoint, as specified in Standard sec 5 and Messages sec 2.4. + * + * @author AANGANES + * + */ @Controller -@RequestMapping("/userinfo") public class UserInfoEndpoint { - @RequestMapping("/") + @Autowired + OAuth2TokenEntityService tokenService; + + @Autowired + UserInfoService userInfoService; + + /** + * Get information about the user as specified in the accessToken->idToken included in this request + * + * @param accessToken the Access Token associated with this request + * @param schema the data schema to use, default is openid + * @param mav the ModelAndView object associated with this request + * @return JSON or JWT response containing UserInfo data + */ + @RequestMapping(value="/userinfo", method= {RequestMethod.GET, RequestMethod.POST}) public ModelAndView getInfo(@RequestParam("access_token") String accessToken, @RequestParam("schema") String schema, ModelAndView mav) { - UserInfo userInfo = new UserInfo(); + //This will throw the proper error if the token cannot be found + OAuth2AccessTokenEntity token = tokenService.getAccessToken(accessToken); + + if (schema != "openid") { + //openid is the ONLY defined schema and is a required parameter + //Will we be defining other schemas? + //if schema is unrecognized, throw an error? + + } + + String userId = token.getIdToken().getTokenClaims().getUserId(); + + UserInfo userInfo = userInfoService.getByUserId(userId); + + ClientDetailsEntity client = token.getClient(); - //populate with info + //if client wants plain JSON, give it JSON; if it wants a JWT, give it a JWT //If returning JSON return new ModelAndView("jsonUserInfoView", "userInfo", userInfo); - //TODO: If returning JWT? + // If returning JWT + //Jwt jwt = new Jwt(new JwtHeader(), new JwtClaims(userInfo.toJson()), null); + //sign jwt according to client's userinfo_signed_response_algs parameter + //mav.addObject(jwt); + //return mav; } } diff --git a/server/src/main/webapp/WEB-INF/spring/security-context.xml b/server/src/main/webapp/WEB-INF/spring/security-context.xml index dad0e3ec4..cfe1c27e6 100644 --- a/server/src/main/webapp/WEB-INF/spring/security-context.xml +++ b/server/src/main/webapp/WEB-INF/spring/security-context.xml @@ -32,7 +32,7 @@ + authorization-endpoint-url="/openidconnect/auth">