5
5
AuthProvider ,
6
6
AccessTokenAuthProvider ,
7
7
ExternalAuthProvider ,
8
- CredentialsProvider ,
9
8
DatabricksOAuthProvider ,
9
+ ClientCredentialsProvider ,
10
10
)
11
11
12
12
13
13
class AuthType (Enum ):
14
14
DATABRICKS_OAUTH = "databricks-oauth"
15
15
AZURE_OAUTH = "azure-oauth"
16
- # TODO: Token federation should be a feature that works with different auth types,
17
- # not an auth type itself. This will be refactored in a future change.
18
- # We will add a use_token_federation flag that can be used with any auth type.
19
- TOKEN_FEDERATION = "token-federation"
20
- # other supported types (access_token) can be inferred
21
- # we can add more types as needed later
22
-
16
+ CLIENT_CREDENTIALS = "client-credentials"
23
17
24
18
class ClientContext :
25
19
def __init__ (
@@ -34,8 +28,10 @@ def __init__(
34
28
tls_client_cert_file : Optional [str ] = None ,
35
29
oauth_persistence = None ,
36
30
credentials_provider = None ,
37
- identity_federation_client_id : Optional [str ] = None ,
31
+ oauth_client_secret : Optional [str ] = None ,
32
+ tenant_id : Optional [str ] = None ,
38
33
use_token_federation : bool = False ,
34
+ identity_federation_client_id : Optional [str ] = None ,
39
35
):
40
36
self .hostname = hostname
41
37
self .access_token = access_token
@@ -49,20 +45,52 @@ def __init__(
49
45
self .credentials_provider = credentials_provider
50
46
self .identity_federation_client_id = identity_federation_client_id
51
47
self .use_token_federation = use_token_federation
48
+ self .oauth_client_secret = oauth_client_secret
49
+ self .tenant_id = tenant_id
50
+
51
+ def _create_azure_client_credentials_provider (cfg : ClientContext ) -> ClientCredentialsProvider :
52
+ """Create an Azure client credentials provider."""
53
+ if not cfg .oauth_client_id or not cfg .oauth_client_secret or not cfg .tenant_id :
54
+ raise ValueError ("Azure client credentials flow requires oauth_client_id, oauth_client_secret, and tenant_id" )
55
+
56
+ token_endpoint = "https://login.microsoftonline.com/{}/oauth2/v2.0/token" .format (cfg .tenant_id )
57
+ return ClientCredentialsProvider (
58
+ client_id = cfg .oauth_client_id ,
59
+ client_secret = cfg .oauth_client_secret ,
60
+ token_endpoint = token_endpoint ,
61
+ auth_type_value = "azure-client-credentials"
62
+ )
63
+
64
+
65
+ def _create_databricks_client_credentials_provider (cfg : ClientContext ) -> ClientCredentialsProvider :
66
+ """Create a Databricks client credentials provider for service principals."""
67
+ if not cfg .oauth_client_id or not cfg .oauth_client_secret :
68
+ raise ValueError ("Databricks client credentials flow requires oauth_client_id and oauth_client_secret" )
69
+
70
+ token_endpoint = "{}oidc/v1/token" .format (cfg .hostname )
71
+ return ClientCredentialsProvider (
72
+ client_id = cfg .oauth_client_id ,
73
+ client_secret = cfg .oauth_client_secret ,
74
+ token_endpoint = token_endpoint ,
75
+ auth_type_value = "client-credentials"
76
+ )
52
77
53
78
54
79
def get_auth_provider (cfg : ClientContext ):
55
80
"""
56
81
Get an appropriate auth provider based on the provided configuration.
57
82
83
+ OAuth Flow Support:
84
+ This function supports multiple OAuth flows:
85
+ 1. Interactive OAuth (databricks-oauth, azure-oauth) - for user authentication
86
+ 2. Client Credentials (client-credentials) - for machine-to-machine authentication
87
+ 3. Token Federation - implemented as a feature flag that wraps any auth type
88
+
58
89
Token Federation Support:
59
90
-----------------------
60
- Currently, token federation is implemented as a separate auth type, but the goal is to
61
- refactor it as a feature that can work with any auth type. The current implementation
62
- is maintained for backward compatibility while the refactoring is planned.
63
-
64
- Future refactoring will introduce a `use_token_federation` flag that can be combined
65
- with any auth type to enable token federation.
91
+ Token federation is implemented as a feature flag (`use_token_federation=True`) that
92
+ can be combined with any auth type. When enabled, it wraps the base auth provider
93
+ in a DatabricksTokenFederationProvider for token exchange functionality.
66
94
67
95
Args:
68
96
cfg: The client context containing configuration parameters
@@ -74,21 +102,31 @@ def get_auth_provider(cfg: ClientContext):
74
102
RuntimeError: If no valid authentication settings are provided
75
103
"""
76
104
from databricks .sql .auth .token_federation import DatabricksTokenFederationProvider
105
+
106
+ base_provider = None
107
+
77
108
if cfg .credentials_provider :
78
109
base_provider = ExternalAuthProvider (cfg .credentials_provider )
79
110
elif cfg .access_token is not None :
80
111
base_provider = AccessTokenAuthProvider (cfg .access_token )
112
+ elif cfg .auth_type == AuthType .CLIENT_CREDENTIALS .value :
113
+ if cfg .tenant_id :
114
+ # Azure client credentials flow
115
+ base_provider = _create_azure_client_credentials_provider (cfg )
116
+ else :
117
+ # Databricks service principal client credentials flow
118
+ base_provider = _create_databricks_client_credentials_provider (cfg )
81
119
elif cfg .auth_type in [AuthType .DATABRICKS_OAUTH .value , AuthType .AZURE_OAUTH .value ]:
82
120
assert cfg .oauth_redirect_port_range is not None
83
121
assert cfg .oauth_client_id is not None
84
122
assert cfg .oauth_scopes is not None
85
123
base_provider = DatabricksOAuthProvider (
86
- cfg .hostname ,
87
- cfg .oauth_persistence ,
88
- cfg .oauth_redirect_port_range ,
89
- cfg .oauth_client_id ,
90
- cfg .oauth_scopes ,
91
- cfg .auth_type ,
124
+ hostname = cfg .hostname ,
125
+ oauth_persistence = cfg .oauth_persistence ,
126
+ redirect_port_range = cfg .oauth_redirect_port_range ,
127
+ client_id = cfg .oauth_client_id ,
128
+ scopes = cfg .oauth_scopes ,
129
+ auth_type = cfg .auth_type ,
92
130
)
93
131
elif cfg .use_cert_as_auth and cfg .tls_client_cert_file :
94
132
base_provider = AuthProvider ()
@@ -99,11 +137,11 @@ def get_auth_provider(cfg: ClientContext):
99
137
and cfg .oauth_scopes is not None
100
138
):
101
139
base_provider = DatabricksOAuthProvider (
102
- cfg .hostname ,
103
- cfg .oauth_persistence ,
104
- cfg .oauth_redirect_port_range ,
105
- cfg .oauth_client_id ,
106
- cfg .oauth_scopes ,
140
+ hostname = cfg .hostname ,
141
+ oauth_persistence = cfg .oauth_persistence ,
142
+ redirect_port_range = cfg .oauth_redirect_port_range ,
143
+ client_id = cfg .oauth_client_id ,
144
+ scopes = cfg .oauth_scopes ,
107
145
)
108
146
else :
109
147
raise RuntimeError ("No valid authentication settings!" )
@@ -126,7 +164,7 @@ def get_auth_provider(cfg: ClientContext):
126
164
def normalize_host_name (hostname : str ):
127
165
maybe_scheme = "https://" if not hostname .startswith ("https://" ) else ""
128
166
maybe_trailing_slash = "/" if not hostname .endswith ("/" ) else ""
129
- return f" { maybe_scheme } { hostname } { maybe_trailing_slash } "
167
+ return "{}{}{}" . format ( maybe_scheme , hostname , maybe_trailing_slash )
130
168
131
169
132
170
def get_client_id_and_redirect_port (use_azure_auth : bool ):
@@ -144,14 +182,25 @@ def get_python_sql_connector_auth_provider(hostname: str, **kwargs):
144
182
This function is the main entry point for authentication in the SQL connector.
145
183
It processes the parameters and creates an appropriate auth provider.
146
184
147
- TODO: Future refactoring needed:
148
- 1. Add a use_token_federation flag that can be combined with any auth type
149
- 2. Remove TOKEN_FEDERATION as an auth_type while maintaining backward compatibility
150
- 3. Create a token federation wrapper that can wrap any existing auth provider
185
+ Supported Authentication Methods:
186
+ --------------------------------
187
+ 1. Access Token: Provide 'access_token' parameter
188
+ 2. Interactive OAuth: Set 'auth_type' to 'databricks-oauth' or 'azure-oauth'
189
+ 3. Client Credentials: Set 'auth_type' to 'client-credentials' with client_id, client_secret, tenant_id
190
+ 4. External Provider: Provide 'credentials_provider' parameter
191
+ 5. Token Federation: Set 'use_token_federation=True' with any of the above
151
192
152
193
Args:
153
194
hostname: The Databricks server hostname
154
- **kwargs: Additional configuration parameters
195
+ **kwargs: Additional configuration parameters including:
196
+ - auth_type: Authentication type
197
+ - access_token: Static access token
198
+ - oauth_client_id: OAuth client ID
199
+ - oauth_client_secret: OAuth client secret
200
+ - tenant_id: Azure AD tenant ID (for Azure flows)
201
+ - credentials_provider: External credentials provider
202
+ - use_token_federation: Enable token federation
203
+ - identity_federation_client_id: Federation client ID
155
204
156
205
Returns:
157
206
An appropriate AuthProvider instance
@@ -182,6 +231,8 @@ def get_python_sql_connector_auth_provider(hostname: str, **kwargs):
182
231
else redirect_port_range ,
183
232
oauth_persistence = kwargs .get ("experimental_oauth_persistence" ),
184
233
credentials_provider = kwargs .get ("credentials_provider" ),
234
+ oauth_client_secret = kwargs .get ("oauth_client_secret" ),
235
+ tenant_id = kwargs .get ("tenant_id" ),
185
236
identity_federation_client_id = kwargs .get ("identity_federation_client_id" ),
186
237
use_token_federation = kwargs .get ("use_token_federation" , False ),
187
238
)
0 commit comments