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