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