001/* 002 * Copyright (c) 2010-2021 Mark Allen, Norbert Bartels. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in 012 * all copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 * THE SOFTWARE. 021 */ 022package com.restfb; 023 024import static com.restfb.util.UrlUtils.extractParametersFromQueryString; 025import static java.lang.String.format; 026import static java.util.Collections.unmodifiableList; 027 028import java.util.*; 029 030import com.restfb.batch.BatchRequest; 031import com.restfb.batch.BatchResponse; 032import com.restfb.exception.FacebookException; 033import com.restfb.exception.FacebookOAuthException; 034import com.restfb.exception.FacebookSignedRequestParsingException; 035import com.restfb.exception.FacebookSignedRequestVerificationException; 036import com.restfb.exception.devicetoken.FacebookDeviceTokenCodeExpiredException; 037import com.restfb.exception.devicetoken.FacebookDeviceTokenDeclinedException; 038import com.restfb.exception.devicetoken.FacebookDeviceTokenPendingException; 039import com.restfb.exception.devicetoken.FacebookDeviceTokenSlowdownException; 040import com.restfb.json.JsonObject; 041import com.restfb.scope.ScopeBuilder; 042import com.restfb.types.AbstractFacebookType; 043import com.restfb.types.DeviceCode; 044import com.restfb.util.ReflectionUtils; 045 046/** 047 * Specifies how a <a href="http://developers.facebook.com/docs/api">Facebook Graph API</a> client must operate. 048 * <p> 049 * If you'd like to... 050 * 051 * <ul> 052 * <li>Fetch an object: use {@link #fetchObject(String, Class, Parameter...)} or 053 * {@link #fetchObjects(List, Class, Parameter...)}</li> 054 * <li>Fetch a connection: use {@link #fetchConnection(String, Class, Parameter...)}</li> 055 * <li>Execute operations in batch: use {@link #executeBatch(BatchRequest...)} or {@link #executeBatch(List, List)}</li> 056 * <li>Publish data: use {@link #publish(String, Class, Parameter...)} or 057 * {@link #publish(String, Class, BinaryAttachment, Parameter...)}</li> 058 * <li>Delete an object: use {@link #deleteObject(String, Parameter...)}</li> 059 * </ul> 060 * 061 * <p> 062 * You may also perform some common access token operations. If you'd like to... 063 * 064 * <ul> 065 * <li>Extend the life of an access token: use {@link #obtainExtendedAccessToken(String, String, String)}</li> 066 * <li>Obtain an access token for use on behalf of an application instead of a user, use 067 * {@link #obtainAppAccessToken(String, String)}.</li> 068 * <li>Convert old-style session keys to OAuth access tokens: use 069 * {@link #convertSessionKeysToAccessTokens(String, String, String...)}</li> 070 * <li>Verify and extract data from a signed request: use {@link #parseSignedRequest(String, String, Class)}</li> 071 * </ul> 072 * 073 * @author <a href="http://restfb.com">Mark Allen</a> 074 * @author Scott Hernandez 075 * @author Mattia Tommasone 076 * @author <a href="http://ex-nerd.com">Chris Petersen</a> 077 * @author Josef Gierbl 078 * @author Broc Seib 079 */ 080public interface FacebookClient { 081 /** 082 * Fetches a single <a href="http://developers.facebook.com/docs/reference/api/">Graph API object</a>, mapping the 083 * result to an instance of {@code objectType}. 084 * 085 * @param <T> 086 * Java type to map to. 087 * @param object 088 * ID of the object to fetch, e.g. {@code "me"}. 089 * @param objectType 090 * Object type token. 091 * @param parameters 092 * URL parameters to include in the API call (optional). 093 * @return An instance of type {@code objectType} which contains the requested object's data. 094 * @throws FacebookException 095 * If an error occurs while performing the API call. 096 */ 097 <T> T fetchObject(String object, Class<T> objectType, Parameter... parameters); 098 099 /** 100 * creates a new <code>FacebookClient</code> from a old one. 101 * 102 * App secret and and api version are taken from the original client. 103 * 104 * @param accessToken 105 * this accesstoken is used for the new client 106 * @return a new Facebookclient 107 */ 108 FacebookClient createClientWithAccessToken(String accessToken); 109 110 /** 111 * Fetches multiple <a href="http://developers.facebook.com/docs/reference/api/">Graph API objects</a> in a single 112 * call, mapping the results to an instance of {@code objectType}. 113 * <p> 114 * You'll need to write your own container type ({@code objectType}) to hold the results. See 115 * <a href="http://restfb.com">http://restfb.com</a> for an example of how to do this. 116 * 117 * @param <T> 118 * Java type to map to. 119 * @param ids 120 * IDs of the objects to fetch, e.g. {@code "me", "arjun"}. 121 * @param objectType 122 * Object type token. 123 * @param parameters 124 * URL parameters to include in the API call (optional). 125 * @return An instance of type {@code objectType} which contains the requested objects' data. 126 * @throws FacebookException 127 * If an error occurs while performing the API call. 128 */ 129 <T> T fetchObjects(List<String> ids, Class<T> objectType, Parameter... parameters); 130 131 /** 132 * Fetches a Graph API {@code Connection} type, mapping the result to an instance of {@code connectionType}. 133 * 134 * @param <T> 135 * Java type to map to. 136 * @param connection 137 * The name of the connection, e.g. {@code "me/feed"}. 138 * @param connectionType 139 * Connection type token. 140 * @param parameters 141 * URL parameters to include in the API call (optional). 142 * @return An instance of type {@code connectionType} which contains the requested Connection's data. 143 * @throws FacebookException 144 * If an error occurs while performing the API call. 145 */ 146 <T> Connection<T> fetchConnection(String connection, Class<T> connectionType, Parameter... parameters); 147 148 /** 149 * Fetches a previous/next page of a Graph API {@code Connection} type, mapping the result to an instance of 150 * {@code connectionType}. 151 * 152 * @param <T> 153 * Java type to map to. 154 * @param connectionPageUrl 155 * The URL of the connection page to fetch, usually retrieved via {@link Connection#getPreviousPageUrl()} or 156 * {@link Connection#getNextPageUrl()}. 157 * @param connectionType 158 * Connection type token. 159 * @return An instance of type {@code connectionType} which contains the requested Connection's data. 160 * @throws FacebookException 161 * If an error occurs while performing the API call. 162 */ 163 <T> Connection<T> fetchConnectionPage(String connectionPageUrl, Class<T> connectionType); 164 165 /** 166 * Executes operations as a batch using the <a href="https://developers.facebook.com/docs/reference/api/batch/">Batch 167 * API</a>. 168 * 169 * @param batchRequests 170 * The operations to execute. 171 * @return The execution results in the order in which the requests were specified. 172 */ 173 List<BatchResponse> executeBatch(BatchRequest... batchRequests); 174 175 /** 176 * Executes operations as a batch using the <a href="https://developers.facebook.com/docs/reference/api/batch/">Batch 177 * API</a>. 178 * 179 * @param batchRequests 180 * The operations to execute. 181 * @return The execution results in the order in which the requests were specified. 182 */ 183 List<BatchResponse> executeBatch(List<BatchRequest> batchRequests); 184 185 /** 186 * Executes operations as a batch with binary attachments using the 187 * <a href="https://developers.facebook.com/docs/reference/api/batch/">Batch API</a>. 188 * 189 * @param batchRequests 190 * The operations to execute. 191 * @param binaryAttachments 192 * Binary attachments referenced by the batch requests. 193 * @return The execution results in the order in which the requests were specified. 194 * @since 1.6.5 195 */ 196 List<BatchResponse> executeBatch(List<BatchRequest> batchRequests, List<BinaryAttachment> binaryAttachments); 197 198 /** 199 * Performs a <a href="http://developers.facebook.com/docs/api#publishing">Graph API publish</a> operation on the 200 * given {@code connection}, mapping the result to an instance of {@code objectType}. 201 * 202 * @param <T> 203 * Java type to map to. 204 * @param connection 205 * The Connection to publish to. 206 * @param objectType 207 * Object type token. 208 * @param parameters 209 * URL parameters to include in the API call. 210 * @return An instance of type {@code objectType} which contains the Facebook response to your publish request. 211 * @throws FacebookException 212 * If an error occurs while performing the API call. 213 */ 214 <T> T publish(String connection, Class<T> objectType, Parameter... parameters); 215 216 /** 217 * Performs a <a href="http://developers.facebook.com/docs/api#publishing">Graph API publish</a> operation on the 218 * given {@code connection} and includes some files - photos, for example - in the publish request, and mapping the 219 * result to an instance of {@code objectType}. 220 * 221 * @param <T> 222 * Java type to map to. 223 * @param connection 224 * The Connection to publish to. 225 * @param objectType 226 * Object type token. 227 * @param binaryAttachments 228 * The files to include in the publish request. 229 * @param parameters 230 * URL parameters to include in the API call. 231 * @return An instance of type {@code objectType} which contains the Facebook response to your publish request. 232 * @throws FacebookException 233 * If an error occurs while performing the API call. 234 */ 235 <T> T publish(String connection, Class<T> objectType, List<BinaryAttachment> binaryAttachments, 236 Parameter... parameters); 237 238 /** 239 * Performs a <a href="http://developers.facebook.com/docs/api#publishing">Graph API publish</a> operation on the 240 * given {@code connection} and includes a file - a photo, for example - in the publish request, and mapping the 241 * result to an instance of {@code objectType}. 242 * 243 * @param <T> 244 * Java type to map to. 245 * @param connection 246 * The Connection to publish to. 247 * @param objectType 248 * Object type token. 249 * @param binaryAttachment 250 * The file to include in the publish request. 251 * @param parameters 252 * URL parameters to include in the API call. 253 * @return An instance of type {@code objectType} which contains the Facebook response to your publish request. 254 * @throws FacebookException 255 * If an error occurs while performing the API call. 256 */ 257 <T> T publish(String connection, Class<T> objectType, BinaryAttachment binaryAttachment, Parameter... parameters); 258 259 /** 260 * Performs a <a href="http://developers.facebook.com/docs/api#deleting">Graph API delete</a> operation on the given 261 * {@code object}. 262 * 263 * @param object 264 * The ID of the object to delete. 265 * @param parameters 266 * URL parameters to include in the API call. 267 * @return {@code true} if Facebook indicated that the object was successfully deleted, {@code false} otherwise. 268 * @throws FacebookException 269 * If an error occurred while attempting to delete the object. 270 */ 271 boolean deleteObject(String object, Parameter... parameters); 272 273 /** 274 * Converts an arbitrary number of {@code sessionKeys} to OAuth access tokens. 275 * <p> 276 * See the <a href="http://developers.facebook.com/docs/guides/upgrade">Facebook Platform Upgrade Guide</a> for 277 * details on how this process works and why you should convert your application's session keys if you haven't 278 * already. 279 * 280 * @param appId 281 * A Facebook application ID. 282 * @param secretKey 283 * A Facebook application secret key. 284 * @param sessionKeys 285 * The Old REST API session keys to be converted to OAuth access tokens. 286 * @return A list of access tokens ordered to correspond to the {@code sessionKeys} argument list. 287 * @throws FacebookException 288 * If an error occurs while attempting to convert the session keys to API keys. 289 * @since 1.6 290 */ 291 List<AccessToken> convertSessionKeysToAccessTokens(String appId, String secretKey, String... sessionKeys); 292 293 /** 294 * Obtains an access token which can be used to perform Graph API operations on behalf of a user. 295 * <p> 296 * See <a href="https://developers.facebook.com/docs/facebook-login/access-tokens">Access Tokens</a>. 297 * 298 * @param appId 299 * The ID of the app for which you'd like to obtain an access token. 300 * @param appSecret 301 * The secret for the app for which you'd like to obtain an access token. 302 * @param redirectUri 303 * The redirect URI which was used to obtain the {@code verificationCode}. 304 * @param verificationCode 305 * The verification code in the Graph API callback to the redirect URI. 306 * @return The access token for the user identified by {@code appId}, {@code appSecret}, {@code redirectUri} and 307 * {@code verificationCode}. 308 * @throws FacebookException 309 * If an error occurs while attempting to obtain an access token. 310 * @since 1.8.0 311 */ 312 AccessToken obtainUserAccessToken(String appId, String appSecret, String redirectUri, String verificationCode); 313 314 /** 315 * Obtains an access token which can be used to perform Graph API operations on behalf of an application instead of a 316 * user. 317 * <p> 318 * See <a href="https://developers.facebook.com/docs/authentication/applications/" >Facebook's authenticating as an 319 * app documentation</a>. 320 * 321 * @param appId 322 * The ID of the app for which you'd like to obtain an access token. 323 * @param appSecret 324 * The secret for the app for which you'd like to obtain an access token. 325 * @return The access token for the application identified by {@code appId} and {@code appSecret}. 326 * @throws FacebookException 327 * If an error occurs while attempting to obtain an access token. 328 * @since 1.6.10 329 */ 330 AccessToken obtainAppAccessToken(String appId, String appSecret); 331 332 /** 333 * Obtains an extended access token for the given existing, non-expired, short-lived access_token. 334 * <p> 335 * See <a href="https://developers.facebook.com/roadmap/offline-access-removal/#extend_token">Facebook's extend access 336 * token documentation</a>. 337 * 338 * @param appId 339 * The ID of the app for which you'd like to obtain an extended access token. 340 * @param appSecret 341 * The secret for the app for which you'd like to obtain an extended access token. 342 * @param accessToken 343 * The non-expired, short-lived access token to extend. 344 * @return An extended access token for the given {@code accessToken}. 345 * @throws FacebookException 346 * If an error occurs while attempting to obtain an extended access token. 347 * @since 1.6.10 348 */ 349 AccessToken obtainExtendedAccessToken(String appId, String appSecret, String accessToken); 350 351 /** 352 * Generates an {@code appsecret_proof} value. 353 * <p> 354 * See <a href="https://developers.facebook.com/docs/graph-api/securing-requests">Facebook's 'securing requests' 355 * documentation</a> for more info. 356 * 357 * @param accessToken 358 * The access token required to generate the {@code appsecret_proof} value. 359 * @param appSecret 360 * The secret for the app for which you'd like to generate the {@code appsecret_proof} value. 361 * @return A hex-encoded SHA256 hash as a {@code String}. 362 * @throws IllegalStateException 363 * If creating the {@code appsecret_proof} fails. 364 * @since 1.6.13 365 */ 366 String obtainAppSecretProof(String accessToken, String appSecret); 367 368 /** 369 * Convenience method which invokes {@link #obtainExtendedAccessToken(String, String, String)} with the current access 370 * token. 371 * 372 * @param appId 373 * The ID of the app for which you'd like to obtain an extended access token. 374 * @param appSecret 375 * The secret for the app for which you'd like to obtain an extended access token. 376 * @return An extended access token for the given {@code accessToken}. 377 * @throws FacebookException 378 * If an error occurs while attempting to obtain an extended access token. 379 * @throws IllegalStateException 380 * If this instance was not constructed with an access token. 381 * @since 1.6.10 382 */ 383 AccessToken obtainExtendedAccessToken(String appId, String appSecret); 384 385 /** 386 * Parses a signed request and verifies it against your App Secret. 387 * <p> 388 * See <a href="http://developers.facebook.com/docs/howtos/login/signed-request/">Facebook's signed request 389 * documentation</a>. 390 * 391 * @param signedRequest 392 * The signed request to parse. 393 * @param appSecret 394 * The secret for the app that can read this signed request. 395 * @param objectType 396 * Object type token. 397 * @param <T> 398 * class of objectType 399 * @return An instance of type {@code objectType} which contains the decoded object embedded within 400 * {@code signedRequest}. 401 * @throws FacebookSignedRequestParsingException 402 * If an error occurs while trying to process {@code signedRequest}. 403 * @throws FacebookSignedRequestVerificationException 404 * If {@code signedRequest} fails verification against {@code appSecret}. 405 * @since 1.6.13 406 */ 407 <T> T parseSignedRequest(String signedRequest, String appSecret, Class<T> objectType); 408 409 /** 410 * Method to initialize the device access token generation. 411 * 412 * You receive a {@link DeviceCode} instance and have to show the user the {@link DeviceCode#getVerificationUri()} and 413 * the {@link DeviceCode#getUserCode()}. The user have to enter the user code at the verification url. 414 * 415 * Save the {@link DeviceCode#getCode()} to use it later, when polling Facebook with the 416 * {@link #obtainDeviceAccessToken(java.lang.String)} method. 417 * 418 * @param scope 419 * List of Permissions to request from the person using your app. 420 * @return Instance of {@code DeviceCode} including the information to obtain the Device access token 421 */ 422 DeviceCode fetchDeviceCode(ScopeBuilder scope); 423 424 /** 425 * Method to poll Facebook and fetch the Device Access Token. 426 * 427 * You have to use this method to check if the user confirms the authorization. 428 * 429 * {@link FacebookOAuthException} can be thrown if the authorization is declined or still pending. 430 * 431 * @param code 432 * The device 433 * @return An extended access token for the given {@link AccessToken}. 434 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenCodeExpiredException 435 * the {@link DeviceCode#getCode()} is expired, please fetch a new {@link DeviceCode}. 436 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenPendingException 437 * the user has not finished the authorisation process, yet. Please poll again later. 438 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenDeclinedException 439 * the user declined the authorisation. You have to handle this problem. 440 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenSlowdownException 441 * you tried too often to fetch the device access token. You have to use a larger interval 442 * @since 1.12.0 443 */ 444 AccessToken obtainDeviceAccessToken(String code) throws FacebookDeviceTokenCodeExpiredException, 445 FacebookDeviceTokenPendingException, FacebookDeviceTokenDeclinedException, FacebookDeviceTokenSlowdownException; 446 447 /** 448 * <p> 449 * When working with access tokens, you may need to check what information is associated with them, such as its user 450 * or expiry. To get this information you can use the debug tool in the developer site, or you can use this function. 451 * </p> 452 * 453 * <p> 454 * You must instantiate your FacebookClient using your App Access Token, or a valid User Access Token from a developer 455 * of the app. 456 * </p> 457 * 458 * <p> 459 * Note that if your app is set to Native/Desktop in the Advanced settings of your App Dashboard, the underlying 460 * GraphAPI endpoint will not work with your app token unless you change the "App Secret in Client" setting to NO. If 461 * you do not see this setting, make sure your "App Type" is set to Native/Desktop and then press the save button at 462 * the bottom of the page. This will not affect apps set to Web. 463 * </p> 464 * 465 * <p> 466 * The response of the API call is a JSON array containing data and a map of fields. For example: 467 * </p> 468 * 469 * <pre> 470 * {@code 471 * { 472 * "data": { 473 * "app_id": 138483919580948, 474 * "application": "Social Cafe", 475 * "expires_at": 1352419328, 476 * "is_valid": true, 477 * "issued_at": 1347235328, 478 * "metadata": { 479 * "sso": "iphone-safari" 480 * }, 481 * "scopes": [ 482 * "email", 483 * "publish_actions" 484 * ], 485 * "user_id": 1207059 486 * } 487 * } 488 * } 489 * </pre> 490 * 491 * <p> 492 * Note that the {@code issued_at} field is not returned for short-lived access tokens. 493 * </p> 494 * 495 * <p> 496 * See <a href="https://developers.facebook.com/docs/howtos/login/debugging-access-tokens/"> Debugging an Access 497 * Token</a> 498 * </p> 499 * 500 * @param inputToken 501 * The Access Token to debug. 502 * 503 * @return A JsonObject containing the debug information for the accessToken. 504 * @since 1.6.13 505 */ 506 DebugTokenInfo debugToken(String inputToken); 507 508 /** 509 * Gets the {@code JsonMapper} used to convert Facebook JSON to Java objects. 510 * 511 * @return The {@code JsonMapper} used to convert Facebook JSON to Java objects. 512 * @since 1.6.7 513 */ 514 JsonMapper getJsonMapper(); 515 516 /** 517 * Gets the {@code WebRequestor} used to talk to the Facebook API endpoints. 518 * 519 * @return The {@code WebRequestor} used to talk to the Facebook API endpoints. 520 * @since 1.6.7 521 */ 522 WebRequestor getWebRequestor(); 523 524 /** 525 * generates an logout url 526 * 527 * @param next 528 * may be null, url the webpage should redirect after logout 529 * @return the logout url 530 * @since 1.9.0 531 */ 532 String getLogoutUrl(String next); 533 534 /** 535 * generates the login dialog url 536 * 537 * @param appId 538 * The ID of your app, found in your app's dashboard. 539 * @param redirectUri 540 * The URL that you want to redirect the person logging in back to. This URL will capture the response from 541 * the Login Dialog. If you are using this in a webview within a desktop app, this must be set to 542 * <code>https://www.facebook.com/connect/login_success.html</code>. 543 * @param scope 544 * List of Permissions to request from the person using your app. 545 * @param additionalParameters 546 * List of additional parameters 547 * @since 1.9.0 548 * @return the login dialog url 549 */ 550 String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, Parameter... additionalParameters); 551 552 /** 553 * Represents an access token/expiration date pair. 554 * <p> 555 * Facebook returns these types when performing access token-related operations - see 556 * {@link com.restfb.FacebookClient#convertSessionKeysToAccessTokens(String, String, String...)}, 557 * {@link com.restfb.FacebookClient#obtainAppAccessToken(String, String)}, and 558 * {@link com.restfb.FacebookClient#obtainExtendedAccessToken(String, String, String)} for details. 559 * 560 * @author <a href="http://restfb.com">Mark Allen</a> 561 */ 562 class AccessToken { 563 @Facebook("access_token") 564 private String accessToken; 565 566 @Facebook("expires_in") 567 private Long rawExpires; 568 569 private Long expires; 570 571 @Facebook("token_type") 572 private String tokenType; 573 574 private FacebookClient client; 575 576 public void setClient(FacebookClient client) { 577 this.client = client; 578 } 579 580 public FacebookClient getClient() { 581 return Optional.ofNullable(client).orElse(null); 582 } 583 584 /** 585 * Given a query string of the form {@code access_token=XXX} or {@code access_token=XXX&expires=YYY}, return an 586 * {@code AccessToken} instance. 587 * <p> 588 * The {@code queryString} is required to contain an {@code access_token} parameter with a non-{@code null} value. 589 * The {@code expires} value is optional and should be the number of seconds since the epoch. If the {@code expires} 590 * value cannot be parsed, the returned {@code AccessToken} will have a {@code null} {@code expires} value. 591 * 592 * @param queryString 593 * The Facebook query string out of which to parse an {@code AccessToken} instance. 594 * @return An {@code AccessToken} instance which corresponds to the given {@code queryString}. 595 * @throws IllegalArgumentException 596 * If no {@code access_token} parameter is present in the query string. 597 * @since 1.6.10 598 */ 599 public static AccessToken fromQueryString(String queryString) { 600 // Query string can be of the form 'access_token=XXX' or 601 // 'access_token=XXX&expires=YYY' 602 Map<String, List<String>> urlParameters = extractParametersFromQueryString(queryString); 603 604 String extendedAccessToken = null; 605 String tokenType = null; 606 607 if (urlParameters.containsKey("access_token")) { 608 extendedAccessToken = urlParameters.get("access_token").get(0); 609 } 610 611 if (urlParameters.containsKey("token_type")) { 612 tokenType = urlParameters.get("token_type").get(0); 613 } 614 615 if (extendedAccessToken == null) { 616 throw new IllegalArgumentException(format( 617 "Was expecting a query string of the form 'access_token=XXX' or 'access_token=XXX&expires=YYY'. Instead, the query string was '%s'", 618 queryString)); 619 } 620 621 Long expires = null; 622 623 // If an expires or expires_in value was provided and it's a valid long, great - use it. 624 // Otherwise ignore it. 625 String rawExpires = null; 626 627 if (urlParameters.containsKey("expires")) { 628 rawExpires = urlParameters.get("expires").get(0); 629 } 630 631 if (urlParameters.containsKey("expires_in")) { 632 rawExpires = urlParameters.get("expires_in").get(0); 633 } 634 635 if (rawExpires != null && rawExpires.trim().matches("\\d+")) { 636 expires = new Date().getTime() + Long.parseLong(rawExpires) * 1000L; 637 } 638 639 AccessToken accessToken = new AccessToken(); 640 accessToken.accessToken = extendedAccessToken; 641 accessToken.expires = expires; 642 accessToken.tokenType = tokenType; 643 return accessToken; 644 } 645 646 @Override 647 public int hashCode() { 648 return ReflectionUtils.hashCode(this); 649 } 650 651 @Override 652 public boolean equals(Object that) { 653 return ReflectionUtils.equals(this, that); 654 } 655 656 @Override 657 public String toString() { 658 return ReflectionUtils.toString(this); 659 } 660 661 /** 662 * The access token's value. 663 * 664 * @return The access token's value. 665 */ 666 public String getAccessToken() { 667 return accessToken; 668 } 669 670 /** 671 * The date on which the access token expires. 672 * 673 * @return The date on which the access token expires. 674 */ 675 public Date getExpires() { 676 return expires == null ? null : new Date(expires); 677 } 678 679 /** 680 * The token type of this access token provided by Facebook 681 * 682 * @return the access token type 683 */ 684 public String getTokenType() { 685 return tokenType; 686 } 687 688 @JsonMapper.JsonMappingCompleted 689 void convertExpires() { 690 if (rawExpires != null) { 691 expires = new Date().getTime() + 1000L * rawExpires; 692 } 693 } 694 695 } 696 697 /** 698 * <p> 699 * Represents the result of a {@link FacebookClient#debugToken(String)} inquiry. 700 * </p> 701 * 702 * FIXME does this class belong here? 703 * 704 * <p> 705 * See <a href="https://developers.facebook.com/docs/howtos/login/debugging-access-tokens/">Debug access tokens</a> 706 * 707 * @author Broc Seib 708 */ 709 class DebugTokenInfo extends AbstractFacebookType { 710 711 private static final long serialVersionUID = 1L; 712 713 /** 714 * The ID of the application this access token is for. 715 */ 716 @Facebook("app_id") 717 private String appId; 718 719 /** 720 * Name of the application this access token is for. 721 */ 722 @Facebook 723 private String application; 724 725 /** 726 * Timestamp when this access token expires. 727 */ 728 @Facebook("expires_at") 729 private Date expiresAt; 730 731 /** 732 * Timestamp when app's access to user data expires. 733 */ 734 @Facebook("data_access_expires_at") 735 private Date dataAccessExpiresAt; 736 737 /** 738 * Timestamp when this access token was issued. 739 */ 740 @Facebook("issued_at") 741 private Date issuedAt; 742 743 /** 744 * Whether the access token is still valid or not. 745 */ 746 @Facebook("is_valid") 747 private Boolean isValid; 748 749 /** 750 * The ID of the user this access token is for. 751 */ 752 @Facebook("user_id") 753 private String userId; 754 755 /** 756 * For impersonated access tokens, the ID of the page this token contains. 757 */ 758 @Facebook("profile_id") 759 private String profileId; 760 761 /** 762 * General metadata associated with the access token. Can contain data like 'sso', 'auth_type', 'auth_nonce' 763 */ 764 @Facebook 765 private JsonObject metadata; 766 767 /** 768 * Any error that a request to the graph api would return due to the access token. 769 */ 770 @Facebook 771 private DebugTokenError error; 772 773 /** 774 * List of permissions that the user has granted for the app in this access token. 775 */ 776 @Facebook 777 private List<String> scopes = new ArrayList<>(); 778 779 @Facebook 780 private String type; 781 782 /** 783 * The application id. 784 * 785 * @return The id of the application. 786 */ 787 public String getAppId() { 788 return appId; 789 } 790 791 /** 792 * The application name. 793 * 794 * @return The name of the application. 795 */ 796 public String getApplication() { 797 return application; 798 } 799 800 /** 801 * The date on which the access token expires. 802 * 803 * @return The date on which the access token expires. 804 */ 805 public Date getExpiresAt() { 806 return expiresAt; 807 } 808 809 /** 810 * Timestamp when app's access to user data expires. 811 * 812 * @return The date when app's access to user data expires. 813 */ 814 public Date getDataAccessExpiresAt() { 815 return dataAccessExpiresAt; 816 } 817 818 /** 819 * The date on which the access token was issued. 820 * 821 * @return The date on which the access token was issued. 822 */ 823 public Date getIssuedAt() { 824 return issuedAt; 825 } 826 827 /** 828 * Whether or not the token is valid. 829 * 830 * @return Whether or not the token is valid. 831 */ 832 public Boolean isValid() { 833 return isValid; 834 } 835 836 /** 837 * The user id. 838 * 839 * @return The user id. 840 */ 841 public String getUserId() { 842 return userId; 843 } 844 845 /** 846 * List of scopes the access token 'contains' 847 * 848 * @return list of scopes 849 */ 850 public List<String> getScopes() { 851 return unmodifiableList(scopes); 852 } 853 854 /** 855 * General metadata associated with the access token. Can contain data like 'sso', 'auth_type', 'auth_nonce' 856 * 857 * @return General metadata associated with the access token 858 */ 859 public JsonObject getMetaData() { 860 return metadata; 861 } 862 863 /** 864 * All Error data associated with access token debug. 865 * 866 * @return debug token error 867 */ 868 public DebugTokenError getDebugTokenError() { 869 return error; 870 } 871 872 public String getType() { 873 return type; 874 } 875 } 876 877 class DebugTokenError extends AbstractFacebookType { 878 879 private static final long serialVersionUID = 1L; 880 881 /** 882 * The error code for the error. 883 */ 884 @Facebook 885 private Integer code; 886 887 /** 888 * The error message for the error. 889 */ 890 @Facebook 891 private String message; 892 893 /** 894 * The error subcode for the error. 895 */ 896 @Facebook 897 private Integer subcode; 898 899 /** 900 * The error code for the error. 901 * 902 * @return The error code for the error. 903 */ 904 public Integer getCode() { 905 return code; 906 } 907 908 /** 909 * The error message for the error. 910 * 911 * @return The error message for the error. 912 */ 913 public String getMessage() { 914 return message; 915 } 916 917 /** 918 * The error subcode for the error. 919 * 920 * @return The error subcode for the error. 921 */ 922 public Integer getSubcode() { 923 return subcode; 924 } 925 926 } 927}