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 011 import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 012 013 014 /** 015 * Bearer access token. This class is immutable. 016 * 017 * <p>Example bearer access token serialised to JSON: 018 * 019 * <pre> 020 * { 021 * "access_token" : "2YotnFZFEjr1zCsicMWpAA", 022 * "token_type" : "bearer", 023 * "expires_in" : 3600, 024 * "scope" : "read write" 025 * } 026 * </pre> 027 * 028 * <p>The above example token serialised to a HTTP Authorization header: 029 * 030 * <pre> 031 * Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA 032 * </pre> 033 * 034 * <p>Related specifications: 035 * 036 * <ul> 037 * <li>OAuth 2.0 (RFC 6749), sections 1.4 and 5.1. 038 * <li>OAuth 2.0 Bearer Token Usage (RFC 6750). 039 * </ul> 040 * 041 * @author Vladimir Dzhuvinov 042 * @version $version$ (2013-01-19) 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 }