msgraph-sdk-java icon indicating copy to clipboard operation
msgraph-sdk-java copied to clipboard

Configuring GraphServiceClient with no scope throws UnsupportedOperationException during runtime

Open joakibj opened this issue 1 year ago • 0 comments

Expected behavior

Initializing GraphServiceClient with no scope argument should default to scope https://graph.microsoft.com/.default when using azure-identity TokenCredential and fetch the token

Actual behavior

Attempt to fetch Azure token fails with UnsupportedOperationException during runtime

Steps to reproduce the behavior

Java version:

openjdk version "17.0.10" 2024-01-16
OpenJDK Runtime Environment Temurin-17.0.10+7 (build 17.0.10+7)
OpenJDK 64-Bit Server VM Temurin-17.0.10+7 (build 17.0.10+7, mixed mode, sharing)

Include on classpath:

<dependency>
	<groupId>com.microsoft.graph</groupId>
	<artifactId>microsoft-graph</artifactId>
	<version>6.4.0</version>
</dependency>
<dependency>
	<groupId>com.azure</groupId>
	<artifactId>azure-identity</artifactId>
	<version>1.11.3</version>
</dependency>

Implement the following code:

public class MsGraphConsumer {
	private final GraphServiceClient graphClient;

	public MsGraphConsumer(AzureProperties azureProperties) {
		ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
				.tenantId(azureProperties.appTenantId())
				.clientId(azureProperties.appClientId())
				.clientSecret(azureProperties.appClientSecret())
				.build();
		this.graphClient = new GraphServiceClient(clientSecretCredential);
	}

	public Optional<User> getUser(String orgInternalId) {
		try {
			List<User> res = graphClient
					.users()
					.get(requestConfiguration -> {
						requestConfiguration.headers.add("ConsistencyLevel", "eventual");
						requestConfiguration.queryParameters.filter = "onPremisesSamAccountName eq '" + orgInternalId + "'";
						requestConfiguration.queryParameters.count = true;
						requestConfiguration.queryParameters.select = new String[]{"id"};
					}).getValue();
			if (res.isEmpty()) {
				return Optional.empty();
			}
			return Optional.of(res.get(0));
		} catch (ApiException e) {
			throw e;
		}
	}
}

Execute the method msGraphConsumer.getUser("hello"). Observe the following stacktrace:

java.lang.UnsupportedOperationException: null
	at java.base/java.util.AbstractList.add(AbstractList.java:153)
	at java.base/java.util.AbstractList.add(AbstractList.java:111)
	at com.microsoft.kiota.authentication.AzureIdentityAccessTokenProvider.getAuthorizationToken(AzureIdentityAccessTokenProvider.java:133)
	at com.microsoft.kiota.authentication.BaseBearerTokenAuthenticationProvider.authenticateRequest(BaseBearerTokenAuthenticationProvider.java:46)
	at com.microsoft.kiota.http.OkHttpRequestAdapter.getHttpResponseMessage(OkHttpRequestAdapter.java:709)
	at com.microsoft.kiota.http.OkHttpRequestAdapter.send(OkHttpRequestAdapter.java:274)
	at com.microsoft.graph.users.UsersRequestBuilder.get(UsersRequestBuilder.java:120)
	at com.github.example.MsGraphConsumer.getUser(MsGraphConsumer.java:53)

Workaround

Set the scope directly or initialise scopes as null:

new GraphServiceClient(clientSecretCredential, "https://graph.microsoft.com/.default");
new GraphServiceClient(clientSecretCredential,  null);

Authors remarks

Looks like this is due to com.microsoft.kiota.authentication.AzureIdentityAccessTokenProvider:66 assigns scopes using the Arrays.ArrayList type that does not implement add

joakibj avatar Mar 11 '24 11:03 joakibj