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