001package com.nimbusds.oauth2.sdk;
002
003
004import java.util.Collections;
005import java.util.HashMap;
006import java.util.Map;
007import java.util.Set;
008
009import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
010import net.jcip.annotations.Immutable;
011
012import net.minidev.json.JSONObject;
013
014import com.nimbusds.oauth2.sdk.token.AccessToken;
015import com.nimbusds.oauth2.sdk.token.RefreshToken;
016import com.nimbusds.oauth2.sdk.token.TokenPair;
017import com.nimbusds.oauth2.sdk.http.CommonContentTypes;
018import com.nimbusds.oauth2.sdk.http.HTTPResponse;
019
020
021/**
022 * Access token response from the Token endpoint.
023 *
024 * <p>Example HTTP response:
025 *
026 * <pre>
027 * HTTP/1.1 200 OK
028 * Content-Type: application/json;charset=UTF-8
029 * Cache-Control: no-store
030 * Pragma: no-cache
031 *
032 * {
033 *   "access_token"      : "2YotnFZFEjr1zCsicMWpAA",
034 *   "token_type"        : "example",
035 *   "expires_in"        : 3600,
036 *   "refresh_token"     : "tGzv3JOkF0XG5Qx2TlKWIA",
037 *   "example_parameter" : "example_value"
038 * }
039 * </pre>
040 *
041 * <p>Related specifications:
042 *
043 * <ul>
044 *     <li>OAuth 2.0 (RFC 6749), sections 4.1.4, 4.3.3,  4.4.3 and 5.1.
045 * </ul>
046 */
047@Immutable
048public class AccessTokenResponse 
049        extends TokenResponse
050        implements SuccessResponse {
051
052
053        /**
054         * The access token.
055         */
056        private final AccessToken accessToken;
057        
058        
059        /**
060         * Optional refresh token.
061         */
062        private final RefreshToken refreshToken;
063
064
065        /**
066         * Optional custom parameters.
067         */
068        private final Map<String,Object> customParams;
069        
070        
071        /**
072         * Creates a new access token response.
073         *
074         * @param accessToken  The access token. Must not be {@code null}.
075         * @param refreshToken Optional refresh token, {@code null} if none.
076         */
077        public AccessTokenResponse(final AccessToken accessToken,
078                                   final RefreshToken refreshToken) {
079                                   
080                this(accessToken, refreshToken, null);
081        }
082
083
084        /**
085         * Creates a new access token response.
086         *
087         * @param accessToken  The access token. Must not be {@code null}.
088         * @param refreshToken Optional refresh token, {@code null} if none.
089         * @param customParams Optional custom parameters, {@code null} if
090         *                     none.
091         */
092        public AccessTokenResponse(final AccessToken accessToken,
093                                   final RefreshToken refreshToken,
094                                   final Map<String,Object> customParams) {
095
096                if (accessToken == null)
097                        throw new IllegalArgumentException("The access token must not be null");
098
099                this.accessToken = accessToken;
100
101                this.refreshToken = refreshToken;
102
103                this.customParams = customParams;
104        }
105
106
107        /**
108         * Creates a new access token response.
109         *
110         * @param tokenPair The access and refresh token pair. Must not be 
111         *                  {@code null}.
112         */
113        public AccessTokenResponse(final TokenPair tokenPair) {
114                                   
115                this(tokenPair, null);
116        }
117
118
119        /**
120         * Creates a new access token response.
121         *
122         * @param tokenPair    The access and refresh token pair. Must not be
123         *                     {@code null}.
124         * @param customParams Optional custom parameters, {@code null} if
125         *                     none.
126         */
127        public AccessTokenResponse(final TokenPair tokenPair,
128                                   final Map<String,Object> customParams) {
129
130                this(tokenPair.getAccessToken(), tokenPair.getRefreshToken(), customParams);
131        }
132
133
134        @Override
135        public boolean indicatesSuccess() {
136
137                return true;
138        }
139        
140        
141        /**
142         * Gets the access token.
143         *
144         * @return The access token.
145         */
146        public AccessToken getAccessToken() {
147        
148                return accessToken;
149        }
150
151
152        /**
153         * Gets the access token as type bearer.
154         *
155         * @return The bearer access token.
156         */
157        public BearerAccessToken getBearerAccessToken() {
158
159                // Cast should be safe, only bearer supported at present
160                return (BearerAccessToken)accessToken;
161        }
162        
163        
164        /**
165         * Gets the optional refresh token.
166         *
167         * @return The refresh token, {@code null} if none.
168         */
169        public RefreshToken getRefreshToken() {
170        
171                return refreshToken;
172        }
173
174
175        /**
176         * Gets the access and refresh token pair.
177         *
178         * @return The access and refresh token pair. Must not be {@code null}.
179         */
180        public TokenPair getTokenPair() {
181
182                return new TokenPair(accessToken, refreshToken);
183        }
184
185
186        /**
187         * Gets the custom parameters.
188         *
189         * @return The custom parameters, as a unmodifiable map, empty map if
190         *         none.
191         */
192        public Map<String,Object> getCustomParams() {
193
194                if (customParams == null)
195                        return Collections.emptyMap();
196
197                return Collections.unmodifiableMap(customParams);
198        }
199        
200        
201        /**
202         * Returns the JSON object representing this access token response.
203         *
204         * <p>Example JSON object:
205         *
206         * <pre>
207         * {
208         *   "access_token" : "SlAV32hkKG",
209         *   "token_type"   : "Bearer",
210         *   "refresh_token": "8xLOxBtZp8",
211         *   "expires_in"   : 3600
212         * }
213         * </pre>
214         *
215         * @return The JSON object.
216         *
217         * @throws SerializeException If this access token response couldn't be
218         *                            serialised to a JSON object.
219         */
220        public JSONObject toJSONObject()
221                throws SerializeException {
222        
223                JSONObject o = accessToken.toJSONObject();
224
225                if (refreshToken != null)
226                        o.putAll(refreshToken.toJSONObject());
227
228                if (customParams != null)
229                        o.putAll(customParams);
230                
231                return o;
232        }
233        
234        
235        @Override
236        public HTTPResponse toHTTPResponse()
237                throws SerializeException {
238        
239                HTTPResponse httpResponse = new HTTPResponse(HTTPResponse.SC_OK);
240                
241                httpResponse.setContentType(CommonContentTypes.APPLICATION_JSON);
242                httpResponse.setCacheControl("no-store");
243                httpResponse.setPragma("no-cache");
244                
245                httpResponse.setContent(toJSONObject().toString());
246                
247                return httpResponse;
248        }
249        
250        
251        /**
252         * Parses an access token response from the specified JSON object.
253         *
254         * @param jsonObject The JSON object to parse. Must not be {@code null}.
255         *
256         * @return The access token response.
257         *
258         * @throws ParseException If the JSON object couldn't be parsed to an
259         *                        access token response.
260         */
261        public static AccessTokenResponse parse(final JSONObject jsonObject)
262                throws ParseException {
263                
264                AccessToken accessToken = AccessToken.parse(jsonObject);
265                
266                RefreshToken refreshToken = RefreshToken.parse(jsonObject);
267
268                // Get the std param names for the access + refresh token
269                Set<String> paramNames = accessToken.getParamNames();
270
271                if (refreshToken != null)
272                        paramNames.addAll(refreshToken.getParamNames());
273
274                // Determine the custom param names
275                Set<String> customParamNames = jsonObject.keySet();
276                customParamNames.removeAll(paramNames);
277
278                Map<String,Object> customParams = null;
279
280                if (customParamNames.size() > 0) {
281
282                        customParams = new HashMap<>();
283
284                        for (String name: customParamNames) {
285                                customParams.put(name, jsonObject.get(name));
286                        }
287                }
288                
289                return new AccessTokenResponse(accessToken, refreshToken, customParams);
290        }
291        
292        
293        /**
294         * Parses an access token response from the specified HTTP response.
295         *
296         * @param httpResponse The HTTP response. Must not be {@code null}.
297         *
298         * @return The access token response.
299         *
300         * @throws ParseException If the HTTP response couldn't be parsed to an 
301         *                        access token response.
302         */
303        public static AccessTokenResponse parse(final HTTPResponse httpResponse)
304                throws ParseException {
305                
306                httpResponse.ensureStatusCode(HTTPResponse.SC_OK);
307                
308                JSONObject jsonObject = httpResponse.getContentAsJSONObject();
309                
310                return parse(jsonObject);
311        }
312}