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.Map;
022
023import com.nimbusds.oauth2.sdk.ParseException;
024import com.nimbusds.oauth2.sdk.Scope;
025import com.nimbusds.oauth2.sdk.http.HTTPRequest;
026import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
027import com.nimbusds.oauth2.sdk.util.StringUtils;
028import net.jcip.annotations.Immutable;
029import net.minidev.json.JSONObject;
030
031
032/**
033 * Bearer access token.
034 *
035 * <p>Example bearer access token serialised to JSON:
036 *
037 * <pre>
038 * {
039 *   "access_token" : "2YotnFZFEjr1zCsicMWpAA",
040 *   "token_type"   : "bearer",
041 *   "expires_in"   : 3600,
042 *   "scope"        : "read write"
043 * }
044 * </pre>
045 *
046 * <p>The above example token serialised to a HTTP Authorization header:
047 *
048 * <pre>
049 * Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
050 * </pre>
051 *
052 * <p>Related specifications:
053 *
054 * <ul>
055 *     <li>OAuth 2.0 (RFC 6749), sections 1.4 and 5.1.
056 *     <li>OAuth 2.0 Bearer Token Usage (RFC 6750).
057 * </ul>
058 */
059@Immutable
060public class BearerAccessToken extends AccessToken {
061
062        
063        /**
064         * Creates a new minimal bearer access token with a randomly generated 
065         * 256-bit (32-byte) value, Base64URL-encoded. The optional lifetime 
066         * and scope are left undefined.
067         */
068        public BearerAccessToken() {
069        
070                this(32);
071        }       
072
073
074        /**
075         * Creates a new minimal bearer access token with a randomly generated 
076         * value of the specified byte length, Base64URL-encoded. The optional 
077         * lifetime and scope are left undefined.
078         *
079         * @param byteLength The byte length of the value to generate. Must be
080         *                   greater than one.
081         */
082        public BearerAccessToken(final int byteLength) {
083        
084                this(byteLength, 0L, null);
085        }
086
087
088        /**
089         * Creates a new bearer access token with a randomly generated 256-bit 
090         * (32-byte) value, Base64URL-encoded.
091         *
092         * @param lifetime The lifetime in seconds, 0 if not specified.
093         * @param scope    The scope, {@code null} if not specified.
094         */
095        public BearerAccessToken(final long lifetime, final Scope scope) {
096        
097                this(32, lifetime, scope);
098        }
099
100
101        /**
102         * Creates a new bearer access token with a randomly generated value of 
103         * the specified byte length, Base64URL-encoded.
104         *
105         * @param byteLength The byte length of the value to generate. Must be
106         *                   greater than one.
107         * @param lifetime   The lifetime in seconds, 0 if not specified.
108         * @param scope      The scope, {@code null} if not specified.
109         */
110        public BearerAccessToken(final int byteLength, final long lifetime, final Scope scope) {
111        
112                super(AccessTokenType.BEARER, byteLength, lifetime, scope);
113        }
114        
115        
116        /**
117         * Creates a new minimal bearer access token with the specified value.
118         * The optional lifetime and scope are left undefined.
119         *
120         * @param value The access token value. Must not be {@code null} or
121         *              empty string.
122         */
123        public BearerAccessToken(final String value) {
124        
125                this(value, 0L, null);
126        }
127        
128        
129        /**
130         * Creates a new bearer access token with the specified value and 
131         * optional lifetime and scope.
132         *
133         * @param value    The access token value. Must not be {@code null} or
134         *                 empty string.
135         * @param lifetime The lifetime in seconds, 0 if not specified.
136         * @param scope    The scope, {@code null} if not specified.
137         */
138        public BearerAccessToken(final String value, final long lifetime, final Scope scope) {
139        
140                super(AccessTokenType.BEARER, value, lifetime, scope);
141        }
142        
143        
144        /**
145         * Returns the HTTP Authorization header value for this bearer access 
146         * token.
147         *
148         * <p>Example:
149         *
150         * <pre>
151         * Authorization: Bearer eyJhbGciOiJIUzI1NiJ9
152         * </pre>
153         *
154         * @return The HTTP Authorization header.
155         */
156        @Override
157        public String toAuthorizationHeader(){
158        
159                return "Bearer " + getValue();
160        }
161        
162        
163        @Override
164        public boolean equals(final Object object) {
165        
166                return object instanceof BearerAccessToken &&
167                       this.toString().equals(object.toString());
168        }
169
170
171        /**
172         * Parses a bearer access token from a JSON object access token 
173         * response.
174         *
175         * @param jsonObject The JSON object to parse. Must not be 
176         *                   {@code null}.
177         *
178         * @return The bearer access token.
179         *
180         * @throws ParseException If the JSON object couldn't be parsed to a
181         *                        bearer access token.
182         */
183        public static BearerAccessToken parse(final JSONObject jsonObject)
184                throws ParseException {
185
186                // Parse and verify type
187                AccessTokenType tokenType = new AccessTokenType(JSONObjectUtils.getString(jsonObject, "token_type"));
188                
189                if (! tokenType.equals(AccessTokenType.BEARER))
190                        throw new ParseException("Token type must be \"Bearer\"");
191                
192
193                // Parse value
194                String accessTokenValue = JSONObjectUtils.getString(jsonObject, "access_token");
195                
196
197                // Parse lifetime
198                long lifetime = 0;
199                
200                if (jsonObject.containsKey("expires_in")) {
201
202                        // Lifetime can be a JSON number or string
203
204                        if (jsonObject.get("expires_in") instanceof Number) {
205
206                                lifetime = JSONObjectUtils.getLong(jsonObject, "expires_in");
207                        }
208                        else {
209                                String lifetimeStr = JSONObjectUtils.getString(jsonObject, "expires_in");
210
211                                try {
212                                        lifetime = new Long(lifetimeStr);
213
214                                } catch (NumberFormatException e) {
215
216                                        throw new ParseException("Invalid \"expires_in\" parameter, must be integer");
217                                }
218                        }
219                }
220
221
222                // Parse scope
223                Scope scope = null;
224
225                if (jsonObject.containsKey("scope"))
226                        scope = Scope.parse(JSONObjectUtils.getString(jsonObject, "scope"));
227
228
229                return new BearerAccessToken(accessTokenValue, lifetime, scope);
230        }
231        
232        
233        /**
234         * Parses an HTTP Authorization header for a bearer access token.
235         *
236         * @param header The HTTP Authorization header value to parse. May be
237         *               {@code null} if the header is missing, in which case
238         *               an exception will be thrown.
239         *
240         * @return The bearer access token.
241         *
242         * @throws ParseException If the HTTP Authorization header value 
243         *                        couldn't be parsed to a bearer access token.
244         */
245        public static BearerAccessToken parse(final String header)
246                throws ParseException {
247
248                if (StringUtils.isBlank(header))
249                        throw new ParseException("Missing HTTP Authorization header", BearerTokenError.MISSING_TOKEN);
250        
251                String[] parts = header.split("\\s", 2);
252        
253                if (parts.length != 2)
254                        throw new ParseException("Invalid HTTP Authorization header value", BearerTokenError.INVALID_REQUEST);
255                
256                if (! parts[0].equals("Bearer"))
257                        throw new ParseException("Token type must be \"Bearer\"", BearerTokenError.INVALID_REQUEST);
258                
259                try {
260                        return new BearerAccessToken(parts[1]);
261                        
262                } catch (IllegalArgumentException e) {
263                
264                        throw new ParseException(e.getMessage(), BearerTokenError.INVALID_REQUEST);
265                }
266        }
267        
268        
269        /**
270         * Parses a query or form parameters map for a bearer access token.
271         *
272         * @param parameters The query parameters. Must not be {@code null}.
273         *
274         * @return The bearer access token.
275         *
276         * @throws ParseException If a bearer access token wasn't found in the
277         *                        parameters.
278         */
279        public static BearerAccessToken parse(final Map<String,String> parameters)
280                throws ParseException {
281                
282                if (! parameters.containsKey("access_token")) {
283                        throw new ParseException("Missing access token parameter", BearerTokenError.MISSING_TOKEN);
284                }
285                
286                String accessTokenValue = parameters.get("access_token");
287                
288                if (StringUtils.isBlank(accessTokenValue)) {
289                        throw new ParseException("Blank / empty access token", BearerTokenError.INVALID_REQUEST);
290                }
291                
292                return new BearerAccessToken(accessTokenValue);
293        }
294        
295        
296        
297        /**
298         * Parses an HTTP request for a bearer access token.
299         * 
300         * @param request The HTTP request to parse. Must not be {@code null}.
301         * 
302         * @return The bearer access token.
303         * 
304         * @throws ParseException If a bearer access token wasn't found in the
305         *                        HTTP request.
306         */
307        public static BearerAccessToken parse(final HTTPRequest request)
308                throws ParseException {
309
310                // See http://tools.ietf.org/html/rfc6750#section-2
311
312                String authzHeader = request.getAuthorization();
313
314                if (authzHeader != null) {
315
316                        return parse(authzHeader);
317                }
318
319                // Try alternative token locations, form and query string are
320                // parameters are not differentiated here
321
322                Map<String,String> params = request.getQueryParameters();
323                        
324                return parse(params);
325        }
326}