001/* 002 * Copyright (c) 2010-2023 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#publishing">Graph API publish</a> operation on the 261 * given {@code connection} and includes special body in the publish request, and mapping the 262 * result to an instance of {@code objectType}. 263 * 264 * @param <T> 265 * Java type to map to. 266 * @param connection 267 * The Connection to publish to. 268 * @param objectType 269 * Object type token. 270 * @param body 271 * The body used in the POST request. 272 * @param parameters 273 * URL parameters to include in the API call. 274 * @return An instance of type {@code objectType} which contains the Facebook response to your publish request. 275 * @throws FacebookException 276 * If an error occurs while performing the API call. 277 */ 278 <T> T publish(String connection, Class<T> objectType, Body body, Parameter... parameters); 279 280 /** 281 * Performs a <a href="http://developers.facebook.com/docs/api#deleting">Graph API delete</a> operation on the given 282 * {@code object}. 283 * 284 * @param object 285 * The ID of the object to delete. 286 * @param parameters 287 * URL parameters to include in the API call. 288 * @return {@code true} if Facebook indicated that the object was successfully deleted, {@code false} otherwise. 289 * @throws FacebookException 290 * If an error occurred while attempting to delete the object. 291 */ 292 boolean deleteObject(String object, Parameter... parameters); 293 294 /** 295 * Converts an arbitrary number of {@code sessionKeys} to OAuth access tokens. 296 * <p> 297 * See the <a href="http://developers.facebook.com/docs/guides/upgrade">Facebook Platform Upgrade Guide</a> for 298 * details on how this process works and why you should convert your application's session keys if you haven't 299 * already. 300 * 301 * @param appId 302 * A Facebook application ID. 303 * @param secretKey 304 * A Facebook application secret key. 305 * @param sessionKeys 306 * The Old REST API session keys to be converted to OAuth access tokens. 307 * @return A list of access tokens ordered to correspond to the {@code sessionKeys} argument list. 308 * @throws FacebookException 309 * If an error occurs while attempting to convert the session keys to API keys. 310 * @since 1.6 311 */ 312 List<AccessToken> convertSessionKeysToAccessTokens(String appId, String secretKey, String... sessionKeys); 313 314 /** 315 * Obtains an access token which can be used to perform Graph API operations on behalf of a user. 316 * <p> 317 * See <a href="https://developers.facebook.com/docs/facebook-login/access-tokens">Access Tokens</a>. 318 * 319 * @param appId 320 * The ID of the app for which you'd like to obtain an access token. 321 * @param appSecret 322 * The secret for the app for which you'd like to obtain an access token. 323 * @param redirectUri 324 * The redirect URI which was used to obtain the {@code verificationCode}. 325 * @param verificationCode 326 * The verification code in the Graph API callback to the redirect URI. 327 * @return The access token for the user identified by {@code appId}, {@code appSecret}, {@code redirectUri} and 328 * {@code verificationCode}. 329 * @throws FacebookException 330 * If an error occurs while attempting to obtain an access token. 331 * @since 1.8.0 332 */ 333 AccessToken obtainUserAccessToken(String appId, String appSecret, String redirectUri, String verificationCode); 334 335 /** 336 * Obtains an access token which can be used to perform Graph API operations on behalf of an application instead of a 337 * user. 338 * <p> 339 * See <a href="https://developers.facebook.com/docs/authentication/applications/" >Facebook's authenticating as an 340 * app documentation</a>. 341 * 342 * @param appId 343 * The ID of the app for which you'd like to obtain an access token. 344 * @param appSecret 345 * The secret for the app for which you'd like to obtain an access token. 346 * @return The access token for the application identified by {@code appId} and {@code appSecret}. 347 * @throws FacebookException 348 * If an error occurs while attempting to obtain an access token. 349 * @since 1.6.10 350 */ 351 AccessToken obtainAppAccessToken(String appId, String appSecret); 352 353 /** 354 * Obtains an extended access token for the given existing, non-expired, short-lived access_token. 355 * <p> 356 * See <a href="https://developers.facebook.com/roadmap/offline-access-removal/#extend_token">Facebook's extend access 357 * token documentation</a>. 358 * 359 * @param appId 360 * The ID of the app for which you'd like to obtain an extended access token. 361 * @param appSecret 362 * The secret for the app for which you'd like to obtain an extended access token. 363 * @param accessToken 364 * The non-expired, short-lived access token to extend. 365 * @return An extended access token for the given {@code accessToken}. 366 * @throws FacebookException 367 * If an error occurs while attempting to obtain an extended access token. 368 * @since 1.6.10 369 */ 370 AccessToken obtainExtendedAccessToken(String appId, String appSecret, String accessToken); 371 372 /** 373 * Generates an {@code appsecret_proof} value. 374 * <p> 375 * See <a href="https://developers.facebook.com/docs/graph-api/securing-requests">Facebook's 'securing requests' 376 * documentation</a> for more info. 377 * 378 * @param accessToken 379 * The access token required to generate the {@code appsecret_proof} value. 380 * @param appSecret 381 * The secret for the app for which you'd like to generate the {@code appsecret_proof} value. 382 * @return A hex-encoded SHA256 hash as a {@code String}. 383 * @throws IllegalStateException 384 * If creating the {@code appsecret_proof} fails. 385 * @since 1.6.13 386 */ 387 String obtainAppSecretProof(String accessToken, String appSecret); 388 389 /** 390 * Convenience method which invokes {@link #obtainExtendedAccessToken(String, String, String)} with the current access 391 * token. 392 * 393 * @param appId 394 * The ID of the app for which you'd like to obtain an extended access token. 395 * @param appSecret 396 * The secret for the app for which you'd like to obtain an extended access token. 397 * @return An extended access token for the given {@code accessToken}. 398 * @throws FacebookException 399 * If an error occurs while attempting to obtain an extended access token. 400 * @throws IllegalStateException 401 * If this instance was not constructed with an access token. 402 * @since 1.6.10 403 */ 404 AccessToken obtainExtendedAccessToken(String appId, String appSecret); 405 406 /** 407 * Parses a signed request and verifies it against your App Secret. 408 * <p> 409 * See <a href="http://developers.facebook.com/docs/howtos/login/signed-request/">Facebook's signed request 410 * documentation</a>. 411 * 412 * @param signedRequest 413 * The signed request to parse. 414 * @param appSecret 415 * The secret for the app that can read this signed request. 416 * @param objectType 417 * Object type token. 418 * @param <T> 419 * class of objectType 420 * @return An instance of type {@code objectType} which contains the decoded object embedded within 421 * {@code signedRequest}. 422 * @throws FacebookSignedRequestParsingException 423 * If an error occurs while trying to process {@code signedRequest}. 424 * @throws FacebookSignedRequestVerificationException 425 * If {@code signedRequest} fails verification against {@code appSecret}. 426 * @since 1.6.13 427 */ 428 <T> T parseSignedRequest(String signedRequest, String appSecret, Class<T> objectType); 429 430 /** 431 * Method to initialize the device access token generation. 432 * 433 * You receive a {@link DeviceCode} instance and have to show the user the {@link DeviceCode#getVerificationUri()} and 434 * the {@link DeviceCode#getUserCode()}. The user have to enter the user code at the verification url. 435 * 436 * Save the {@link DeviceCode#getCode()} to use it later, when polling Facebook with the 437 * {@link #obtainDeviceAccessToken(java.lang.String)} method. 438 * 439 * @param scope 440 * List of Permissions to request from the person using your app. 441 * @return Instance of {@code DeviceCode} including the information to obtain the Device access token 442 */ 443 DeviceCode fetchDeviceCode(ScopeBuilder scope); 444 445 /** 446 * Method to poll Facebook and fetch the Device Access Token. 447 * 448 * You have to use this method to check if the user confirms the authorization. 449 * 450 * {@link FacebookOAuthException} can be thrown if the authorization is declined or still pending. 451 * 452 * @param code 453 * The device 454 * @return An extended access token for the given {@link AccessToken}. 455 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenCodeExpiredException 456 * the {@link DeviceCode#getCode()} is expired, please fetch a new {@link DeviceCode}. 457 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenPendingException 458 * the user has not finished the authorisation process, yet. Please poll again later. 459 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenDeclinedException 460 * the user declined the authorisation. You have to handle this problem. 461 * @throws com.restfb.exception.devicetoken.FacebookDeviceTokenSlowdownException 462 * you tried too often to fetch the device access token. You have to use a larger interval 463 * @since 1.12.0 464 */ 465 AccessToken obtainDeviceAccessToken(String code) throws FacebookDeviceTokenCodeExpiredException, 466 FacebookDeviceTokenPendingException, FacebookDeviceTokenDeclinedException, FacebookDeviceTokenSlowdownException; 467 468 /** 469 * <p> 470 * When working with access tokens, you may need to check what information is associated with them, such as its user 471 * or expiry. To get this information you can use the debug tool in the developer site, or you can use this function. 472 * </p> 473 * 474 * <p> 475 * You must instantiate your FacebookClient using your App Access Token, or a valid User Access Token from a developer 476 * of the app. 477 * </p> 478 * 479 * <p> 480 * Note that if your app is set to Native/Desktop in the Advanced settings of your App Dashboard, the underlying 481 * GraphAPI endpoint will not work with your app token unless you change the "App Secret in Client" setting to NO. If 482 * you do not see this setting, make sure your "App Type" is set to Native/Desktop and then press the save button at 483 * the bottom of the page. This will not affect apps set to Web. 484 * </p> 485 * 486 * <p> 487 * The response of the API call is a JSON array containing data and a map of fields. For example: 488 * </p> 489 * 490 * <pre> 491 * {@code 492 * { 493 * "data": { 494 * "app_id": 138483919580948, 495 * "application": "Social Cafe", 496 * "expires_at": 1352419328, 497 * "is_valid": true, 498 * "issued_at": 1347235328, 499 * "metadata": { 500 * "sso": "iphone-safari" 501 * }, 502 * "scopes": [ 503 * "email", 504 * "publish_actions" 505 * ], 506 * "user_id": 1207059 507 * } 508 * } 509 * } 510 * </pre> 511 * 512 * <p> 513 * Note that the {@code issued_at} field is not returned for short-lived access tokens. 514 * </p> 515 * 516 * <p> 517 * See <a href="https://developers.facebook.com/docs/howtos/login/debugging-access-tokens/"> Debugging an Access 518 * Token</a> 519 * </p> 520 * 521 * @param inputToken 522 * The Access Token to debug. 523 * 524 * @return A JsonObject containing the debug information for the accessToken. 525 * @since 1.6.13 526 */ 527 DebugTokenInfo debugToken(String inputToken); 528 529 /** 530 * Gets the {@code JsonMapper} used to convert Facebook JSON to Java objects. 531 * 532 * @return The {@code JsonMapper} used to convert Facebook JSON to Java objects. 533 * @since 1.6.7 534 */ 535 JsonMapper getJsonMapper(); 536 537 /** 538 * Gets the {@code WebRequestor} used to talk to the Facebook API endpoints. 539 * 540 * @return The {@code WebRequestor} used to talk to the Facebook API endpoints. 541 * @since 1.6.7 542 */ 543 WebRequestor getWebRequestor(); 544 545 /** 546 * generates an logout url 547 * 548 * @param next 549 * may be null, url the webpage should redirect after logout 550 * @return the logout url 551 * @since 1.9.0 552 */ 553 String getLogoutUrl(String next); 554 555 /** 556 * generates the login dialog url 557 * 558 * @param appId 559 * The ID of your app, found in your app's dashboard. 560 * @param redirectUri 561 * The URL that you want to redirect the person logging in back to. This URL will capture the response from 562 * the Login Dialog. If you are using this in a webview within a desktop app, this must be set to 563 * <code>https://www.facebook.com/connect/login_success.html</code>. 564 * @param scope 565 * List of Permissions to request from the person using your app. 566 * @param additionalParameters 567 * List of additional parameters 568 * @since 1.9.0 569 * @return the login dialog url 570 */ 571 String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, Parameter... additionalParameters); 572 573 /** 574 * Represents an access token/expiration date pair. 575 * <p> 576 * Facebook returns these types when performing access token-related operations - see 577 * {@link com.restfb.FacebookClient#convertSessionKeysToAccessTokens(String, String, String...)}, 578 * {@link com.restfb.FacebookClient#obtainAppAccessToken(String, String)}, and 579 * {@link com.restfb.FacebookClient#obtainExtendedAccessToken(String, String, String)} for details. 580 * 581 * @author <a href="http://restfb.com">Mark Allen</a> 582 */ 583 class AccessToken { 584 @Facebook("access_token") 585 private String accessToken; 586 587 @Facebook("expires_in") 588 private Long rawExpires; 589 590 private Long expires; 591 592 @Facebook("token_type") 593 private String tokenType; 594 595 private FacebookClient client; 596 597 public void setClient(FacebookClient client) { 598 this.client = client; 599 } 600 601 public FacebookClient getClient() { 602 return Optional.ofNullable(client).orElse(null); 603 } 604 605 /** 606 * Given a query string of the form {@code access_token=XXX} or {@code access_token=XXX&expires=YYY}, return an 607 * {@code AccessToken} instance. 608 * <p> 609 * The {@code queryString} is required to contain an {@code access_token} parameter with a non-{@code null} value. 610 * The {@code expires} value is optional and should be the number of seconds since the epoch. If the {@code expires} 611 * value cannot be parsed, the returned {@code AccessToken} will have a {@code null} {@code expires} value. 612 * 613 * @param queryString 614 * The Facebook query string out of which to parse an {@code AccessToken} instance. 615 * @return An {@code AccessToken} instance which corresponds to the given {@code queryString}. 616 * @throws IllegalArgumentException 617 * If no {@code access_token} parameter is present in the query string. 618 * @since 1.6.10 619 */ 620 public static AccessToken fromQueryString(String queryString) { 621 // Query string can be of the form 'access_token=XXX' or 622 // 'access_token=XXX&expires=YYY' 623 Map<String, List<String>> urlParameters = extractParametersFromQueryString(queryString); 624 625 String extendedAccessToken = null; 626 String tokenType = null; 627 628 if (urlParameters.containsKey("access_token")) { 629 extendedAccessToken = urlParameters.get("access_token").get(0); 630 } 631 632 if (urlParameters.containsKey("token_type")) { 633 tokenType = urlParameters.get("token_type").get(0); 634 } 635 636 if (extendedAccessToken == null) { 637 throw new IllegalArgumentException(format( 638 "Was expecting a query string of the form 'access_token=XXX' or 'access_token=XXX&expires=YYY'. Instead, the query string was '%s'", 639 queryString)); 640 } 641 642 Long expires = null; 643 644 // If an expires or expires_in value was provided and it's a valid long, great - use it. 645 // Otherwise ignore it. 646 String rawExpires = null; 647 648 if (urlParameters.containsKey("expires")) { 649 rawExpires = urlParameters.get("expires").get(0); 650 } 651 652 if (urlParameters.containsKey("expires_in")) { 653 rawExpires = urlParameters.get("expires_in").get(0); 654 } 655 656 if (rawExpires != null && rawExpires.trim().matches("\\d+")) { 657 expires = new Date().getTime() + Long.parseLong(rawExpires) * 1000L; 658 } 659 660 AccessToken accessToken = new AccessToken(); 661 accessToken.accessToken = extendedAccessToken; 662 accessToken.expires = expires; 663 accessToken.tokenType = tokenType; 664 return accessToken; 665 } 666 667 @Override 668 public int hashCode() { 669 return ReflectionUtils.hashCode(this); 670 } 671 672 @Override 673 public boolean equals(Object that) { 674 return ReflectionUtils.equals(this, that); 675 } 676 677 @Override 678 public String toString() { 679 return ReflectionUtils.toString(this); 680 } 681 682 /** 683 * The access token's value. 684 * 685 * @return The access token's value. 686 */ 687 public String getAccessToken() { 688 return accessToken; 689 } 690 691 /** 692 * The date on which the access token expires. 693 * 694 * @return The date on which the access token expires. 695 */ 696 public Date getExpires() { 697 return expires == null ? null : new Date(expires); 698 } 699 700 /** 701 * The token type of this access token provided by Facebook 702 * 703 * @return the access token type 704 */ 705 public String getTokenType() { 706 return tokenType; 707 } 708 709 @JsonMapper.JsonMappingCompleted 710 void convertExpires() { 711 if (rawExpires != null) { 712 expires = new Date().getTime() + 1000L * rawExpires; 713 } 714 } 715 716 } 717 718 /** 719 * <p> 720 * Represents the result of a {@link FacebookClient#debugToken(String)} inquiry. 721 * </p> 722 * 723 * FIXME does this class belong here? 724 * 725 * <p> 726 * See <a href="https://developers.facebook.com/docs/howtos/login/debugging-access-tokens/">Debug access tokens</a> 727 * 728 * @author Broc Seib 729 */ 730 class DebugTokenInfo extends AbstractFacebookType { 731 732 private static final long serialVersionUID = 1L; 733 734 /** 735 * The ID of the application this access token is for. 736 */ 737 @Facebook("app_id") 738 private String appId; 739 740 /** 741 * Name of the application this access token is for. 742 */ 743 @Facebook 744 private String application; 745 746 /** 747 * Timestamp when this access token expires. 748 */ 749 @Facebook("expires_at") 750 private Date expiresAt; 751 752 /** 753 * Timestamp when app's access to user data expires. 754 */ 755 @Facebook("data_access_expires_at") 756 private Date dataAccessExpiresAt; 757 758 /** 759 * Timestamp when this access token was issued. 760 */ 761 @Facebook("issued_at") 762 private Date issuedAt; 763 764 /** 765 * Whether the access token is still valid or not. 766 */ 767 @Facebook("is_valid") 768 private Boolean isValid; 769 770 /** 771 * The ID of the user this access token is for. 772 */ 773 @Facebook("user_id") 774 private String userId; 775 776 /** 777 * For impersonated access tokens, the ID of the page this token contains. 778 */ 779 @Facebook("profile_id") 780 private String profileId; 781 782 /** 783 * General metadata associated with the access token. Can contain data like 'sso', 'auth_type', 'auth_nonce' 784 */ 785 @Facebook 786 private JsonObject metadata; 787 788 /** 789 * Any error that a request to the graph api would return due to the access token. 790 */ 791 @Facebook 792 private DebugTokenError error; 793 794 /** 795 * List of permissions that the user has granted for the app in this access token. 796 */ 797 @Facebook 798 private List<String> scopes = new ArrayList<>(); 799 800 /** 801 * List of granular permissions that the user has granted for this app in this access token. 802 */ 803 @Facebook("granular_scopes") 804 private List<GranularScope> granularScopes = new ArrayList<>(); 805 806 @Facebook 807 private String type; 808 809 /** 810 * The application id. 811 * 812 * @return The id of the application. 813 */ 814 public String getAppId() { 815 return appId; 816 } 817 818 /** 819 * The application name. 820 * 821 * @return The name of the application. 822 */ 823 public String getApplication() { 824 return application; 825 } 826 827 /** 828 * The date on which the access token expires. 829 * 830 * @return The date on which the access token expires. 831 */ 832 public Date getExpiresAt() { 833 return expiresAt; 834 } 835 836 /** 837 * Timestamp when app's access to user data expires. 838 * 839 * @return The date when app's access to user data expires. 840 */ 841 public Date getDataAccessExpiresAt() { 842 return dataAccessExpiresAt; 843 } 844 845 /** 846 * The date on which the access token was issued. 847 * 848 * @return The date on which the access token was issued. 849 */ 850 public Date getIssuedAt() { 851 return issuedAt; 852 } 853 854 /** 855 * Whether or not the token is valid. 856 * 857 * @return Whether or not the token is valid. 858 */ 859 public Boolean isValid() { 860 return isValid; 861 } 862 863 /** 864 * The user id. 865 * 866 * @return The user id. 867 */ 868 public String getUserId() { 869 return userId; 870 } 871 872 /** 873 * List of scopes the access token 'contains' 874 * 875 * @return list of scopes 876 */ 877 public List<String> getScopes() { 878 return unmodifiableList(scopes); 879 } 880 881 /** 882 * List of granular scopes the access token 'contains' 883 * 884 * @return list of granular scopes 885 */ 886 public List<GranularScope> getGranularScopes() { 887 return unmodifiableList(granularScopes); 888 } 889 890 /** 891 * General metadata associated with the access token. Can contain data like 'sso', 'auth_type', 'auth_nonce' 892 * 893 * @return General metadata associated with the access token 894 */ 895 public JsonObject getMetaData() { 896 return metadata; 897 } 898 899 /** 900 * All Error data associated with access token debug. 901 * 902 * @return debug token error 903 */ 904 public DebugTokenError getDebugTokenError() { 905 return error; 906 } 907 908 public String getType() { 909 return type; 910 } 911 } 912 913 class GranularScope extends AbstractFacebookType { 914 915 private static final long serialVersionUID = 1L; 916 917 /** 918 * The permission granted by the user. 919 */ 920 @Facebook 921 private String scope; 922 923 /** 924 * The target ids of Pages, Groups, or business assets the user granted the above permission for. 925 */ 926 @Facebook("target_ids") 927 private List<String> targetIds = new ArrayList<>(); 928 929 /** 930 * The permission granted by the user. 931 * 932 * @return The permission granted by the user. 933 */ 934 public String getScope() { 935 return scope; 936 } 937 938 /** 939 * The target ids of Pages, Groups, or business assets the user granted the above permission for. 940 * 941 * @return The target ids of Pages, Groups, or business assets the user granted the above permission for. 942 */ 943 public List<String> getTargetIds() { 944 return unmodifiableList(targetIds); 945 } 946 } 947 948 class DebugTokenError extends AbstractFacebookType { 949 950 private static final long serialVersionUID = 1L; 951 952 /** 953 * The error code for the error. 954 */ 955 @Facebook 956 private Integer code; 957 958 /** 959 * The error message for the error. 960 */ 961 @Facebook 962 private String message; 963 964 /** 965 * The error subcode for the error. 966 */ 967 @Facebook 968 private Integer subcode; 969 970 /** 971 * The error code for the error. 972 * 973 * @return The error code for the error. 974 */ 975 public Integer getCode() { 976 return code; 977 } 978 979 /** 980 * The error message for the error. 981 * 982 * @return The error message for the error. 983 */ 984 public String getMessage() { 985 return message; 986 } 987 988 /** 989 * The error subcode for the error. 990 * 991 * @return The error subcode for the error. 992 */ 993 public Integer getSubcode() { 994 return subcode; 995 } 996 997 } 998}