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