Skip to content

Commit 896360a

Browse files
feat: Refactoring code to get oidc end points from discovery URL. (#4429)
* refactoring the permissions side server side code to get the OIDC end points from the discovery URL. Also removing the auth_server_url config from oidc auth config. Signed-off-by: Lokesh Rangineni <19699092+lokeshrangineni@users.noreply.github.com> * refactoring the permissions side server side code to get the OIDC end points from the discovery URL. Also removing the auth_server_url config from oidc auth config. Signed-off-by: Lokesh Rangineni <19699092+lokeshrangineni@users.noreply.github.com> * refactoring the permissions side server side code to get the OIDC end points from the discovery URL. Also removing the auth_server_url config from oidc auth config. Signed-off-by: Lokesh Rangineni <19699092+lokeshrangineni@users.noreply.github.com> * refactoring the permissions side server side code to get the OIDC end points from the discovery URL. Also removing the auth_server_url config from oidc auth config. Signed-off-by: Lokesh Rangineni <19699092+lokeshrangineni@users.noreply.github.com> * Fixing the issue with pre-commit hook template. Accidentally this was reverted in previous rebase and reverting it now. Signed-off-by: Lokesh Rangineni <19699092+lokeshrangineni@users.noreply.github.com> --------- Signed-off-by: Lokesh Rangineni <19699092+lokeshrangineni@users.noreply.github.com>
1 parent dda0088 commit 896360a

File tree

12 files changed

+73
-35
lines changed

12 files changed

+73
-35
lines changed

docs/getting-started/components/authz_manager.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ auth:
6868
type: oidc
6969
client_id: _CLIENT_ID__
7070
client_secret: _CLIENT_SECRET__
71-
realm: _REALM__
72-
auth_server_url: _OIDC_SERVER_URL_
71+
realm: _REALM__
7372
auth_discovery_url: _OIDC_SERVER_URL_/realms/master/.well-known/openid-configuration
7473
...
7574
```

sdk/python/feast/permissions/auth/oidc_token_parser.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from feast.permissions.auth.token_parser import TokenParser
1313
from feast.permissions.auth_model import OidcAuthConfig
14+
from feast.permissions.oidc_service import OIDCDiscoveryService
1415
from feast.permissions.user import User
1516

1617
logger = logging.getLogger(__name__)
@@ -27,6 +28,9 @@ class OidcTokenParser(TokenParser):
2728

2829
def __init__(self, auth_config: OidcAuthConfig):
2930
self._auth_config = auth_config
31+
self.oidc_discovery_service = OIDCDiscoveryService(
32+
self._auth_config.auth_discovery_url
33+
)
3034

3135
async def _validate_token(self, access_token: str):
3236
"""
@@ -38,9 +42,9 @@ async def _validate_token(self, access_token: str):
3842
request.headers = {"Authorization": f"Bearer {access_token}"}
3943

4044
oauth_2_scheme = OAuth2AuthorizationCodeBearer(
41-
tokenUrl=f"{self._auth_config.auth_server_url}/realms/{self._auth_config.realm}/protocol/openid-connect/token",
42-
authorizationUrl=f"{self._auth_config.auth_server_url}/realms/{self._auth_config.realm}/protocol/openid-connect/auth",
43-
refreshUrl=f"{self._auth_config.auth_server_url}/realms/{self._auth_config.realm}/protocol/openid-connect/token",
45+
tokenUrl=self.oidc_discovery_service.get_token_url(),
46+
authorizationUrl=self.oidc_discovery_service.get_authorization_url(),
47+
refreshUrl=self.oidc_discovery_service.get_refresh_url(),
4448
)
4549

4650
await oauth_2_scheme(request=request)
@@ -62,9 +66,10 @@ async def user_details_from_access_token(self, access_token: str) -> User:
6266
except Exception as e:
6367
raise AuthenticationError(f"Invalid token: {e}")
6468

65-
url = f"{self._auth_config.auth_server_url}/realms/{self._auth_config.realm}/protocol/openid-connect/certs"
6669
optional_custom_headers = {"User-agent": "custom-user-agent"}
67-
jwks_client = PyJWKClient(url, headers=optional_custom_headers)
70+
jwks_client = PyJWKClient(
71+
self.oidc_discovery_service.get_jwks_url(), headers=optional_custom_headers
72+
)
6873

6974
try:
7075
signing_key = jwks_client.get_signing_key_from_jwt(access_token)

sdk/python/feast/permissions/auth_model.py

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ class AuthConfig(FeastConfigBaseModel):
88

99

1010
class OidcAuthConfig(AuthConfig):
11-
auth_server_url: Optional[str] = None
1211
auth_discovery_url: str
1312
client_id: str
1413
client_secret: Optional[str] = None

sdk/python/feast/permissions/client/oidc_authentication_client_manager.py

+4-17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from feast.permissions.auth_model import OidcAuthConfig
66
from feast.permissions.client.auth_client_manager import AuthenticationClientManager
7+
from feast.permissions.oidc_service import OIDCDiscoveryService
78

89
logger = logging.getLogger(__name__)
910

@@ -12,25 +13,11 @@ class OidcAuthClientManager(AuthenticationClientManager):
1213
def __init__(self, auth_config: OidcAuthConfig):
1314
self.auth_config = auth_config
1415

15-
def _get_token_endpoint(self):
16-
response = requests.get(self.auth_config.auth_discovery_url)
17-
if response.status_code == 200:
18-
oidc_config = response.json()
19-
if not oidc_config["token_endpoint"]:
20-
raise RuntimeError(
21-
" OIDC token_endpoint is not available from discovery url response."
22-
)
23-
return oidc_config["token_endpoint"].replace(
24-
"master", self.auth_config.realm
25-
)
26-
else:
27-
raise RuntimeError(
28-
f"Error fetching OIDC token endpoint configuration: {response.status_code} - {response.text}"
29-
)
30-
3116
def get_token(self):
3217
# Fetch the token endpoint from the discovery URL
33-
token_endpoint = self._get_token_endpoint()
18+
token_endpoint = OIDCDiscoveryService(
19+
self.auth_config.auth_discovery_url
20+
).get_token_url()
3421

3522
token_request_body = {
3623
"grant_type": "password",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import requests
2+
3+
4+
class OIDCDiscoveryService:
5+
def __init__(self, discovery_url: str):
6+
self.discovery_url = discovery_url
7+
self._discovery_data = None # Initialize it lazily.
8+
9+
@property
10+
def discovery_data(self):
11+
"""Lazily fetches and caches the OIDC discovery data."""
12+
if self._discovery_data is None:
13+
self._discovery_data = self._fetch_discovery_data()
14+
return self._discovery_data
15+
16+
def _fetch_discovery_data(self) -> dict:
17+
try:
18+
response = requests.get(self.discovery_url)
19+
response.raise_for_status()
20+
return response.json()
21+
except requests.RequestException as e:
22+
raise RuntimeError(
23+
f"Error fetching OIDC discovery response, discovery url - {self.discovery_url}, exception - {e} "
24+
)
25+
26+
def get_authorization_url(self) -> str:
27+
"""Returns the authorization endpoint URL."""
28+
return self.discovery_data.get("authorization_endpoint")
29+
30+
def get_token_url(self) -> str:
31+
"""Returns the token endpoint URL."""
32+
return self.discovery_data.get("token_endpoint")
33+
34+
def get_jwks_url(self) -> str:
35+
"""Returns the jwks endpoint URL."""
36+
return self.discovery_data.get("jwks_uri")
37+
38+
def get_refresh_url(self) -> str:
39+
"""Returns the refresh token URL (usually same as token URL)."""
40+
return self.get_token_url()

sdk/python/tests/conftest.py

-1
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,6 @@ def is_integration_test(all_markers_from_module):
463463
username: reader_writer
464464
password: password
465465
realm: master
466-
auth_server_url: KEYCLOAK_URL_PLACE_HOLDER
467466
auth_discovery_url: KEYCLOAK_URL_PLACE_HOLDER/realms/master/.well-known/openid-configuration
468467
"""),
469468
],

sdk/python/tests/integration/feature_repos/repo_configuration.py

-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,6 @@ def setup(self):
464464
password="password",
465465
realm="master",
466466
type="oidc",
467-
auth_server_url=keycloak_url,
468467
auth_discovery_url=f"{keycloak_url}/realms/master/.well-known"
469468
f"/openid-configuration",
470469
)

sdk/python/tests/integration/feature_repos/universal/data_sources/file.py

-1
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,6 @@ def __init__(self, project_name: str, *args, **kwargs):
449449
username: reader_writer
450450
password: password
451451
realm: master
452-
auth_server_url: {keycloak_url}
453452
auth_discovery_url: {keycloak_url}/realms/master/.well-known/openid-configuration
454453
"""
455454
self.auth_config = auth_config_template.format(keycloak_url=self.keycloak_url)

sdk/python/tests/unit/infra/scaffolding/test_repo_config.py

-4
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ def test_auth_config():
214214
username: test_user_name
215215
password: test_password
216216
realm: master
217-
auth_server_url: http://localhost:8712
218217
auth_discovery_url: http://localhost:8080/realms/master/.well-known/openid-configuration
219218
registry: "registry.db"
220219
provider: local
@@ -237,7 +236,6 @@ def test_auth_config():
237236
username: test_user_name
238237
password: test_password
239238
realm: master
240-
auth_server_url: http://localhost:8712
241239
auth_discovery_url: http://localhost:8080/realms/master/.well-known/openid-configuration
242240
registry: "registry.db"
243241
provider: local
@@ -260,7 +258,6 @@ def test_auth_config():
260258
username: test_user_name
261259
password: test_password
262260
realm: master
263-
auth_server_url: http://localhost:8080
264261
auth_discovery_url: http://localhost:8080/realms/master/.well-known/openid-configuration
265262
registry: "registry.db"
266263
provider: local
@@ -278,7 +275,6 @@ def test_auth_config():
278275
assert oidc_repo_config.auth_config.username == "test_user_name"
279276
assert oidc_repo_config.auth_config.password == "test_password"
280277
assert oidc_repo_config.auth_config.realm == "master"
281-
assert oidc_repo_config.auth_config.auth_server_url == "http://localhost:8080"
282278
assert (
283279
oidc_repo_config.auth_config.auth_discovery_url
284280
== "http://localhost:8080/realms/master/.well-known/openid-configuration"

sdk/python/tests/unit/permissions/auth/conftest.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ def clusterrolebindings(sa_name, namespace) -> dict:
7373
@pytest.fixture
7474
def oidc_config() -> OidcAuthConfig:
7575
return OidcAuthConfig(
76-
auth_server_url="",
77-
auth_discovery_url="",
76+
auth_discovery_url="https://localhost:8080/realms/master/.well-known/openid-configuration",
7877
client_id=_CLIENT_ID,
7978
client_secret="",
8079
username="",

sdk/python/tests/unit/permissions/auth/server/mock_utils.py

+9
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ async def mock_oath2(self, request):
4242
lambda url, data, headers: token_response,
4343
)
4444

45+
monkeypatch.setattr(
46+
"feast.permissions.oidc_service.OIDCDiscoveryService._fetch_discovery_data",
47+
lambda self, *args, **kwargs: {
48+
"authorization_endpoint": "https://localhost:8080/realms/master/protocol/openid-connect/auth",
49+
"token_endpoint": "https://localhost:8080/realms/master/protocol/openid-connect/token",
50+
"jwks_uri": "https://localhost:8080/realms/master/protocol/openid-connect/certs",
51+
},
52+
)
53+
4554

4655
def mock_kubernetes(request, monkeypatch):
4756
sa_name = request.getfixturevalue("sa_name")

sdk/python/tests/unit/permissions/auth/test_token_parser.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@
2121
)
2222
@patch("feast.permissions.auth.oidc_token_parser.PyJWKClient.get_signing_key_from_jwt")
2323
@patch("feast.permissions.auth.oidc_token_parser.jwt.decode")
24+
@patch("feast.permissions.oidc_service.OIDCDiscoveryService._fetch_discovery_data")
2425
def test_oidc_token_validation_success(
25-
mock_jwt, mock_signing_key, mock_oauth2, oidc_config
26+
mock_discovery_data, mock_jwt, mock_signing_key, mock_oauth2, oidc_config
2627
):
2728
signing_key = MagicMock()
2829
signing_key.key = "a-key"
2930
mock_signing_key.return_value = signing_key
3031

32+
mock_discovery_data.return_value = {
33+
"authorization_endpoint": "https://localhost:8080/realms/master/protocol/openid-connect/auth",
34+
"token_endpoint": "https://localhost:8080/realms/master/protocol/openid-connect/token",
35+
"jwks_uri": "https://localhost:8080/realms/master/protocol/openid-connect/certs",
36+
}
37+
3138
user_data = {
3239
"preferred_username": "my-name",
3340
"resource_access": {_CLIENT_ID: {"roles": ["reader", "writer"]}},

0 commit comments

Comments
 (0)