Skip to content

Azure: add support to specify token credential provider - #14136

Merged
nastra merged 7 commits into
apache:mainfrom
munendrasn:azure-credential-provider
Sep 30, 2025
Merged

nastra merged 7 commits into
apache:mainfrom
munendrasn:azure-credential-provider

Conversation

@munendrasn

Copy link
Copy Markdown
Contributor

Similar to AWS, adls.token-credential-provider can be used to specify the TokenCredential provider.
This is useful where user can configure custom token credential or in-built TokenCredential impl like ClientSecretCredential - where specifying clientId,secret not possible via env variables like federation.

In built-one support auto-refresh of access-token on the expiry.

public class ClientSecretCredentialProvider implements AzureTokenCredentialProvider {

  private static final String AZURE_CLIENT_ID = "azure.client-id";
  private static final String AZURE_TENANT_ID = "azure.tenant-id";
  private static final String AZURE_CLIENT_SECRET = "azure.client-secret";

  private String clientId;
  private String clientSecret;
  private String tenantId;


  @Override
  public TokenCredential credential() {
    return new ClientSecretCredentialBuilder()
        .tenantId(tenantId)
        .clientId(clientId)
        .clientSecret(clientSecret)
        .build();
  }

  @Override
  public void initialize(Map<String, String> properties) {
    this.clientId = properties.get(AZURE_CLIENT_ID);
    this.clientSecret = properties.get(AZURE_CLIENT_SECRET);
    this.tenantId = properties.get(AZURE_TENANT_ID);
  }
}

@github-actions github-actions Bot added the AZURE label Sep 21, 2025
@munendrasn

Copy link
Copy Markdown
Contributor Author

@kevinjqliu
Post creatio of the PR, I found a PR from you for similar functinality #13829. Please let me know if this aligns with your original idea.

As of now, I have avoided including the ClientSecretCredential Provider in the PR.
This provides user to have custom providers based on certificate, or workload and so on without the need for each impl to be part of Iceberg

TokenCredential credential();

/**
* Initialize Azure credential factory from catalog properties.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of "factory" this should probably just say "Initialize the credential provider..."

*
* @param properties catalog properties
*/
void initialize(Map<String, String> properties);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to AWS we should probably only pass down properties that have a specific prefix, such as adls.token-credential-provider., so only adls.token-credential-provider.client_id would be passed to the credential provider but not any other catalog properties

import com.azure.core.credential.TokenCredential;
import java.util.Map;

public interface AzureTokenCredentialProvider {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public interface AzureTokenCredentialProvider {
public interface AdlsTokenCredentialProvider {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasoning is same as below - applicable for any azure services

import org.apache.iceberg.relocated.com.google.common.base.Strings;
import org.apache.iceberg.util.PropertyUtil;

public class AzureTokenCredentialProviders {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public class AzureTokenCredentialProviders {
public class AdlsTokenCredentialProviders {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TokenCredential is applicable for all Azure services, so should we keep the Azure has prefix or Adls - considering we only support Adlsv2
Also, if renamed should we move the classes under adlsv2 package?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to move this to a different package but we should still rename the class to Adls... because the property also says adls....

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed the classes

public void testDefaultFactory() {
AzureTokenCredentialProvider provider = AzureTokenCredentialProviders.defaultFactory();
assertThat(provider).isNotNull();
assertThat(provider)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can combine both assertions and have assertThat(provider).isNotNull().isInstanceOf(..)

public class TestAzureTokenCredentialProviders {

@Test
public void testDefaultFactory() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't really need the test prefix in all of those new test methods. Instead just use meaningful method names, such as useDefaultFactory() or emptyPropertiesWithNoProvider() and so on


assertThat(provider).isNotNull();
assertThat(provider).isInstanceOf(DummyTokenCredentialProvider.class);
assertThat(provider.credential()).isInstanceOf(DummyTokenCredential.class);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also update TestAzureProperties to make sure the credential actually comes back correctly and has the right value

@munendrasn
munendrasn force-pushed the azure-credential-provider branch from 56e46e5 to f4b18d4 Compare September 29, 2025 14:22
@munendrasn

munendrasn commented Sep 29, 2025 •

Copy link
Copy Markdown
Contributor Author

@nastra I have incorporated the most of the suggestions.
returning the same instance of DefaultTokenCredential - I have left couple of questions

* <p>Example: adls.token-credential-provider=com.example.MyCustomTokenCredentialProvider
*
* <p>When set, the {@link AdlsTokenCredentialProviders#from(Map)} method will use this provider
* to get Azure credentials instead of using the default.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* to get Azure credentials instead of using the default.
* to get ADLS credentials instead of using the default.

public static final String ADLS_TOKEN = "adls.token";

/**
* Configure the Azure token credential provider used to get {@link TokenCredential}. A fully

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Configure the Azure token credential provider used to get {@link TokenCredential}. A fully
* Configure the ADLS token credential provider used to get {@link TokenCredential}. A fully

@kevinjqliu kevinjqliu Sep 29, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer Azure here since the token credentials are from Azure Identity. Plus we're adding support for OneLake as well as ADLS.

@Test
public void emptyPropertiesWithNoProvider() {
Map<String, String> properties = ImmutableMap.of();
AdlsTokenCredentialProvider provider = AdlsTokenCredentialProviders.from(properties);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the provider can be inlined in all of the tests: assertThat(AdlsTokenCredentialProviders.from(properties))...

}
}

// Dummy TokenCredential for testing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment can be removed as I think it's obvious from the naming of the class

assertThat(provider.credential()).isInstanceOf(DummyTokenCredential.class);
}

// Dummy implementation for testing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed

}

static class DummyTokenCredentialProvider implements AdlsTokenCredentialProvider {
static Map<String, String> lastInitializedProperties;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just call this properties?

verify(clientBuilder, never()).credential(any(StorageSharedKeyCredential.class));
}

// Helper classes for custom provider testing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed

@nastra nastra left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a few more comments but overall LGTM

@kevinjqliu kevinjqliu left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution! This is a lot cleaner than implementing individual auth method (like in #13829). Perhaps we can also refactor the static token auth (#13825) as a follow up.

Regarding the naming convention, ADLS vs Azure, I would prefer to use Azure since these are Azure identity auth methods.
We're also adding support for this on the Microsoft OneLake side. "Azure" would be less confusing than ADLS since OneLake is ADLS-compatible

Since everything else mentions ADLS (except AzureProperties), i think it would be less confusing to users if we just default everything to ADLS for now.
We can look to rename ADLS -> Azure in the future

public static final String ADLS_TOKEN = "adls.token";

/**
* Configure the Azure token credential provider used to get {@link TokenCredential}. A fully

@kevinjqliu kevinjqliu Sep 29, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer Azure here since the token credentials are from Azure Identity. Plus we're adding support for OneLake as well as ADLS.

@munendrasn

Copy link
Copy Markdown
Contributor Author

Thanks @kevinjqliu @nastra for the review. I have addressed the last set of review suggestions. Please have a look

@nastra
nastra merged commit 8ae7559 into apache:main Sep 30, 2025
42 checks passed
@munendrasn
munendrasn deleted the azure-credential-provider branch September 30, 2025 07:40
gabeiglio pushed a commit to gabeiglio/iceberg that referenced this pull request Oct 1, 2025
adawrapub pushed a commit to adawrapub/iceberg that referenced this pull request Oct 16, 2025
@aswirszcz aswirszcz mentioned this pull request Nov 28, 2025
thomaschow pushed a commit to thomaschow/iceberg that referenced this pull request Jan 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants