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 com.nimbusds.oauth2.sdk.ParseException;
022import com.nimbusds.oauth2.sdk.Scope;
023import com.nimbusds.oauth2.sdk.http.HTTPRequest;
024import com.nimbusds.oauth2.sdk.rar.AuthorizationDetail;
025import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
026import net.minidev.json.JSONObject;
027
028import java.util.HashSet;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
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 *     <li>OAuth 2.0 Rich Authorization Requests (RFC 9396), section 7.
043 *     <li>OAuth 2.0 Token Exchange (RFC 8693), section 3.
044 * </ul>
045 */
046public abstract class AccessToken extends Token {
047        
048        
049        private static final long serialVersionUID = 2947643641344083799L;
050        
051        
052        /**
053         * The access token type.
054         */
055        private final AccessTokenType type;
056        
057        
058        /**
059         * Optional lifetime, in seconds.
060         */
061        private final long lifetime;
062        
063        
064        /**
065         * Optional scope.
066         */
067        private final Scope scope;
068
069
070        /**
071         * Optional authorisation details.
072         */
073        private final List<AuthorizationDetail> authorizationDetails;
074
075
076        /**
077         * Optional identifier URI for the token type, as defined in OAuth 2.0
078         * Token Exchange (RFC 8693).
079         */
080        private final TokenTypeURI issuedTokenType;
081
082
083        /**
084         * Creates a new minimal access token with a randomly generated 256-bit 
085         * (32-byte) value, Base64URL-encoded. The optional lifetime, scope and
086         * token type URI are left unspecified.
087         *
088         * @param type The access token type. Must not be {@code null}.
089         */
090        public AccessToken(final AccessTokenType type) {
091        
092                this(type, 32);
093        }
094
095
096        /**
097         * Creates a new minimal access token with a randomly generated value 
098         * of the specified byte length, Base64URL-encoded. The optional 
099         * lifetime, scope and token type URI are left unspecified.
100         *
101         * @param type       The access token type. Must not be {@code null}.
102         * @param byteLength The byte length of the value to generate. Must be
103         *                   greater than one.
104         */
105        public AccessToken(final AccessTokenType type, final int byteLength) {
106        
107                this(type, byteLength, 0L, null);
108        }
109
110
111        /**
112         * Creates a new access token with a randomly generated 256-bit 
113         * (32-byte) value, Base64URL-encoded. The optional token type URI is
114         * left unspecified.
115         *
116         * @param type     The access token type. Must not be {@code null}.
117         * @param lifetime The lifetime in seconds, 0 if not specified.
118         * @param scope    The scope, {@code null} if not specified.
119         */
120        public AccessToken(final AccessTokenType type,
121                           final long lifetime, 
122                           final Scope scope) {
123        
124                this(type, 32, lifetime, scope);
125        }
126
127
128        /**
129         * Creates a new access token with a randomly generated value 
130         * of the specified byte length, Base64URL-encoded. The optional token
131         * type URI is left unspecified.
132         *
133         * @param type       The access token type. Must not be {@code null}.
134         * @param byteLength The byte length of the value to generate. Must be
135         *                   greater than one.
136         * @param lifetime   The lifetime in seconds, 0 if not specified.
137         * @param scope      The scope, {@code null} if not specified.
138         */
139        public AccessToken(final AccessTokenType type, 
140                           final int byteLength, 
141                           final long lifetime, 
142                           final Scope scope) {
143        
144                this(type, byteLength, lifetime, scope, null);
145        }
146
147        
148        /**
149         * Creates a new access token with a randomly generated value
150         * of the specified byte length, Base64URL-encoded.
151         *
152         * @param type            The access token type. Must not be
153         *                        {@code null}.
154         * @param byteLength      The byte length of the value to generate.
155         *                        Must be greater than one.
156         * @param lifetime        The lifetime in seconds, 0 if not specified.
157         * @param scope           The scope, {@code null} if not specified.
158         * @param issuedTokenType The token type URI, {@code null} if not
159         *                        specified.
160         */
161        public AccessToken(final AccessTokenType type,
162                           final int byteLength,
163                           final long lifetime,
164                           final Scope scope,
165                           final TokenTypeURI issuedTokenType) {
166
167                this(type, byteLength, lifetime, scope, null, issuedTokenType);
168        }
169
170
171        /**
172         * Creates a new access token with a randomly generated value
173         * of the specified byte length, Base64URL-encoded.
174         *
175         * @param type                 The access token type. Must not be
176         *                             {@code null}.
177         * @param byteLength           The byte length of the value to
178         *                             generate. Must be greater than one.
179         * @param lifetime             The lifetime in seconds, 0 if not
180         *                             specified.
181         * @param scope                The scope, {@code null} if not
182         *                             specified.
183         * @param authorizationDetails The authorisation details, {@code null}
184         *                             if not specified.
185         * @param issuedTokenType      The token type URI, {@code null} if not
186         *                             specified.
187         */
188        public AccessToken(final AccessTokenType type,
189                           final int byteLength,
190                           final long lifetime,
191                           final Scope scope,
192                           final List<AuthorizationDetail> authorizationDetails,
193                           final TokenTypeURI issuedTokenType) {
194
195                super(byteLength);
196
197                if (type == null)
198                        throw new IllegalArgumentException("The access token type must not be null");
199
200                this.type = type;
201
202                this.lifetime = lifetime;
203                this.scope = scope;
204                this.authorizationDetails = authorizationDetails;
205                this.issuedTokenType = issuedTokenType;
206        }
207        
208        
209        /**
210         * Creates a new minimal access token with the specified value. The 
211         * optional lifetime, scope and token type URI are left unspecified.
212         *
213         * @param type  The access token type. Must not be {@code null}.
214         * @param value The access token value. Must not be {@code null} or
215         *              empty string.
216         */
217        public AccessToken(final AccessTokenType type, final String value) {
218        
219                this(type, value, 0L, null);
220        }
221        
222        
223        /**
224         * Creates a new access token with the specified value. The optional
225         * token type URI is left unspecified.
226         *
227         * @param type     The access token type. Must not be {@code null}.
228         * @param value    The access token value. Must not be {@code null} or
229         *                 empty string.
230         * @param lifetime The lifetime in seconds, 0 if not specified.
231         * @param scope    The scope, {@code null} if not specified.
232         */
233        public AccessToken(final AccessTokenType type, 
234                           final String value, 
235                           final long lifetime, 
236                           final Scope scope) {
237                this(type, value, lifetime, scope, null);
238        }
239
240        
241        /**
242         * Creates a new access token with the specified value.
243         *
244         * @param type            The access token type. Must not be
245         *                        {@code null}.
246         * @param value           The access token value. Must not be
247         *                        {@code null} or empty string.
248         * @param lifetime        The lifetime in seconds, 0 if not specified.
249         * @param scope           The scope, {@code null} if not specified.
250         * @param issuedTokenType The token type URI, {@code null} if not
251         *                        specified.
252         */
253        public AccessToken(final AccessTokenType type,
254                           final String value,
255                           final long lifetime,
256                           final Scope scope,
257                           final TokenTypeURI issuedTokenType) {
258                
259                this(type, value, lifetime, scope, null, issuedTokenType);
260        }
261
262
263        /**
264         * Creates a new access token with the specified value.
265         *
266         * @param type            The access token type. Must not be
267         *                        {@code null}.
268         * @param value           The access token value. Must not be
269         *                        {@code null} or empty string.
270         * @param lifetime        The lifetime in seconds, 0 if not specified.
271         * @param scope           The scope, {@code null} if not specified.
272         * @param issuedTokenType The token type URI, {@code null} if not
273         *                        specified.
274         */
275        public AccessToken(final AccessTokenType type,
276                           final String value,
277                           final long lifetime,
278                           final Scope scope,
279                           final List<AuthorizationDetail> authorizationDetails,
280                           final TokenTypeURI issuedTokenType) {
281
282                super(value);
283
284                if (type == null)
285                        throw new IllegalArgumentException("The access token type must not be null");
286
287                this.type = type;
288
289                this.lifetime = lifetime;
290                this.scope = scope;
291                this.authorizationDetails = authorizationDetails;
292                this.issuedTokenType = issuedTokenType;
293        }
294
295
296        /**
297         * Returns the access token type.
298         *
299         * @return The access token type.
300         */
301        public AccessTokenType getType() {
302
303                return type;
304        }
305
306        
307        /**
308         * Returns the lifetime of this access token.
309         *
310         * @return The lifetime in seconds, 0 if not specified.
311         */
312        public long getLifetime() {
313        
314                return lifetime;
315        }
316        
317        
318        /**
319         * Returns the scope of this access token.
320         *
321         * @return The scope, {@code null} if not specified.
322         */
323        public Scope getScope() {
324        
325                return scope;
326        }
327
328
329        /**
330         * Returns the authorisation details for this access token.
331         *
332         * @return The authorisation details, {@code null} if not specified.
333         */
334        public List<AuthorizationDetail> getAuthorizationDetails() {
335
336                return authorizationDetails;
337        }
338
339        
340        /**
341         * Returns the identifier URI for the type of this access token. Used
342         * in OAuth 2.0 Token Exchange (RFC 8693).
343         *
344         * @return The token type URI, {@code null} if not specified.
345         */
346        public TokenTypeURI getIssuedTokenType() {
347                
348                return issuedTokenType;
349        }
350
351        
352        @Override
353        public Set<String> getParameterNames() {
354
355                Set<String> paramNames = new HashSet<>();
356                paramNames.add("access_token");
357                paramNames.add("token_type");
358
359                if (getLifetime() > 0)
360                        paramNames.add("expires_in");
361
362                if (getScope() != null)
363                        paramNames.add("scope");
364
365                if (getAuthorizationDetails() != null)
366                        paramNames.add("authorization_details");
367
368                if (getIssuedTokenType() != null) {
369                        paramNames.add("issued_token_type");
370                }
371
372                return paramNames;
373        }
374
375
376        @Override
377        public JSONObject toJSONObject() {
378
379                JSONObject o = new JSONObject();
380
381                o.put("access_token", getValue());
382                o.put("token_type", type.toString());
383                
384                if (getLifetime() > 0)
385                        o.put("expires_in", lifetime);
386
387                if (getScope() != null)
388                        o.put("scope", scope.toString());
389
390                if (getAuthorizationDetails() != null)
391                        o.put("authorization_details", AuthorizationDetail.toJSONArray(getAuthorizationDetails()));
392
393                if (getIssuedTokenType() != null) {
394                        o.put("issued_token_type", getIssuedTokenType().getURI().toString());
395                }
396                
397                return o;
398        }
399
400
401        @Override
402        public String toJSONString() {
403
404                return toJSONObject().toString();
405        }
406        
407        
408        /**
409         * Returns the {@code Authorization} HTTP request header value for this
410         * access token.
411         *
412         * @return The {@code Authorization} header value.
413         */
414        public abstract String toAuthorizationHeader();
415
416
417        /**
418         * Parses an access token from a JSON object access token response.
419         * Only bearer and DPoP access tokens are supported.
420         *
421         * @param jsonObject The JSON object to parse. Must not be 
422         *                   {@code null}.
423         *
424         * @return The access token.
425         *
426         * @throws ParseException If the JSON object couldn't be parsed to an
427         *                        access token.
428         */
429        public static AccessToken parse(final JSONObject jsonObject)
430                throws ParseException {
431
432                AccessTokenType tokenType = new AccessTokenType(JSONObjectUtils.getString(jsonObject, "token_type"));
433                
434                if (AccessTokenType.BEARER.equals(tokenType)) {
435                        return BearerAccessToken.parse(jsonObject);
436                } else if (AccessTokenType.DPOP.equals(tokenType)) {
437                        return DPoPAccessToken.parse(jsonObject);
438                } else if (AccessTokenType.N_A.equals(tokenType)) {
439                        return NAAccessToken.parse(jsonObject);
440                } else {
441                        throw new ParseException("Unsupported token_type: " + tokenType);
442                }
443        }
444        
445        
446        /**
447         * Parses an {@code Authorization} HTTP request header value for an 
448         * access token. Only bearer access token are supported.
449         *
450         * @param header The {@code Authorization} header value to parse. Must 
451         *               not be {@code null}.
452         *
453         * @return The access token.
454         *
455         * @throws ParseException If the {@code Authorization} header value 
456         *                        couldn't be parsed to an access token.
457         *
458         * @see #parse(String, AccessTokenType)
459         */
460        @Deprecated
461        public static AccessToken parse(final String header)
462                throws ParseException {
463        
464                return BearerAccessToken.parse(header);
465        }
466        
467        
468        /**
469         * Parses an {@code Authorization} HTTP request header value for an
470         * access token. Only bearer and DPoP access token are supported.
471         *
472         * @param header        The {@code Authorization} header value to
473         *                      parse. Must not be {@code null}.
474         * @param preferredType The preferred (primary) access token type.
475         *                      Must be either {@link AccessTokenType#BEARER}
476         *                      or {@link AccessTokenType#DPOP} and not
477         *                      {@code null}.
478         *
479         * @return The access token.
480         *
481         * @throws ParseException If the {@code Authorization} header value
482         *                        couldn't be parsed to an access token.
483         */
484        public static AccessToken parse(final String header,
485                                        final AccessTokenType preferredType)
486                throws ParseException {
487        
488                if (! AccessTokenType.BEARER.equals(preferredType) && ! AccessTokenType.DPOP.equals(preferredType)) {
489                        throw new IllegalArgumentException("Unsupported Authorization scheme: " + preferredType);
490                }
491                
492                if (header != null && header.startsWith(AccessTokenType.BEARER.getValue()) || AccessTokenType.BEARER.equals(preferredType)) {
493                        return BearerAccessToken.parse(header);
494                } else {
495                        return DPoPAccessToken.parse(header);
496                }
497        }
498        
499        
500        /**
501         * Parses an HTTP request header value for an access token.
502         *
503         * @param request The HTTP request to parse. Must not be {@code null}.
504         *
505         * @return The access token.
506         *
507         * @throws ParseException If an access token wasn't found in the HTTP
508         *                        request.
509         */
510        public static AccessToken parse(final HTTPRequest request)
511                throws ParseException {
512                
513                if (request.getAuthorization() != null) {
514                        
515                        AccessTokenType tokenType = AccessTokenUtils.determineAccessTokenTypeFromAuthorizationHeader(request.getAuthorization());
516                        
517                        if (AccessTokenType.BEARER.equals(tokenType)) {
518                                return BearerAccessToken.parse(request.getAuthorization());
519                        }
520                        
521                        if (AccessTokenType.DPOP.equals(tokenType)) {
522                                return DPoPAccessToken.parse(request.getAuthorization());
523                        }
524                        
525                        throw new ParseException("Couldn't determine access token type from Authorization header");
526                }
527                
528                // Try alternative token locations, form and query string are
529                // parameters are not differentiated here
530                Map<String, List<String>> params = request.getQueryParameters();
531                return new TypelessAccessToken(AccessTokenUtils.parseValueFromQueryParameters(params));
532        }
533}