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 150 HTTPResponse httpResponse = new HTTPResponse(HTTPResponse.SC_OK); 151 152 httpResponse.setContentType(getContentType()); 153 154 String content; 155 156 if (claimsSet != null) { 157 158 content = claimsSet.toJSONObject().toString(); 159 160 } else { 161 162 try { 163 content = jwt.serialize(); 164 165 } catch (IllegalStateException e) { 166 167 throw new SerializeException("Couldn't serialize UserInfo claims JWT: " + 168 e.getMessage(), e); 169 } 170 } 171 172 httpResponse.setContent(content); 173 174 return httpResponse; 175 } 176 177 178 /** 179 * Parses a UserInfo response from the specified HTTP response. 180 * 181 * <p>Example HTTP response: 182 * 183 * <pre> 184 * HTTP/1.1 200 OK 185 * Content-Type: application/json 186 * 187 * { 188 * "sub" : "248289761001", 189 * "name" : "Jane Doe" 190 * "given_name" : "Jane", 191 * "family_name" : "Doe", 192 * "email" : "[email protected]", 193 * "picture" : "http://example.com/janedoe/me.jpg" 194 * } 195 * </pre> 196 * 197 * @param httpResponse The HTTP response. Must not be {@code null}. 198 * 199 * @return The UserInfo response. 200 * 201 * @throws ParseException If the HTTP response couldn't be parsed to a 202 * UserInfo response. 203 */ 204 public static UserInfoSuccessResponse parse(final HTTPResponse httpResponse) 205 throws ParseException { 206 207 httpResponse.ensureStatusCode(HTTPResponse.SC_OK); 208 209 httpResponse.ensureContentType(); 210 211 ContentType ct = httpResponse.getContentType(); 212 213 214 UserInfoSuccessResponse response; 215 216 if (ct.match(CommonContentTypes.APPLICATION_JSON)) { 217 218 UserInfo claimsSet; 219 220 try { 221 claimsSet = new UserInfo(httpResponse.getContentAsJSONObject()); 222 223 } catch (Exception e) { 224 225 throw new ParseException("Couldn't parse UserInfo claims: " + 226 e.getMessage(), e); 227 } 228 229 response = new UserInfoSuccessResponse(claimsSet); 230 } 231 else if (ct.match(CommonContentTypes.APPLICATION_JWT)) { 232 233 JWT jwt; 234 235 try { 236 jwt = httpResponse.getContentAsJWT(); 237 238 } catch (ParseException e) { 239 240 throw new ParseException("Couldn't parse UserInfo claims JWT: " + 241 e.getMessage(), e); 242 } 243 244 response = new UserInfoSuccessResponse(jwt); 245 } 246 else { 247 throw new ParseException("Unexpected Content-Type, must be " + 248 CommonContentTypes.APPLICATION_JSON + 249 " or " + 250 CommonContentTypes.APPLICATION_JWT); 251 } 252 253 return response; 254 } 255}