001package com.box.sdkgen.box.oauth;
002
003import static com.box.sdkgen.internal.utils.UtilsManager.entryOf;
004import static com.box.sdkgen.internal.utils.UtilsManager.mapOf;
005import static com.box.sdkgen.internal.utils.UtilsManager.prepareParams;
006import static com.box.sdkgen.serialization.json.JsonManager.sdToUrlParams;
007
008import com.box.sdkgen.box.errors.BoxSDKError;
009import com.box.sdkgen.box.tokenstorage.TokenStorage;
010import com.box.sdkgen.managers.authorization.AuthorizationManager;
011import com.box.sdkgen.networking.auth.Authentication;
012import com.box.sdkgen.networking.network.NetworkSession;
013import com.box.sdkgen.schemas.accesstoken.AccessToken;
014import com.box.sdkgen.schemas.postoauth2revoke.PostOAuth2Revoke;
015import com.box.sdkgen.schemas.postoauth2token.PostOAuth2Token;
016import com.box.sdkgen.schemas.postoauth2token.PostOAuth2TokenGrantTypeField;
017import com.box.sdkgen.schemas.postoauth2token.PostOAuth2TokenSubjectTokenTypeField;
018import com.box.sdkgen.serialization.json.JsonManager;
019import java.util.List;
020import java.util.Map;
021
022public class BoxOAuth implements Authentication {
023
024  /** Configuration object of OAuth. */
025  public final OAuthConfig config;
026
027  /**
028   * An object responsible for storing token. If no custom implementation provided, the token will
029   * be stored in memory.
030   */
031  public final TokenStorage tokenStorage;
032
033  public BoxOAuth(OAuthConfig config) {
034    this.config = config;
035    this.tokenStorage = this.config.getTokenStorage();
036  }
037
038  /** Get the authorization URL for the app user. */
039  public String getAuthorizeUrl() {
040    return getAuthorizeUrl(new GetAuthorizeUrlOptions());
041  }
042
043  /**
044   * Get the authorization URL for the app user.
045   *
046   * @param options The options parameter
047   */
048  public String getAuthorizeUrl(GetAuthorizeUrlOptions options) {
049    Map<String, String> paramsMap =
050        prepareParams(
051            mapOf(
052                entryOf(
053                    "client_id",
054                    (!(options.getClientId() == null)
055                        ? options.getClientId()
056                        : this.config.getClientId())),
057                entryOf(
058                    "response_type",
059                    (!(options.getResponseType() == null) ? options.getResponseType() : "code")),
060                entryOf("redirect_uri", options.getRedirectUri()),
061                entryOf("state", options.getState()),
062                entryOf("scope", options.getScope())));
063    return String.join(
064        "",
065        "https://account.box.com/api/oauth2/authorize?",
066        sdToUrlParams(JsonManager.serialize(paramsMap)));
067  }
068
069  /**
070   * Acquires token info using an authorization code.
071   *
072   * @param authorizationCode The authorization code to use to get tokens.
073   */
074  public AccessToken getTokensAuthorizationCodeGrant(String authorizationCode) {
075    return getTokensAuthorizationCodeGrant(authorizationCode, null);
076  }
077
078  /**
079   * Acquires token info using an authorization code.
080   *
081   * @param authorizationCode The authorization code to use to get tokens.
082   * @param networkSession An object to keep network session state
083   */
084  public AccessToken getTokensAuthorizationCodeGrant(
085      String authorizationCode, NetworkSession networkSession) {
086    AuthorizationManager authManager =
087        new AuthorizationManager.Builder()
088            .networkSession((!(networkSession == null) ? networkSession : new NetworkSession()))
089            .build();
090    AccessToken token =
091        authManager.requestAccessToken(
092            new PostOAuth2Token.Builder(PostOAuth2TokenGrantTypeField.AUTHORIZATION_CODE)
093                .code(authorizationCode)
094                .clientId(this.config.getClientId())
095                .clientSecret(this.config.getClientSecret())
096                .build());
097    this.tokenStorage.store(token);
098    return token;
099  }
100
101  /**
102   * Get the current access token. If the current access token is expired or not found, this method
103   * will attempt to refresh the token.
104   */
105  public AccessToken retrieveToken() {
106    return retrieveToken(null);
107  }
108
109  /**
110   * Get the current access token. If the current access token is expired or not found, this method
111   * will attempt to refresh the token.
112   *
113   * @param networkSession An object to keep network session state
114   */
115  @Override
116  public AccessToken retrieveToken(NetworkSession networkSession) {
117    AccessToken token = this.tokenStorage.get();
118    if (token == null) {
119      throw new BoxSDKError(
120          "Access and refresh tokens not available. Authenticate before making any API call first.");
121    }
122    return token;
123  }
124
125  /** Get a new access token for the platform app user. */
126  public AccessToken refreshToken() {
127    return refreshToken(null);
128  }
129
130  /**
131   * Get a new access token for the platform app user.
132   *
133   * @param networkSession An object to keep network session state
134   */
135  @Override
136  public AccessToken refreshToken(NetworkSession networkSession) {
137    AccessToken oldToken = this.tokenStorage.get();
138    String tokenUsedForRefresh = (!(oldToken == null) ? oldToken.getRefreshToken() : null);
139    AuthorizationManager authManager =
140        new AuthorizationManager.Builder()
141            .networkSession((!(networkSession == null) ? networkSession : new NetworkSession()))
142            .build();
143    AccessToken token =
144        authManager.requestAccessToken(
145            new PostOAuth2Token.Builder(PostOAuth2TokenGrantTypeField.REFRESH_TOKEN)
146                .clientId(this.config.getClientId())
147                .clientSecret(this.config.getClientSecret())
148                .refreshToken(tokenUsedForRefresh)
149                .build());
150    this.tokenStorage.store(token);
151    return token;
152  }
153
154  public String retrieveAuthorizationHeader() {
155    return retrieveAuthorizationHeader(null);
156  }
157
158  @Override
159  public String retrieveAuthorizationHeader(NetworkSession networkSession) {
160    AccessToken token = this.retrieveToken(networkSession);
161    return String.join("", "Bearer ", token.getAccessToken());
162  }
163
164  /**
165   * Revoke an active Access Token, effectively logging a user out that has been previously
166   * authenticated.
167   */
168  public void revokeToken() {
169    revokeToken(null);
170  }
171
172  /**
173   * Revoke an active Access Token, effectively logging a user out that has been previously
174   * authenticated.
175   *
176   * @param networkSession An object to keep network session state
177   */
178  @Override
179  public void revokeToken(NetworkSession networkSession) {
180    AccessToken token = this.tokenStorage.get();
181    if (token == null) {
182      return;
183    }
184    AuthorizationManager authManager =
185        new AuthorizationManager.Builder()
186            .networkSession((!(networkSession == null) ? networkSession : new NetworkSession()))
187            .build();
188    authManager.revokeAccessToken(
189        new PostOAuth2Revoke.Builder()
190            .clientId(this.config.getClientId())
191            .clientSecret(this.config.getClientSecret())
192            .token(token.getAccessToken())
193            .build());
194  }
195
196  /**
197   * Downscope access token to the provided scopes. Returning a new access token with the provided
198   * scopes, with the original access token unchanged.
199   *
200   * @param scopes The scope(s) to apply to the resulting token.
201   * @param resource The file or folder to get a downscoped token for. If None and shared_link None,
202   *     the resulting token will not be scoped down to just a single item. The resource should be a
203   *     full URL to an item, e.g. https://api.box.com/2.0/files/123456.
204   * @param sharedLink The shared link to get a downscoped token for. If None and item None, the
205   *     resulting token will not be scoped down to just a single item.
206   * @param networkSession An object to keep network session state
207   */
208  @Override
209  public AccessToken downscopeToken(
210      List<String> scopes, String resource, String sharedLink, NetworkSession networkSession) {
211    AccessToken token = this.retrieveToken(networkSession);
212    if (token == null || token.getAccessToken() == null) {
213      throw new BoxSDKError("No access token is available.");
214    }
215    AuthorizationManager authManager =
216        new AuthorizationManager.Builder()
217            .networkSession((!(networkSession == null) ? networkSession : new NetworkSession()))
218            .build();
219    AccessToken downscopedToken =
220        authManager.requestAccessToken(
221            new PostOAuth2Token.Builder(
222                    PostOAuth2TokenGrantTypeField.URN_IETF_PARAMS_OAUTH_GRANT_TYPE_TOKEN_EXCHANGE)
223                .subjectToken(token.getAccessToken())
224                .subjectTokenType(
225                    PostOAuth2TokenSubjectTokenTypeField
226                        .URN_IETF_PARAMS_OAUTH_TOKEN_TYPE_ACCESS_TOKEN)
227                .resource(resource)
228                .scope(String.join(" ", scopes))
229                .boxSharedLink(sharedLink)
230                .build());
231    return downscopedToken;
232  }
233
234  public TokenStorage getTokenStorage() {
235    return tokenStorage;
236  }
237}