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.oauth2.sdk;
019
020
021import java.util.*;
022
023import net.jcip.annotations.Immutable;
024import net.minidev.json.JSONObject;
025
026import com.nimbusds.common.contenttype.ContentType;
027import com.nimbusds.oauth2.sdk.http.HTTPResponse;
028import com.nimbusds.oauth2.sdk.token.Tokens;
029
030
031/**
032 * Access token response from the Token endpoint.
033 *
034 * <p>Example HTTP response:
035 *
036 * <pre>
037 * HTTP/1.1 200 OK
038 * Content-Type: application/json;charset=UTF-8
039 * Cache-Control: no-store
040 * Pragma: no-cache
041 *
042 * {
043 *   "access_token"      : "2YotnFZFEjr1zCsicMWpAA",
044 *   "token_type"        : "example",
045 *   "expires_in"        : 3600,
046 *   "refresh_token"     : "tGzv3JOkF0XG5Qx2TlKWIA",
047 *   "example_parameter" : "example_value"
048 * }
049 * </pre>
050 *
051 * <p>Related specifications:
052 *
053 * <ul>
054 *     <li>OAuth 2.0 (RFC 6749), sections 4.1.4, 4.3.3,  4.4.3 and 5.1.
055 * </ul>
056 */
057@Immutable
058public class AccessTokenResponse extends TokenResponse implements SuccessResponse {
059
060
061        /**
062         * The tokens.
063         */
064        private final Tokens tokens;
065
066
067        /**
068         * Optional custom parameters.
069         */
070        private final Map<String,Object> customParams;
071
072
073        /**
074         * Creates a new access token response.
075         *
076         * @param tokens The tokens. Must not be {@code null}.
077         */
078        public AccessTokenResponse(final Tokens tokens) {
079
080                this(tokens, null);
081        }
082
083
084        /**
085         * Creates a new access token response.
086         *
087         * @param tokens       The tokens. Must not be {@code null}.
088         * @param customParams Optional custom parameters, {@code null} if
089         *                     none.
090         */
091        public AccessTokenResponse(final Tokens tokens,
092                                   final Map<String,Object> customParams) {
093
094                if (tokens == null)
095                        throw new IllegalArgumentException("The tokens must not be null");
096
097                this.tokens = tokens;
098
099                this.customParams = customParams;
100        }
101
102
103        @Override
104        public boolean indicatesSuccess() {
105
106                return true;
107        }
108
109
110        /**
111         * Returns the tokens.
112         *
113         * @return The tokens.
114         */
115        public Tokens getTokens() {
116
117                return tokens;
118        }
119
120
121        /**
122         * Returns the custom parameters.
123         *
124         * @return The custom parameters, as a unmodifiable map, empty map if
125         *         none.
126         */
127        public Map<String,Object> getCustomParameters() {
128
129                if (customParams == null)
130                        return Collections.emptyMap();
131
132                return Collections.unmodifiableMap(customParams);
133        }
134
135
136        @Deprecated
137        public Map<String,Object> getCustomParams() {
138
139                return getCustomParameters();
140        }
141        
142        
143        /**
144         * Returns a JSON object representation of this access token response.
145         *
146         * <p>Example JSON object:
147         *
148         * <pre>
149         * {
150         *   "access_token"  : "SlAV32hkKG",
151         *   "token_type"    : "Bearer",
152         *   "refresh_token" : "8xLOxBtZp8",
153         *   "expires_in"    : 3600
154         * }
155         * </pre>
156         *
157         * @return The JSON object.
158         */
159        public JSONObject toJSONObject() {
160        
161                JSONObject o = tokens.toJSONObject();
162
163                if (customParams != null)
164                        o.putAll(customParams);
165                
166                return o;
167        }
168        
169        
170        @Override
171        public HTTPResponse toHTTPResponse() {
172        
173                HTTPResponse httpResponse = new HTTPResponse(HTTPResponse.SC_OK);
174                
175                httpResponse.setEntityContentType(ContentType.APPLICATION_JSON);
176                httpResponse.setCacheControl("no-store");
177                httpResponse.setPragma("no-cache");
178                
179                httpResponse.setContent(toJSONObject().toString());
180                
181                return httpResponse;
182        }
183        
184        
185        /**
186         * Parses an access token response from the specified JSON object.
187         *
188         * @param jsonObject The JSON object to parse. Must not be {@code null}.
189         *
190         * @return The access token response.
191         *
192         * @throws ParseException If the JSON object couldn't be parsed to an
193         *                        access token response.
194         */
195        public static AccessTokenResponse parse(final JSONObject jsonObject)
196                throws ParseException {
197                
198                Tokens tokens = Tokens.parse(jsonObject);
199
200                // Determine the custom param names
201                Set<String> customParamNames = new HashSet<>(jsonObject.keySet());
202                customParamNames.removeAll(tokens.getParameterNames());
203
204                Map<String,Object> customParams = null;
205
206                if (! customParamNames.isEmpty()) {
207
208                        customParams = new HashMap<>();
209
210                        for (String name: customParamNames) {
211                                customParams.put(name, jsonObject.get(name));
212                        }
213                }
214                
215                return new AccessTokenResponse(tokens, customParams);
216        }
217        
218        
219        /**
220         * Parses an access token response from the specified HTTP response.
221         *
222         * @param httpResponse The HTTP response. Must not be {@code null}.
223         *
224         * @return The access token response.
225         *
226         * @throws ParseException If the HTTP response couldn't be parsed to an 
227         *                        access token response.
228         */
229        public static AccessTokenResponse parse(final HTTPResponse httpResponse)
230                throws ParseException {
231                
232                httpResponse.ensureStatusCode(HTTPResponse.SC_OK);
233                JSONObject jsonObject = httpResponse.getContentAsJSONObject();
234                return parse(jsonObject);
235        }
236}