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