From 078bf5e464f0cd4472d2fd44e16cc75bdca57cd7 Mon Sep 17 00:00:00 2001
From: Justin Richer <jricher@mit.edu>
Date: Sat, 28 Jun 2014 23:44:37 -0400
Subject: [PATCH] combine HTTP content negotiation with client preferences for
 user info endpoint

---
 .../openid/connect/view/UserInfoJwtView.java  |  4 +-
 .../openid/connect/web/UserInfoEndpoint.java  | 40 ++++++++++++++-----
 2 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJwtView.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJwtView.java
index 5664838d5..ddb8e84d5 100644
--- a/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJwtView.java
+++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/view/UserInfoJwtView.java
@@ -120,9 +120,9 @@ public class UserInfoJwtView extends UserInfoView {
 				}
 			} else {
 
-				JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm();
+				JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm(); // default to the server's preference
 				if (client.getUserInfoSignedResponseAlg() != null) {
-					signingAlg = client.getUserInfoSignedResponseAlg();
+					signingAlg = client.getUserInfoSignedResponseAlg(); // override with the client's preference if available
 				}
 
 				SignedJWT signed = new SignedJWT(new JWSHeader(signingAlg), claims);
diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java
index 5972ec1a9..54b5ffe01 100644
--- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java
+++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/UserInfoEndpoint.java
@@ -55,6 +55,8 @@ public class UserInfoEndpoint {
 
 	private static Logger logger = LoggerFactory.getLogger(UserInfoEndpoint.class);
 
+	private static final MediaType JOSE_MEDIA_TYPE = new MediaType("application", "jwt");
+
 	/**
 	 * Get information about the user as specified in the accessToken included in this request
 	 */
@@ -90,21 +92,37 @@ public class UserInfoEndpoint {
 		model.addAttribute("userInfo", userInfo);
 
 		// content negotiation
+
+		// start off by seeing if the client has registered for a signed/encrypted JWT from here
+		ClientDetailsEntity client = clientService.loadClientByClientId(auth.getOAuth2Request().getClientId());
+		model.addAttribute("client", client);
+		
 		List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader);
 		MediaType.sortBySpecificityAndQuality(mediaTypes);
-
-		MediaType jose = new MediaType("application", "jwt");
-
-		for (MediaType m : mediaTypes) {
-			if (!m.isWildcardType() && m.isCompatibleWith(jose)) {
-				ClientDetailsEntity client = clientService.loadClientByClientId(auth.getOAuth2Request().getClientId());
-				model.addAttribute("client", client);
-
-				return "userInfoJwtView";
+		
+		if (client.getUserInfoSignedResponseAlg() != null 
+				|| client.getUserInfoEncryptedResponseAlg() != null
+				|| client.getUserInfoEncryptedResponseEnc() != null) {
+			// client has a preference, see if they ask for plain JSON specifically on this request
+			for (MediaType m : mediaTypes) {
+				if (!m.isWildcardType() && m.isCompatibleWith(MediaType.APPLICATION_JSON)) {
+					return "userInfoView";
+				}
+			}
+			
+			// otherwise return JWT
+			return "userInfoJwtView";
+		} else {
+			// client has no preference, see if they asked for JWT specifically on this request
+			for (MediaType m : mediaTypes) {
+				if (!m.isWildcardType() && m.isCompatibleWith(JOSE_MEDIA_TYPE)) {
+					return "userInfoJwtView";
+				}
 			}
-		}
 
-		return "userInfoView";
+			// otherwise return JSON
+			return "userInfoView";
+		}
 
 	}