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