-
Notifications
You must be signed in to change notification settings - Fork 31
Add native Azure DevOps OIDC authentication support #512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Divyansh-db
merged 17 commits into
main
from
divyansh-vijayvergia_data/AzureDevOps_OIDC_support
Oct 2, 2025
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
d443694
add azure devops OIDC authentication
Divyansh-db f46ac44
Merge branch 'main' into divyansh-vijayvergia_data/AzureDevOps_OIDC_s…
Divyansh-db 7717a1b
added tests, refactored code
Divyansh-db 255739e
testing changing push workflow
Divyansh-db 2d0cd7b
trying JDK 11
Divyansh-db 5d38f21
removed maven part in workflow
Divyansh-db d8833e5
reverted to original workflow and fromatting
Divyansh-db e167257
removed outdated github OIDC files
Divyansh-db 864b6f0
Merge branch 'main' into divyansh-vijayvergia_data/AzureDevOps_OIDC_s…
Divyansh-db f17200b
Merge branch 'main' into divyansh-vijayvergia_data/AzureDevOps_OIDC_s…
Divyansh-db 770a6f7
Update databricks-sdk-java/src/main/java/com/databricks/sdk/core/oaut…
Divyansh-db f33405d
Update databricks-sdk-java/src/main/java/com/databricks/sdk/core/oaut…
Divyansh-db daa4122
Update databricks-sdk-java/src/main/java/com/databricks/sdk/core/oaut…
Divyansh-db e2e8936
Update databricks-sdk-java/src/main/java/com/databricks/sdk/core/oaut…
Divyansh-db 0c49d8b
Update databricks-sdk-java/src/main/java/com/databricks/sdk/core/oaut…
Divyansh-db b6f1e48
chaned constructor to protected
Divyansh-db fc83237
refactored code
Divyansh-db File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
177 changes: 177 additions & 0 deletions
177
...bricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/AzureDevOpsIDTokenSource.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package com.databricks.sdk.core.oauth; | ||
|
||
import com.databricks.sdk.core.DatabricksException; | ||
import com.databricks.sdk.core.http.HttpClient; | ||
import com.databricks.sdk.core.http.Request; | ||
import com.databricks.sdk.core.http.Response; | ||
import com.databricks.sdk.core.utils.Environment; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import com.google.common.base.Strings; | ||
import java.io.IOException; | ||
|
||
/** | ||
* AzureDevOpsIDTokenSource retrieves JWT Tokens from Azure DevOps Pipelines. This class implements | ||
* the IDTokenSource interface and provides a method for obtaining ID tokens specifically from Azure | ||
* DevOps Pipeline environment. | ||
* | ||
* <p>This implementation relies on the <a | ||
* href="https://learn.microsoft.com/en-us/rest/api/azure/devops/distributedtask/oidctoken/create">Azure | ||
* DevOps OIDC token API</a>. | ||
*/ | ||
public class AzureDevOpsIDTokenSource implements IDTokenSource { | ||
/* Access token for authenticating with Azure DevOps API */ | ||
private final String azureDevOpsAccessToken; | ||
/* Team Foundation Collection URI (e.g., https://dev.azure.com/organization) */ | ||
private final String azureDevOpsTeamFoundationCollectionUri; | ||
/* Plan ID for the current pipeline run */ | ||
private final String azureDevOpsPlanId; | ||
/* Job ID for the current pipeline job */ | ||
private final String azureDevOpsJobId; | ||
/* Team Project ID where the pipeline is running */ | ||
private final String azureDevOpsTeamProjectId; | ||
/* Host type (e.g., "build", "release") */ | ||
private final String azureDevOpsHostType; | ||
/* HTTP client for making requests to Azure DevOps */ | ||
private final HttpClient httpClient; | ||
/* Environment for reading configuration values */ | ||
private final Environment environment; | ||
/* JSON mapper for parsing response data */ | ||
private static final ObjectMapper mapper = new ObjectMapper(); | ||
|
||
/** | ||
* Constructs a new AzureDevOpsIDTokenSource by reading environment variables. This constructor | ||
* implements fail-early validation - if any required environment variables are missing, it will | ||
* throw a DatabricksException immediately. | ||
* | ||
* @param httpClient The HTTP client to use for making requests | ||
* @throws DatabricksException if any required environment variables are missing | ||
*/ | ||
public AzureDevOpsIDTokenSource(HttpClient httpClient) { | ||
this(httpClient, createDefaultEnvironment()); | ||
} | ||
|
||
/** | ||
* Constructs a new AzureDevOpsIDTokenSource with a custom environment. This constructor is | ||
* primarily used for testing to inject mock environment variables. | ||
* | ||
* @param httpClient The HTTP client to use for making requests | ||
* @param environment The environment to read configuration from | ||
* @throws DatabricksException if httpClient is null or any required environment variables are | ||
* missing | ||
*/ | ||
protected AzureDevOpsIDTokenSource(HttpClient httpClient, Environment environment) { | ||
if (httpClient == null) { | ||
throw new DatabricksException("HttpClient cannot be null"); | ||
} | ||
this.httpClient = httpClient; | ||
this.environment = environment; | ||
|
||
this.azureDevOpsAccessToken = validateEnvironmentVariable("SYSTEM_ACCESSTOKEN"); | ||
this.azureDevOpsTeamFoundationCollectionUri = | ||
validateEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"); | ||
this.azureDevOpsPlanId = validateEnvironmentVariable("SYSTEM_PLANID"); | ||
this.azureDevOpsJobId = validateEnvironmentVariable("SYSTEM_JOBID"); | ||
this.azureDevOpsTeamProjectId = validateEnvironmentVariable("SYSTEM_TEAMPROJECTID"); | ||
this.azureDevOpsHostType = validateEnvironmentVariable("SYSTEM_HOSTTYPE"); | ||
} | ||
|
||
/** Creates a default Environment using system environment variables. */ | ||
private static Environment createDefaultEnvironment() { | ||
String pathEnv = System.getenv("PATH"); | ||
String[] pathArray = | ||
pathEnv != null ? pathEnv.split(java.io.File.pathSeparator) : new String[0]; | ||
return new Environment(System.getenv(), pathArray, System.getProperty("os.name")); | ||
} | ||
|
||
/** | ||
* Validates that an environment variable is present and not empty. | ||
* | ||
* @param varName The environment variable name | ||
* @return The environment variable value | ||
* @throws DatabricksException if the environment variable is missing or empty | ||
*/ | ||
private String validateEnvironmentVariable(String varName) { | ||
String value = environment.get(varName); | ||
if (Strings.isNullOrEmpty(value)) { | ||
if (varName.equals("SYSTEM_ACCESSTOKEN")) { | ||
throw new DatabricksException( | ||
String.format( | ||
"Missing environment variable %s, if calling from Azure DevOps Pipeline, please set this env var following https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#systemaccesstoken", | ||
varName)); | ||
} | ||
throw new DatabricksException( | ||
String.format( | ||
"Missing environment variable %s, likely not calling from Azure DevOps Pipeline", | ||
varName)); | ||
} | ||
return value; | ||
} | ||
|
||
/** | ||
* Retrieves an ID token from Azure DevOps Pipelines. This method makes an authenticated request | ||
* to Azure DevOps to obtain a JWT token that can later be exchanged for a Databricks access | ||
* token. | ||
* | ||
* <p>Note: The audience parameter is ignored for Azure DevOps OIDC tokens as they have a | ||
* hardcoded audience for Azure AD integration. | ||
* | ||
* @param audience Ignored for Azure DevOps OIDC tokens | ||
* @return An IDToken object containing the JWT token value | ||
* @throws DatabricksException if the token request fails | ||
*/ | ||
@Override | ||
public IDToken getIDToken(String audience) { | ||
|
||
// Build Azure DevOps OIDC endpoint URL. | ||
String requestUrl = | ||
String.format( | ||
"%s/%s/_apis/distributedtask/hubs/%s/plans/%s/jobs/%s/oidctoken?api-version=7.2-preview.1", | ||
azureDevOpsTeamFoundationCollectionUri, | ||
azureDevOpsTeamProjectId, | ||
azureDevOpsHostType, | ||
azureDevOpsPlanId, | ||
azureDevOpsJobId); | ||
|
||
Request req = | ||
new Request("POST", requestUrl) | ||
.withHeader("Authorization", "Bearer " + azureDevOpsAccessToken) | ||
.withHeader("Content-Type", "application/json"); | ||
|
||
Response resp; | ||
try { | ||
resp = httpClient.execute(req); | ||
} catch (IOException e) { | ||
throw new DatabricksException( | ||
"Failed to request ID token from Azure DevOps at " + requestUrl + ": " + e.getMessage(), | ||
e); | ||
} | ||
|
||
if (resp.getStatusCode() != 200) { | ||
throw new DatabricksException( | ||
"Failed to request ID token from Azure DevOps: status code " | ||
+ resp.getStatusCode() | ||
+ ", response body: " | ||
+ resp.getBody().toString()); | ||
} | ||
|
||
ObjectNode jsonResp; | ||
try { | ||
jsonResp = mapper.readValue(resp.getBody(), ObjectNode.class); | ||
} catch (IOException e) { | ||
throw new DatabricksException( | ||
"Failed to parse Azure DevOps OIDC token response: " + e.getMessage(), e); | ||
} | ||
|
||
// Azure DevOps returns {"oidcToken":"***"} format, not {"value":"***"} like GitHub Actions. | ||
if (!jsonResp.has("oidcToken")) { | ||
throw new DatabricksException("Azure DevOps OIDC token response missing 'oidcToken' field"); | ||
} | ||
|
||
String tokenValue = jsonResp.get("oidcToken").textValue(); | ||
if (Strings.isNullOrEmpty(tokenValue)) { | ||
throw new DatabricksException("Received empty OIDC token from Azure DevOps"); | ||
} | ||
return new IDToken(tokenValue); | ||
} | ||
} |
79 changes: 0 additions & 79 deletions
79
databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/GitHubOidcTokenSupplier.java
This file was deleted.
Oops, something went wrong.
71 changes: 0 additions & 71 deletions
71
...s-sdk-java/src/main/java/com/databricks/sdk/core/oauth/GithubOidcCredentialsProvider.java
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.