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