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
271         *                             specified.
272         * @param scope                The scope, {@code null} if not
273         *                             specified.
274         * @param authorizationDetails The authorisation details, {@code null}
275         *                             if not specified.
276         * @param issuedTokenType      The token type URI, {@code null} if not
277         *                             specified.
278         */
279        public AccessToken(final AccessTokenType type,
280                           final String value,
281                           final long lifetime,
282                           final Scope scope,
283                           final List<AuthorizationDetail> authorizationDetails,
284                           final TokenTypeURI issuedTokenType) {
285
286                super(value);
287
288                if (type == null)
289                        throw new IllegalArgumentException("The access token type must not be null");
290
291                this.type = type;
292
293                this.lifetime = lifetime;
294                this.scope = scope;
295                this.authorizationDetails = authorizationDetails;
296                this.issuedTokenType = issuedTokenType;
297        }
298
299
300        /**
301         * Returns the access token type.
302         *
303         * @return The access token type.
304         */
305        public AccessTokenType getType() {
306
307                return type;
308        }
309
310        
311        /**
312         * Returns the lifetime of this access token.
313         *
314         * @return The lifetime in seconds, 0 if not specified.
315         */
316        public long getLifetime() {
317        
318                return lifetime;
319        }
320        
321        
322        /**
323         * Returns the scope of this access token.
324         *
325         * @return The scope, {@code null} if not specified.
326         */
327        public Scope getScope() {
328        
329                return scope;
330        }
331
332
333        /**
334         * Returns the authorisation details for this access token.
335         *
336         * @return The authorisation details, {@code null} if not specified.
337         */
338        public List<AuthorizationDetail> getAuthorizationDetails() {
339
340                return authorizationDetails;
341        }
342
343        
344        /**
345         * Returns the identifier URI for the type of this access token. Used
346         * in OAuth 2.0 Token Exchange (RFC 8693).
347         *
348         * @return The token type URI, {@code null} if not specified.
349         */
350        public TokenTypeURI getIssuedTokenType() {
351                
352                return issuedTokenType;
353        }
354
355        
356        @Override
357        public Set<String> getParameterNames() {
358
359                Set<String> paramNames = new HashSet<>();
360                paramNames.add("access_token");
361                paramNames.add("token_type");
362
363                if (getLifetime() > 0)
364                        paramNames.add("expires_in");
365
366                if (getScope() != null)
367                        paramNames.add("scope");
368
369                if (getAuthorizationDetails() != null)
370                        paramNames.add("authorization_details");
371
372                if (getIssuedTokenType() != null) {
373                        paramNames.add("issued_token_type");
374                }
375
376                return paramNames;
377        }
378
379
380        @Override
381        public JSONObject toJSONObject() {
382
383                JSONObject o = new JSONObject();
384
385                o.put("access_token", getValue());
386                o.put("token_type", type.toString());
387                
388                if (getLifetime() > 0)
389                        o.put("expires_in", lifetime);
390
391                if (getScope() != null)
392                        o.put("scope", scope.toString());
393
394                if (getAuthorizationDetails() != null)
395                        o.put("authorization_details", AuthorizationDetail.toJSONArray(getAuthorizationDetails()));
396
397                if (getIssuedTokenType() != null) {
398                        o.put("issued_token_type", getIssuedTokenType().getURI().toString());
399                }
400                
401                return o;
402        }
403
404
405        @Override
406        public String toJSONString() {
407
408                return toJSONObject().toString();
409        }
410        
411        
412        /**
413         * Returns the {@code Authorization} HTTP request header value for this
414         * access token.
415         *
416         * @return The {@code Authorization} header value.
417         */
418        public abstract String toAuthorizationHeader();
419
420
421        /**
422         * Parses an access token from a JSON object access token response.
423         * Only bearer and DPoP access tokens are supported.
424         *
425         * @param jsonObject The JSON object to parse. Must not be 
426         *                   {@code null}.
427         *
428         * @return The access token.
429         *
430         * @throws ParseException If the JSON object couldn't be parsed to an
431         *                        access token.
432         */
433        public static AccessToken parse(final JSONObject jsonObject)
434                throws ParseException {
435
436                AccessTokenType tokenType = new AccessTokenType(JSONObjectUtils.getString(jsonObject, "token_type"));
437                
438                if (AccessTokenType.BEARER.equals(tokenType)) {
439                        return BearerAccessToken.parse(jsonObject);
440                } else if (AccessTokenType.DPOP.equals(tokenType)) {
441                        return DPoPAccessToken.parse(jsonObject);
442                } else if (AccessTokenType.N_A.equals(tokenType)) {
443                        return NAAccessToken.parse(jsonObject);
444                } else {
445                        throw new ParseException("Unsupported token_type: " + tokenType);
446                }
447        }
448        
449        
450        /**
451         * Parses an {@code Authorization} HTTP request header value for an 
452         * access token. Only bearer access token are supported.
453         *
454         * @param header The {@code Authorization} header value to parse. Must 
455         *               not be {@code null}.
456         *
457         * @return The access token.
458         *
459         * @throws ParseException If the {@code Authorization} header value 
460         *                        couldn't be parsed to an access token.
461         *
462         * @see #parse(String, AccessTokenType)
463         */
464        @Deprecated
465        public static AccessToken parse(final String header)
466                throws ParseException {
467        
468                return BearerAccessToken.parse(header);
469        }
470        
471        
472        /**
473         * Parses an {@code Authorization} HTTP request header value for an
474         * access token. Only bearer and DPoP access token are supported.
475         *
476         * @param header        The {@code Authorization} header value to
477         *                      parse. Must not be {@code null}.
478         * @param preferredType The preferred (primary) access token type.
479         *                      Must be either {@link AccessTokenType#BEARER}
480         *                      or {@link AccessTokenType#DPOP} and not
481         *                      {@code null}.
482         *
483         * @return The access token.
484         *
485         * @throws ParseException If the {@code Authorization} header value
486         *                        couldn't be parsed to an access token.
487         */
488        public static AccessToken parse(final String header,
489                                        final AccessTokenType preferredType)
490                throws ParseException {
491        
492                if (! AccessTokenType.BEARER.equals(preferredType) && ! AccessTokenType.DPOP.equals(preferredType)) {
493                        throw new IllegalArgumentException("Unsupported Authorization scheme: " + preferredType);
494                }
495                
496                if (header != null && header.startsWith(AccessTokenType.BEARER.getValue()) || AccessTokenType.BEARER.equals(preferredType)) {
497                        return BearerAccessToken.parse(header);
498                } else {
499                        return DPoPAccessToken.parse(header);
500                }
501        }
502        
503        
504        /**
505         * Parses an HTTP request header value for an access token.
506         *
507         * @param request The HTTP request to parse. Must not be {@code null}.
508         *
509         * @return The access token.
510         *
511         * @throws ParseException If an access token wasn't found in the HTTP
512         *                        request.
513         */
514        public static AccessToken parse(final HTTPRequest request)
515                throws ParseException {
516                
517                if (request.getAuthorization() != null) {
518                        
519                        AccessTokenType tokenType = AccessTokenUtils.determineAccessTokenTypeFromAuthorizationHeader(request.getAuthorization());
520                        
521                        if (AccessTokenType.BEARER.equals(tokenType)) {
522                                return BearerAccessToken.parse(request.getAuthorization());
523                        }
524                        
525                        if (AccessTokenType.DPOP.equals(tokenType)) {
526                                return DPoPAccessToken.parse(request.getAuthorization());
527                        }
528                        
529                        throw new ParseException("Couldn't determine access token type from Authorization header");
530                }
531                
532                // Try alternative token locations, form and query string are
533                // parameters are not differentiated here
534                Map<String, List<String>> params = request.getQueryParameters(); // TODO switch to non-deprecated method
535                return new TypelessAccessToken(AccessTokenUtils.parseValueFromQueryParameters(params));
536        }
537}