OneDriveAuthInfo.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.AuthInfo;
import com.amilesend.onedrive.parse.GsonFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;
/**
* Represents the authentication information for OAuth (OBO) access to a user's MS OneDrive account.
* This is returned in response when making calls to acquire the initial token or when refreshing the token.
* <p>
* Note: For business accounts, the auth tokens are only valid with a single resource. If
* the user is to access a different resource (different service/site), then new access tokens are required.
* See {@link BusinessAccountAuthManager} for more information.
* <p>
* <a href="https://learn.microsoft.com/en-us/graph/auth-v2-user?tabs=http">Official documentation</a>
*/
@Slf4j
@Builder
@Getter
@EqualsAndHashCode
public class OneDriveAuthInfo implements AuthInfo {
private static final String STANDARD_SPACE = " ";
private static final String URL_ENCODED_SPACE = "%20";
/** The auth token type. Default is {@code Bearer}. */
@Builder.Default
private final String tokenType = "Bearer";
/** The list of scopes, or permissions to access the Graph API. */
@Builder.Default
private final List<String> scopes = Collections.emptyList();
/** Time in milliseconds when the auth token expires. */
private final long expiresIn;
/** Time in milliseconds when the auth token expires. */
private final long extExpiresIn;
/** The current authorization token used to make API requests. */
@NonNull
private final String accessToken;
/** The current refresh token used to refresh access tokens. */
@NonNull
private final String refreshToken;
/** The associated resource identifier associated with the access tokens (business accounts only). */
@Builder.Default
private final String resourceId = StringUtils.EMPTY;
/**
* Deserializes the given {@code authInfoJson} string to a new {@code AuthInfo} object.
*
* @param authInfoJson the JSON-formatted auth info
* @return the new {@code AuthInfo} object
* @throws JsonSyntaxException if there is an error while deserializing the JSON string
*/
public static OneDriveAuthInfo fromJson(final String authInfoJson) {
final Gson gson = GsonFactory.getInstanceForAuthManager();
final AuthInfoInternal internalAuthInfo = gson.fromJson(authInfoJson, AuthInfoInternal.class);
return OneDriveAuthInfo.builder()
.accessToken(internalAuthInfo.getAccessToken())
.expiresIn(internalAuthInfo.getExpiresIn())
.extExpiresIn(internalAuthInfo.getExtExpiresIn())
.refreshToken(internalAuthInfo.getRefreshToken())
.resourceId(internalAuthInfo.getResourceId())
.scopes(fromScope(internalAuthInfo.getScope()))
.tokenType(internalAuthInfo.getTokenType())
.build();
}
/**
* Gets the full token that is used in request headers to sign API requests.
* Full token strings are formatted as {@code "[Token Type] [Access token]"}
*
* @return the full token
*/
public String getFullToken() {
return new StringJoiner(STANDARD_SPACE)
.add(tokenType)
.add(accessToken)
.toString();
}
/**
* Serializes this {@code AuthInfo} to a JSON formatted string.
*
* @return the JSON formatted {@code AuthInfo}
*/
public String toJson() {
final Gson gson = GsonFactory.getInstanceForAuthManager();
final AuthInfoInternal internalAuthInfo = AuthInfoInternal.builder()
.accessToken(accessToken)
.expiresIn(expiresIn)
.extExpiresIn(extExpiresIn)
.refreshToken(refreshToken)
.resourceId(resourceId)
.scope(toScope(scopes))
.tokenType(tokenType)
.build();
return gson.toJson(internalAuthInfo);
}
/**
* Creates a copy of this object while injecting the given {@code resourceId}.
*
* @param resourceId the resource identifier associated with the auth tokens
* @return the copy
*/
public OneDriveAuthInfo copyWithResourceId(final String resourceId) {
return OneDriveAuthInfo.builder()
.accessToken(getAccessToken())
.expiresIn(getExpiresIn())
.extExpiresIn(getExtExpiresIn())
.refreshToken(getRefreshToken())
.resourceId(resourceId)
.scopes(getScopes())
.tokenType(getTokenType())
.build();
}
private static String toScope(final List<String> scopes) {
if (CollectionUtils.isEmpty(scopes)) {
return StringUtils.EMPTY;
}
return String.join(URL_ENCODED_SPACE, scopes);
}
private static List<String> fromScope(final String scope) {
if (StringUtils.isBlank(scope)) {
return Collections.emptyList();
}
return Collections.unmodifiableList(
Arrays.stream(scope.split(" "))
.map(String::trim)
.collect(Collectors.toList()));
}
/**
* Internal representation that is actually serialized and deserialized to/from JSON. This is to serialize
* the list of scopes to a space-delimited string used with API resource types that require it.
*/
@Builder
@Getter
private static class AuthInfoInternal {
/** The type of token. */
private final String tokenType;
/** The scopes associated with the authentication. */
private final String scope;
/** The expiration time in milliseconds. */
private final long expiresIn;
/** The expiration time in milliseconds. */
private final long extExpiresIn;
/** The current access token. */
private final String accessToken;
/** the current refresh token. */
private final String refreshToken;
/** The associated resource identifier associated with the access tokens (business accounts only). */
private final String resourceId;
}
}