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