001/* 002 * Copyright (c) 2010-2024 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.ObjectUtil.verifyParameterPresence; 025 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.List; 029 030import com.restfb.exception.FacebookResponseContentException; 031import com.restfb.scope.ScopeBuilder; 032 033/** 034 * The default implementation to work with the Instagram Basic Display API. 035 * <p> 036 * it is used for this Instagram API and for the Threads API. This API is accordingly to the reference based on the 037 * Instagram Basic Display API. 038 */ 039public class DefaultInstagramClient extends DefaultFacebookClient { 040 041 public DefaultInstagramClient(Version version) { 042 super(version); 043 } 044 045 public DefaultInstagramClient(String accessToken, Version apiVersion) { 046 super(accessToken, apiVersion); 047 } 048 049 public DefaultInstagramClient(String accessToken, String appSecret, Version apiVersion) { 050 super(accessToken, appSecret, apiVersion); 051 } 052 053 public DefaultInstagramClient(String accessToken, WebRequestor webRequestor, JsonMapper jsonMapper, 054 Version apiVersion) { 055 super(accessToken, webRequestor, jsonMapper, apiVersion); 056 } 057 058 public DefaultInstagramClient(String accessToken, String appSecret, WebRequestor webRequestor, JsonMapper jsonMapper, 059 Version apiVersion) { 060 super(accessToken, appSecret, webRequestor, jsonMapper, apiVersion); 061 } 062 063 @Override 064 public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, String state, 065 Parameter... parameters) { 066 List<Parameter> parameterList = new ArrayList<>(); 067 Collections.addAll(parameterList, parameters); 068 parameterList.add(Parameter.with("response_type", CODE)); 069 return getGenericLoginDialogUrl(appId, redirectUri, scope, 070 () -> getFacebookEndpointUrls().getInstagramApiEndpoint() + "/oauth/authorize", state, parameterList); 071 } 072 073 @Override 074 public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, Parameter... parameters) { 075 return getLoginDialogUrl(appId, redirectUri, scope, null, parameters); 076 } 077 078 @Override 079 public AccessToken obtainUserAccessToken(String clientId, String clientSecret, String redirectUri, String code) { 080 verifyParameterPresence(CLIENT_ID, clientId); 081 verifyParameterPresence(PARAM_CLIENT_SECRET, clientSecret); 082 verifyParameterPresence(CODE, code); 083 verifyParameterPresence(REDIRECT_URI, redirectUri); 084 085 return publish(PATH_OAUTH_ACCESS_TOKEN, AccessToken.class, // 086 Parameter.with(CLIENT_ID, clientId), // 087 Parameter.with(PARAM_CLIENT_SECRET, clientSecret), // 088 Parameter.with(CODE, code), // 089 Parameter.with(GRANT_TYPE, "authorization_code"), // 090 Parameter.with(REDIRECT_URI, redirectUri)); 091 } 092 093 @Override 094 public AccessToken obtainExtendedAccessToken(String appId, String appSecret, String accessToken) { 095 throw new UnsupportedOperationException("Not supported, use the other obtainExtendedAccessToken instead"); 096 } 097 098 @Override 099 public AccessToken obtainExtendedAccessToken(String appId, String appSecret) { 100 verifyParameterPresence(APP_SECRET, appSecret); 101 verifyParameterPresence("accessToken", accessToken); 102 103 String response = makeRequest("access_token", false, false, null, // 104 Parameter.with(PARAM_CLIENT_SECRET, appSecret), // 105 Parameter.with(GRANT_TYPE, "ig_exchange_token"), // 106 Parameter.withFields("access_token,expires_in,token_type")); 107 108 try { 109 return getAccessTokenFromResponse(response); 110 } catch (Exception t) { 111 throw new FacebookResponseContentException(CANNOT_EXTRACT_ACCESS_TOKEN_MESSAGE, t); 112 } 113 } 114 115 /** 116 * Obtain a refreshed Instagram extended access token. 117 * 118 * <p> 119 * This method is used to refresh an existing Instagram extended access token. Extended access tokens expire after a 120 * certain period of time, and this method allows you to obtain a new one using the refresh token provided with the 121 * original extended access token. 122 * 123 * @return A new {@link AccessToken} object containing the refreshed access token, expiration time, and token type. 124 * @throws FacebookResponseContentException 125 * If the response from the Facebook API cannot be parsed or if the access token cannot be extracted from 126 * the response. 127 */ 128 @Override 129 public AccessToken obtainRefreshedExtendedAccessToken() { 130 String response = makeRequest("refresh_access_token", false, false, null, // 131 Parameter.with(GRANT_TYPE, "ig_refresh_token"), // 132 Parameter.withFields("access_token,expires_in,token_type")); 133 try { 134 return getAccessTokenFromResponse(response); 135 } catch (Exception t) { 136 throw new FacebookResponseContentException(CANNOT_EXTRACT_ACCESS_TOKEN_MESSAGE, t); 137 } 138 } 139 140 @Override 141 public FacebookClient createClientWithAccessToken(String accessToken) { 142 return new DefaultInstagramClient(accessToken, this.appSecret, getWebRequestor(), getJsonMapper(), this.apiVersion); 143 } 144 145 @Override 146 protected String createBaseUrlForEndpoint(String apiCall, boolean hasAttachment, boolean hasReel) { 147 String baseUrl = getInstagramGraphEndpointUrl(); 148 if (apiCall.startsWith("oauth")) { 149 baseUrl = getFacebookEndpointUrls().getInstagramApiEndpoint(); 150 } 151 152 return baseUrl; 153 } 154 155 private String getInstagramGraphEndpointUrl() { 156 if (apiVersion.isUrlElementRequired()) { 157 return getFacebookEndpointUrls().getInstagramEndpoint() + '/' + apiVersion.getUrlElement(); 158 } else { 159 return getFacebookEndpointUrls().getInstagramEndpoint(); 160 } 161 } 162}