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