001package com.nimbusds.openid.connect.sdk; 002 003 004import javax.mail.internet.ContentType; 005 006import net.jcip.annotations.Immutable; 007 008import com.nimbusds.jwt.JWT; 009 010import com.nimbusds.oauth2.sdk.ParseException; 011import com.nimbusds.oauth2.sdk.SerializeException; 012import com.nimbusds.oauth2.sdk.SuccessResponse; 013import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 014import com.nimbusds.oauth2.sdk.http.HTTPResponse; 015 016import com.nimbusds.openid.connect.sdk.claims.UserInfo; 017 018 019/** 020 * UserInfo success response. 021 * 022 * <p>The UserInfo claims may be passed as an unprotected JSON object or as a 023 * plain, signed or encrypted JSON Web Token (JWT). Use the appropriate 024 * constructor for that. 025 * 026 * <p>Example UserInfo HTTP response: 027 * 028 * <pre> 029 * HTTP/1.1 200 OK 030 * Content-Type: application/json 031 * 032 * { 033 * "sub" : "248289761001", 034 * "name" : "Jane Doe" 035 * "given_name" : "Jane", 036 * "family_name" : "Doe", 037 * "email" : "[email protected]", 038 * "picture" : "http://example.com/janedoe/me.jpg" 039 * } 040 * </pre> 041 * 042 * <p>Related specifications: 043 * 044 * <ul> 045 * <li>OpenID Connect Core 1.0, section 5.3.2. 046 * </ul> 047 */ 048@Immutable 049public class UserInfoSuccessResponse 050 extends UserInfoResponse 051 implements SuccessResponse { 052 053 054 /** 055 * The UserInfo claims set, serialisable to a JSON object. 056 */ 057 private final UserInfo claimsSet; 058 059 060 /** 061 * The UserInfo claims set, as plain, signed or encrypted JWT. 062 */ 063 private final JWT jwt; 064 065 066 /** 067 * Creates a new UserInfo success response where the claims are 068 * specified as an unprotected UserInfo claims set. 069 * 070 * @param claimsSet The UserInfo claims set. Must not be {@code null}. 071 */ 072 public UserInfoSuccessResponse(final UserInfo claimsSet) { 073 074 if (claimsSet == null) 075 throw new IllegalArgumentException("The claims must not be null"); 076 077 this.claimsSet = claimsSet; 078 079 this.jwt = null; 080 } 081 082 083 /** 084 * Creates a new UserInfo success response where the claims are 085 * specified as a plain, signed or encrypted JSON Web Token (JWT). 086 * 087 * @param jwt The UserInfo claims set. Must not be {@code null}. 088 */ 089 public UserInfoSuccessResponse(final JWT jwt) { 090 091 if (jwt == null) 092 throw new IllegalArgumentException("The claims JWT must not be null"); 093 094 this.jwt = jwt; 095 096 this.claimsSet = null; 097 } 098 099 100 @Override 101 public boolean indicatesSuccess() { 102 103 return true; 104 } 105 106 107 /** 108 * Gets the content type of this UserInfo response. 109 * 110 * @return The content type, according to the claims format. 111 */ 112 public ContentType getContentType() { 113 114 if (claimsSet != null) 115 return CommonContentTypes.APPLICATION_JSON; 116 else 117 return CommonContentTypes.APPLICATION_JWT; 118 } 119 120 121 /** 122 * Gets the UserInfo claims set as an unprotected UserInfo claims set. 123 * 124 * @return The UserInfo claims set, {@code null} if it was specified as 125 * JSON Web Token (JWT) instead. 126 */ 127 public UserInfo getUserInfo() { 128 129 return claimsSet; 130 } 131 132 133 /** 134 * Gets the UserInfo claims set as a plain, signed or encrypted JSON 135 * Web Token (JWT). 136 * 137 * @return The UserInfo claims set as a JSON Web Token (JWT), 138 * {@code null} if it was specified as an unprotected UserInfo 139 * claims set instead. 140 */ 141 public JWT getUserInfoJWT() { 142 143 return jwt; 144 } 145 146 147 @Override 148 public HTTPResponse toHTTPResponse() 149 throws SerializeException { 150 151 HTTPResponse httpResponse = new HTTPResponse(HTTPResponse.SC_OK); 152 153 httpResponse.setContentType(getContentType()); 154 155 String content; 156 157 if (claimsSet != null) { 158 159 content = claimsSet.toJSONObject().toString(); 160 161 } else { 162 163 try { 164 content = jwt.serialize(); 165 166 } catch (IllegalStateException e) { 167 168 throw new SerializeException("Couldn't serialize UserInfo claims JWT: " + 169 e.getMessage(), e); 170 } 171 } 172 173 httpResponse.setContent(content); 174 175 return httpResponse; 176 } 177 178 179 /** 180 * Parses a UserInfo response from the specified HTTP response. 181 * 182 * <p>Example HTTP response: 183 * 184 * <pre> 185 * HTTP/1.1 200 OK 186 * Content-Type: application/json 187 * 188 * { 189 * "sub" : "248289761001", 190 * "name" : "Jane Doe" 191 * "given_name" : "Jane", 192 * "family_name" : "Doe", 193 * "email" : "[email protected]", 194 * "picture" : "http://example.com/janedoe/me.jpg" 195 * } 196 * </pre> 197 * 198 * @param httpResponse The HTTP response. Must not be {@code null}. 199 * 200 * @return The UserInfo response. 201 * 202 * @throws ParseException If the HTTP response couldn't be parsed to a 203 * UserInfo response. 204 */ 205 public static UserInfoSuccessResponse parse(final HTTPResponse httpResponse) 206 throws ParseException { 207 208 httpResponse.ensureStatusCode(HTTPResponse.SC_OK); 209 210 httpResponse.ensureContentType(); 211 212 ContentType ct = httpResponse.getContentType(); 213 214 215 UserInfoSuccessResponse response; 216 217 if (ct.match(CommonContentTypes.APPLICATION_JSON)) { 218 219 UserInfo claimsSet; 220 221 try { 222 claimsSet = new UserInfo(httpResponse.getContentAsJSONObject()); 223 224 } catch (Exception e) { 225 226 throw new ParseException("Couldn't parse UserInfo claims: " + 227 e.getMessage(), e); 228 } 229 230 response = new UserInfoSuccessResponse(claimsSet); 231 } 232 else if (ct.match(CommonContentTypes.APPLICATION_JWT)) { 233 234 JWT jwt; 235 236 try { 237 jwt = httpResponse.getContentAsJWT(); 238 239 } catch (ParseException e) { 240 241 throw new ParseException("Couldn't parse UserInfo claims JWT: " + 242 e.getMessage(), e); 243 } 244 245 response = new UserInfoSuccessResponse(jwt); 246 } 247 else { 248 throw new ParseException("Unexpected Content-Type, must be " + 249 CommonContentTypes.APPLICATION_JSON + 250 " or " + 251 CommonContentTypes.APPLICATION_JWT); 252 } 253 254 return response; 255 } 256}