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.token;
019
020
021import java.util.HashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import net.minidev.json.JSONObject;
027
028import com.nimbusds.oauth2.sdk.ParseException;
029import com.nimbusds.oauth2.sdk.Scope;
030import com.nimbusds.oauth2.sdk.http.HTTPRequest;
031import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
032
033
034/**
035 * The base abstract class for access tokens. Concrete extending classes should
036 * be immutable.
037 *
038 * <p>Related specifications:
039 *
040 * <ul>
041 *     <li>OAuth 2.0 (RFC 6749), sections 1.4 and 5.1.
042 * </ul>
043 */
044public abstract class AccessToken extends Token {
045        
046        
047        private static final long serialVersionUID = 2947643641344083799L;
048        
049        
050        /**
051         * The access token type.
052         */
053        private final AccessTokenType type;
054        
055        
056        /**
057         * Optional lifetime, in seconds.
058         */
059        private final long lifetime;
060        
061        
062        /**
063         * Optional scope.
064         */
065        private final Scope scope;
066
067
068        /**
069         * Creates a new minimal access token with a randomly generated 256-bit 
070         * (32-byte) value, Base64URL-encoded. The optional lifetime and scope 
071         * are left undefined.
072         *
073         * @param type The access token type. Must not be {@code null}.
074         */
075        public AccessToken(final AccessTokenType type) {
076        
077                this(type, 32);
078        }
079
080
081        /**
082         * Creates a new minimal access token with a randomly generated value 
083         * of the specified byte length, Base64URL-encoded. The optional 
084         * lifetime and scope are left undefined.
085         *
086         * @param type       The access token type. Must not be {@code null}.
087         * @param byteLength The byte length of the value to generate. Must be
088         *                   greater than one.
089         */
090        public AccessToken(final AccessTokenType type, final int byteLength) {
091        
092                this(type, byteLength, 0L, null);
093        }
094
095
096        /**
097         * Creates a new access token with a randomly generated 256-bit 
098         * (32-byte) value, Base64URL-encoded.
099         *
100         * @param type     The access token type. Must not be {@code null}.
101         * @param lifetime The lifetime in seconds, 0 if not specified.
102         * @param scope    The scope, {@code null} if not specified.
103         */
104        public AccessToken(final AccessTokenType type,
105                           final long lifetime, 
106                           final Scope scope) {
107        
108                this(type, 32, lifetime, scope);
109        }
110
111
112        /**
113         * Creates a new access token with a randomly generated value 
114         * of the specified byte length, Base64URL-encoded, and optional 
115         * lifetime and scope.
116         *
117         * @param type       The access token type. Must not be {@code null}.
118         * @param byteLength The byte length of the value to generate. Must be
119         *                   greater than one.
120         * @param lifetime   The lifetime in seconds, 0 if not specified.
121         * @param scope      The scope, {@code null} if not specified.
122         */
123        public AccessToken(final AccessTokenType type, 
124                           final int byteLength, 
125                           final long lifetime, 
126                           final Scope scope) {
127        
128                super(byteLength);
129
130                if (type == null)
131                        throw new IllegalArgumentException("The access token type must not be null");
132
133                this.type = type;
134
135                this.lifetime = lifetime;
136                this.scope = scope;
137        }
138        
139        
140        /**
141         * Creates a new minimal access token with the specified value. The 
142         * optional lifetime and scope are left undefined.
143         *
144         * @param type  The access token type. Must not be {@code null}.
145         * @param value The access token value. Must not be {@code null} or
146         *              empty string.
147         */
148        public AccessToken(final AccessTokenType type, final String value) {
149        
150                this(type, value, 0L, null);
151        }
152        
153        
154        /**
155         * Creates a new access token with the specified value and optional 
156         * lifetime and scope.
157         *
158         * @param type     The access token type. Must not be {@code null}.
159         * @param value    The access token value. Must not be {@code null} or
160         *                 empty string.
161         * @param lifetime The lifetime in seconds, 0 if not specified.
162         * @param scope    The scope, {@code null} if not specified.
163         */
164        public AccessToken(final AccessTokenType type, 
165                           final String value, 
166                           final long lifetime, 
167                           final Scope scope) {
168        
169                super(value);
170
171                if (type == null)
172                        throw new IllegalArgumentException("The access token type must not be null");
173
174                this.type = type;
175
176                this.lifetime = lifetime;
177                this.scope = scope;
178        }
179
180
181        /**
182         * Returns the access token type.
183         *
184         * @return The access token type.
185         */
186        public AccessTokenType getType() {
187
188                return type;
189        }
190
191        
192        /**
193         * Returns the lifetime of this access token.
194         *
195         * @return The lifetime in seconds, 0 if not specified.
196         */
197        public long getLifetime() {
198        
199                return lifetime;
200        }
201        
202        
203        /**
204         * Returns the scope of this access token.
205         *
206         * @return The scope, {@code null} if not specified.
207         */
208        public Scope getScope() {
209        
210                return scope;
211        }
212
213
214        @Override
215        public Set<String> getParameterNames() {
216
217                Set<String> paramNames = new HashSet<>();
218                paramNames.add("access_token");
219                paramNames.add("token_type");
220
221                if (getLifetime() > 0)
222                        paramNames.add("expires_in");
223
224                if (getScope() != null)
225                        paramNames.add("scope");
226
227                return paramNames;
228        }
229
230
231        @Override
232        public JSONObject toJSONObject() {
233
234                JSONObject o = new JSONObject();
235
236                o.put("access_token", getValue());
237                o.put("token_type", type.toString());
238                
239                if (getLifetime() > 0)
240                        o.put("expires_in", lifetime);
241
242                if (getScope() != null)
243                        o.put("scope", scope.toString());
244                
245                return o;
246        }
247
248
249        @Override
250        public String toJSONString() {
251
252                return toJSONObject().toString();
253        }
254        
255        
256        /**
257         * Returns the {@code Authorization} HTTP request header value for this
258         * access token.
259         *
260         * @return The {@code Authorization} header value.
261         */
262        public abstract String toAuthorizationHeader();
263
264
265        /**
266         * Parses an access token from a JSON object access token response.
267         * Only bearer and DPoP access tokens are supported.
268         *
269         * @param jsonObject The JSON object to parse. Must not be 
270         *                   {@code null}.
271         *
272         * @return The access token.
273         *
274         * @throws ParseException If the JSON object couldn't be parsed to an
275         *                        access token.
276         */
277        public static AccessToken parse(final JSONObject jsonObject)
278                throws ParseException {
279
280                AccessTokenType tokenType = new AccessTokenType(JSONObjectUtils.getString(jsonObject, "token_type"));
281                
282                if (AccessTokenType.BEARER.equals(tokenType)) {
283                        return BearerAccessToken.parse(jsonObject);
284                } else if (AccessTokenType.DPOP.equals(tokenType)){
285                        return DPoPAccessToken.parse(jsonObject);
286                } else {
287                        throw new ParseException("Unsupported token_type: " + tokenType);
288                }
289        }
290        
291        
292        /**
293         * Parses an {@code Authorization} HTTP request header value for an 
294         * access token. Only bearer access token are supported.
295         *
296         * @param header The {@code Authorization} header value to parse. Must 
297         *               not be {@code null}.
298         *
299         * @return The access token.
300         *
301         * @throws ParseException If the {@code Authorization} header value 
302         *                        couldn't be parsed to an access token.
303         *
304         * @see #parse(String, AccessTokenType)
305         */
306        @Deprecated
307        public static AccessToken parse(final String header)
308                throws ParseException {
309        
310                return BearerAccessToken.parse(header);
311        }
312        
313        
314        /**
315         * Parses an {@code Authorization} HTTP request header value for an
316         * access token. Only bearer and DPoP access token are supported.
317         *
318         * @param header        The {@code Authorization} header value to
319         *                      parse. Must not be {@code null}.
320         * @param preferredType The preferred (primary) access token type.
321         *                      Must be either {@link AccessTokenType#BEARER}
322         *                      or {@link AccessTokenType#DPOP} and not
323         *                      {@code null}.
324         *
325         * @return The access token.
326         *
327         * @throws ParseException If the {@code Authorization} header value
328         *                        couldn't be parsed to an access token.
329         */
330        public static AccessToken parse(final String header,
331                                        final AccessTokenType preferredType)
332                throws ParseException {
333        
334                if (! AccessTokenType.BEARER.equals(preferredType) && ! AccessTokenType.DPOP.equals(preferredType)) {
335                        throw new IllegalArgumentException("Unsupported Authorization scheme: " + preferredType);
336                }
337                
338                if (header != null && header.startsWith(AccessTokenType.BEARER.getValue()) || AccessTokenType.BEARER.equals(preferredType)) {
339                        return BearerAccessToken.parse(header);
340                } else {
341                        return DPoPAccessToken.parse(header);
342                }
343        }
344        
345        
346        /**
347         * Parses an HTTP request header value for an access token.
348         *
349         * @param request The HTTP request to parse. Must not be {@code null}.
350         *
351         * @return The access token.
352         *
353         * @throws ParseException If an access token wasn't found in the HTTP
354         *                        request.
355         */
356        public static AccessToken parse(final HTTPRequest request)
357                throws ParseException {
358                
359                if (request.getAuthorization() != null) {
360                        
361                        AccessTokenType tokenType = AccessTokenUtils.determineAccessTokenTypeFromAuthorizationHeader(request.getAuthorization());
362                        
363                        if (AccessTokenType.BEARER.equals(tokenType)) {
364                                return BearerAccessToken.parse(request.getAuthorization());
365                        }
366                        
367                        if (AccessTokenType.DPOP.equals(tokenType)) {
368                                return DPoPAccessToken.parse(request.getAuthorization());
369                        }
370                        
371                        throw new ParseException("Couldn't determine access token type from Authorization header");
372                }
373                
374                // Try alternative token locations, form and query string are
375                // parameters are not differentiated here
376                Map<String, List<String>> params = request.getQueryParameters();
377                return new TypelessAccessToken(AccessTokenUtils.parseValueFromQueryParameters(params));
378        }
379}