OneDriveAuthManager.java

/*
 * onedrive-java-sdk - A Java SDK to access OneDrive drives and files.
 * Copyright © 2023-2025 Andy Miles (andy.miles@amilesend.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.amilesend.onedrive.connection.auth;

import com.amilesend.client.connection.auth.AuthManager;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import static com.google.common.net.HttpHeaders.AUTHORIZATION;
import static com.google.common.net.MediaType.FORM_DATA;

/**
 *  The interface that defines the manager that is responsible for obtaining and refreshing tokens
 *  to interact with a OneDrive account. Note: This does not manage the initial stages of the OAUTH request
 *  flow and instead relies on a provided auth code or a pre-existing refresh token.
 */
public interface OneDriveAuthManager extends AuthManager<OneDriveAuthInfo> {
    String AUTH_TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
    String FORM_DATA_CONTENT_TYPE = FORM_DATA.toString();
    String CLIENT_ID_BODY_PARAM = "client_id";
    String CLIENT_SECRET_BODY_PARAM = "client_secret";
    String REDIRECT_URI_BODY_PARAM = "redirect_uri";
    String AUTH_CODE_BODY_ARAM = "code";
    String REFRESH_TOKEN_BODY_PARAM = "refresh_token";
    String GRANT_TYPE_BODY_PARAM = "grant_type";
    String AUTH_CODE_GRANT_TYPE_BODY_PARAM_VALUE = "authorization_code";
    String REFRESH_TOKEN_GRANT_TYPE_BODY_PARAM_VALUE = REFRESH_TOKEN_BODY_PARAM;

    @Override
    default Request.Builder addAuthentication(final Request.Builder requestBuilder) {
        return requestBuilder.addHeader(AUTHORIZATION, refreshIfExpiredAndFetchFullToken());
    }

    /**
     * Determines if the current authentication information is expired.
     *
     * @return {@code true} if expired; else, {@code false}
     */
    boolean isExpired();

    /**
     * Checks to see if the current authentication info is expired and refreshes the tokens
     * accordingly.
     */
    default void refreshIfExpired() {
        if (isExpired()) {
            refreshToken();
        }
    }

    /**
     * Helper method to refresh authentication if expired and return the full token used in request headers.
     *
     * @return the full auth token
     */
    default String refreshIfExpiredAndFetchFullToken() {
        refreshIfExpired();
        return getAuthInfo().getFullToken();
    }

    /** Retrieves the associated endpoint to use for OneDrive operations. */
    String getAuthenticatedEndpoint();

    /**
     * Issues a request to redeem the given {@code authCode} in order to retrieve access and refresh tokens as a
     * {@link OneDriveAuthInfo}.
     *
     * @param authCode the authorization code
     * @return the authorization information
     * @see OneDriveAuthInfo
     */
    OneDriveAuthInfo redeemToken(String authCode);

    /**
     * Issues a request to refresh the auth tokens and returns the refreshed tokens as a {@link OneDriveAuthInfo}.
     *
     * @return the authorization information
     * @see OneDriveAuthInfo
     */
    OneDriveAuthInfo refreshToken();

    /**
     * Helper method to dispatch the request to redeem or refresh authorization tokens.
     *
     * @param httpClient the http client
     * @param request the request
     * @return the authorization information
     */
    static OneDriveAuthInfo fetchAuthInfo(final OkHttpClient httpClient, final Request request) {
        try {
            try (final Response response = httpClient.newCall(request).execute()) {
                if (!response.isSuccessful()) {
                    throw new AuthManagerException("Unsuccessful refresh token request: " + response);
                }

                final String json = response.body().string();
                return OneDriveAuthInfo.fromJson(json);
            }
        } catch (final AuthManagerException ex) {
            throw ex;
        } catch (final Exception ex) {
            throw new AuthManagerException("Error refreshing token: " + ex.getMessage(), ex);
        }
    }
}