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