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