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 com.restfb.exception.FacebookResponseContentException;
025import com.restfb.scope.ScopeBuilder;
026
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.List;
030
031import static com.restfb.util.ObjectUtil.verifyParameterPresence;
032
033/**
034 * InstagramLoginClient is the default implementation of the
035 * <a href="https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login">
036 * Instagram API with Instagram Login</a>.
037 * <p>
038 * This client is used to use a instagram Login to get a access token, extended access token and so on for the Instagram
039 * API.
040 * <p>
041 * Don't use the client if you are planning to use the Facebbok Login to work with the Instagram API.
042 */
043public class DefaultInstagramLoginClient extends DefaultFacebookClient {
044
045  public DefaultInstagramLoginClient(Version version) {
046    super(version);
047  }
048
049  public DefaultInstagramLoginClient(String accessToken, Version apiVersion) {
050    super(accessToken, apiVersion);
051  }
052
053  public DefaultInstagramLoginClient(String accessToken, String appSecret, Version apiVersion) {
054    super(accessToken, appSecret, apiVersion);
055  }
056
057  public DefaultInstagramLoginClient(String accessToken, WebRequestor webRequestor, JsonMapper jsonMapper,
058      Version apiVersion) {
059    super(accessToken, webRequestor, jsonMapper, apiVersion);
060  }
061
062  public DefaultInstagramLoginClient(String accessToken, String appSecret, WebRequestor webRequestor,
063      JsonMapper jsonMapper, Version apiVersion) {
064    super(accessToken, appSecret, webRequestor, jsonMapper, apiVersion);
065  }
066
067  @Override
068  public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, String state,
069      Parameter... parameters) {
070    List<Parameter> parameterList = new ArrayList<>();
071    Collections.addAll(parameterList, parameters);
072    parameterList.add(Parameter.with("response_type", CODE));
073    return getGenericLoginDialogUrl(appId, redirectUri, scope,
074      () -> getFacebookEndpointUrls().getInstagramOAuthEndpoint() + "/oauth/authorize", state, parameterList);
075  }
076
077  @Override
078  public String getLoginDialogUrl(String appId, String redirectUri, ScopeBuilder scope, Parameter... parameters) {
079    return this.getLoginDialogUrl(appId, redirectUri, scope, null, parameters);
080  }
081
082  @Override
083  public AccessToken obtainUserAccessToken(String clientId, String clientSecret, String redirectUri, String code) {
084    verifyParameterPresence(CLIENT_ID, clientId);
085    verifyParameterPresence(PARAM_CLIENT_SECRET, clientSecret);
086    verifyParameterPresence(CODE, code);
087    verifyParameterPresence(REDIRECT_URI, redirectUri);
088
089    return publish(PATH_OAUTH_ACCESS_TOKEN, AccessToken.class, //
090      Parameter.with(CLIENT_ID, clientId), //
091      Parameter.with(PARAM_CLIENT_SECRET, clientSecret), //
092      Parameter.with(CODE, code), //
093      Parameter.with(GRANT_TYPE, "authorization_code"), //
094      Parameter.with(REDIRECT_URI, redirectUri));
095  }
096
097  @Override
098  public AccessToken obtainExtendedAccessToken(String appId, String appSecret) {
099    verifyParameterPresence(APP_SECRET, appSecret);
100    verifyParameterPresence("accessToken", accessToken);
101
102    String response = makeRequest("access_token", false, false, null, //
103      Parameter.with(PARAM_CLIENT_SECRET, appSecret), //
104      Parameter.with(GRANT_TYPE, "ig_exchange_token"), //
105      Parameter.withFields("access_token,expires_in,token_type"));
106    try {
107      return getAccessTokenFromResponse(response);
108    } catch (Exception t) {
109      throw new FacebookResponseContentException(CANNOT_EXTRACT_ACCESS_TOKEN_MESSAGE, t);
110    }
111  }
112
113  @Override
114  public FacebookClient createClientWithAccessToken(String accessToken) {
115    return new DefaultInstagramLoginClient(accessToken, this.appSecret, getWebRequestor(), getJsonMapper(),
116      this.apiVersion);
117  }
118
119  @Override
120  public AccessToken obtainRefreshedExtendedAccessToken() {
121    String response = makeRequest("refresh_access_token", false, false, null, //
122      Parameter.with(GRANT_TYPE, "ig_refresh_token"), //
123      Parameter.withFields("access_token,expires_in,token_type"));
124    try {
125      return getAccessTokenFromResponse(response);
126    } catch (Exception t) {
127      throw new FacebookResponseContentException(CANNOT_EXTRACT_ACCESS_TOKEN_MESSAGE, t);
128    }
129  }
130
131  @Override
132  protected String createBaseUrlForEndpoint(String apiCall, boolean hasAttachment, boolean hasReel) {
133    if (apiCall.startsWith(PATH_OAUTH_ACCESS_TOKEN)) {
134      return getInstagramApiEndpointUrl();
135    }
136    return getInstagramGraphEndpointUrl();
137  }
138
139  private String getInstagramApiEndpointUrl() {
140    return getFacebookEndpointUrls().getInstagramApiEndpoint();
141  }
142
143  private String getInstagramGraphEndpointUrl() {
144    if (apiVersion.isUrlElementRequired()) {
145      return getFacebookEndpointUrls().getInstagramEndpoint() + '/' + apiVersion.getUrlElement();
146    } else {
147      return getFacebookEndpointUrls().getInstagramEndpoint();
148    }
149  }
150}