-
Notifications
You must be signed in to change notification settings - Fork 33
Add GitHub OIDC authentication support #444
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
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
5203e57
Add GithubIDTokenSource and TokenSourceCredentialsProvider
emmyzhou-db 69a0215
Add example code
emmyzhou-db e0789e6
Fix example
emmyzhou-db 1579898
Fix code example
emmyzhou-db 2bc27a9
Working Github OIDC code example
emmyzhou-db 6f492bf
Add unit tests and comments
emmyzhou-db 6b16d2b
Merge branch 'main' into emmyzhou-db/github-oidc
renaudhartert-db 71a8d3d
Minor fix
emmyzhou-db 01a0bc2
Update unit tests
emmyzhou-db b527b0c
Update databricks-sdk-java/src/main/java/com/databricks/sdk/core/Defa…
emmyzhou-db 7384c0b
Add TODO
emmyzhou-db 5bf9b37
Small fix
emmyzhou-db e3ca4af
Add Mock HttpClient to tests
emmyzhou-db e181620
Fix formatting
emmyzhou-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
110 changes: 110 additions & 0 deletions
110
databricks-sdk-java/src/main/java/com/databricks/sdk/core/oauth/GithubIDTokenSource.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,110 @@ | ||
| 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.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.fasterxml.jackson.databind.node.ObjectNode; | ||
| import com.google.common.base.Strings; | ||
| import java.io.IOException; | ||
|
|
||
| /** | ||
| * GithubIDTokenSource retrieves JWT Tokens from GitHub Actions. This class implements the | ||
| * IDTokenSource interface and provides a method for obtaining ID tokens specifically from GitHub | ||
| * Actions environment. | ||
| */ | ||
| public class GithubIDTokenSource implements IDTokenSource { | ||
| /* URL endpoint for requesting ID tokens from GitHub Actions */ | ||
| private final String actionsIDTokenRequestURL; | ||
| /* Authentication token required to request ID tokens from GitHub Actions */ | ||
| private final String actionsIDTokenRequestToken; | ||
| /* HTTP client for making requests to GitHub Actions */ | ||
| private final HttpClient httpClient; | ||
| /* JSON mapper for parsing response data */ | ||
| private static final ObjectMapper mapper = new ObjectMapper(); | ||
|
|
||
| /** | ||
| * Constructs a new GithubIDTokenSource. | ||
| * | ||
| * @param actionsIDTokenRequestURL The URL to request the ID token from GitHub Actions. | ||
| * @param actionsIDTokenRequestToken The token used to authenticate the request. | ||
| * @param httpClient The HTTP client to use for making requests. | ||
| */ | ||
| public GithubIDTokenSource( | ||
| String actionsIDTokenRequestURL, String actionsIDTokenRequestToken, HttpClient httpClient) { | ||
| this.actionsIDTokenRequestURL = actionsIDTokenRequestURL; | ||
| this.actionsIDTokenRequestToken = actionsIDTokenRequestToken; | ||
| this.httpClient = httpClient; | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves an ID token from GitHub Actions. This method makes an authenticated request to GitHub | ||
| * Actions to obtain a JWT token that later can be exchanged for a Databricks access token. | ||
| * | ||
| * @param audience Optional audience claim for the token. If provided, it will be included in the | ||
| * token request to GitHub Actions. | ||
| * @return An IDToken object containing the JWT token value | ||
| * @throws DatabricksException if the token request fails or if required configuration is missing | ||
| */ | ||
| @Override | ||
| public IDToken getIDToken(String audience) { | ||
| // Validate required configuration | ||
| if (Strings.isNullOrEmpty(actionsIDTokenRequestURL)) { | ||
| throw new DatabricksException("Missing ActionsIDTokenRequestURL"); | ||
| } | ||
| if (Strings.isNullOrEmpty(actionsIDTokenRequestToken)) { | ||
| throw new DatabricksException("Missing ActionsIDTokenRequestToken"); | ||
| } | ||
| if (httpClient == null) { | ||
| throw new DatabricksException("HttpClient cannot be null"); | ||
| } | ||
|
|
||
| String requestUrl = actionsIDTokenRequestURL; | ||
| if (!Strings.isNullOrEmpty(audience)) { | ||
| requestUrl = String.format("%s&audience=%s", requestUrl, audience); | ||
| } | ||
|
|
||
| Request req = | ||
| new Request("GET", requestUrl) | ||
| .withHeader("Authorization", "Bearer " + actionsIDTokenRequestToken); | ||
|
|
||
| Response resp; | ||
| try { | ||
| resp = httpClient.execute(req); | ||
| } catch (IOException e) { | ||
| throw new DatabricksException( | ||
| "Failed to request ID token from " + requestUrl + ": " + e.getMessage(), e); | ||
| } | ||
|
|
||
| // Validate response status code | ||
| if (resp.getStatusCode() != 200) { | ||
| throw new DatabricksException( | ||
| "Failed to request ID token: status code " | ||
| + resp.getStatusCode() | ||
| + ", response body: " | ||
| + resp.getBody().toString()); | ||
| } | ||
|
|
||
| // Parse the JSON response | ||
| ObjectNode jsonResp; | ||
| try { | ||
| jsonResp = mapper.readValue(resp.getBody(), ObjectNode.class); | ||
| } catch (IOException e) { | ||
| throw new DatabricksException( | ||
| "Failed to request ID token: corrupted token: " + e.getMessage()); | ||
| } | ||
|
|
||
| // Validate response structure and token value | ||
| if (!jsonResp.has("value")) { | ||
| throw new DatabricksException("ID token response missing 'value' field"); | ||
| } | ||
|
|
||
| try { | ||
| String tokenValue = jsonResp.get("value").textValue(); | ||
| return new IDToken(tokenValue); | ||
| } catch (IllegalArgumentException e) { | ||
| throw new DatabricksException("Received empty ID token from GitHub Actions"); | ||
| } | ||
| } | ||
| } |
67 changes: 67 additions & 0 deletions
67
...-sdk-java/src/main/java/com/databricks/sdk/core/oauth/TokenSourceCredentialsProvider.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,67 @@ | ||
| package com.databricks.sdk.core.oauth; | ||
|
|
||
| import com.databricks.sdk.core.CredentialsProvider; | ||
| import com.databricks.sdk.core.DatabricksConfig; | ||
| import com.databricks.sdk.core.HeaderFactory; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| /** | ||
| * A credentials provider that uses a TokenSource to obtain and manage authentication tokens. This | ||
| * class serves as a base implementation for token-based authentication, handling the conversion of | ||
| * tokens into HTTP authorization headers. | ||
| * | ||
| * <p>The provider validates token availability during configuration and creates. appropriate | ||
| * authorization headers for API requests. | ||
| */ | ||
| public class TokenSourceCredentialsProvider implements CredentialsProvider { | ||
| private final TokenSource tokenSource; | ||
| private final String authType; | ||
|
|
||
| /** | ||
| * Creates a new TokenSourceCredentialsProvider with the specified token source and auth type. | ||
| * | ||
| * @param tokenSource The token source responsible for token acquisition and management. | ||
| * @param authType The authentication type identifier. | ||
| */ | ||
| public TokenSourceCredentialsProvider(TokenSource tokenSource, String authType) { | ||
| this.tokenSource = tokenSource; | ||
| this.authType = authType; | ||
| } | ||
|
|
||
| /** | ||
| * Configures the credentials provider and creates a HeaderFactory for generating authentication | ||
| * headers. This method validates token availability by attempting to obtain an access token | ||
| * before returning the HeaderFactory. | ||
| * | ||
| * @param config The Databricks configuration object. | ||
| * @return A HeaderFactory that generates "Bearer" token authorization headers, or null if token | ||
| * acquisition fails. | ||
| */ | ||
| @Override | ||
| public HeaderFactory configure(DatabricksConfig config) { | ||
| try { | ||
| // Validate that we can get a token before returning the HeaderFactory | ||
| String accessToken = tokenSource.getToken().getAccessToken(); | ||
|
|
||
| return () -> { | ||
| Map<String, String> headers = new HashMap<>(); | ||
| headers.put("Authorization", "Bearer " + accessToken); | ||
| return headers; | ||
| }; | ||
| } catch (Exception e) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Returns the authentication type identifier for this credentials provider. This is used to | ||
| * identify the authentication method being used. | ||
| * | ||
| * @return The authentication type string | ||
| */ | ||
| @Override | ||
| public String authType() { | ||
| return authType; | ||
| } | ||
| } |
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.