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