vincbeck commented on code in PR #51657:
URL: https://github.com/apache/airflow/pull/51657#discussion_r2219417815
##########
airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_auth.py:
##########
@@ -91,3 +92,38 @@ def test_should_respond_307(
assert response.status_code == 307
assert response.headers["location"] == expected_redirection
+
+
+class TestRefresh(TestAuthEndpoint):
+ @pytest.mark.parametrize(
+ "params",
+ [
+ {},
+ {"next": None},
+ {"next": "http://localhost:8080"},
+ {"next": "http://localhost:8080", "other_param": "something_else"},
+ ],
+ )
+ @patch("airflow.api_fastapi.core_api.routes.public.auth.is_safe_url",
return_value=True)
+ def test_should_respond_307(self, mock_is_safe_url, test_client, params):
+ response = test_client.get("/auth/refresh", follow_redirects=False,
params=params)
+
+ assert response.status_code == 307
+ assert (
+ response.headers["location"] ==
f"{AUTH_MANAGER_LOGIN_URL}?next={params.get('next')}"
Review Comment:
You should test here you are redirected to the refresh token URL from the
auth manager. Please test when the URL is None as well
##########
airflow-core/src/airflow/api_fastapi/core_api/routes/public/auth.py:
##########
@@ -55,3 +55,20 @@ def logout(request: Request, next: None | str = None) ->
RedirectResponse:
logout_url = request.app.state.auth_manager.get_url_login()
return RedirectResponse(logout_url)
+
+
+@auth_router.get(
+ "/refresh",
+
responses=create_openapi_http_exception_doc([status.HTTP_307_TEMPORARY_REDIRECT]),
+)
+def refresh(request: Request, next: None | str = None) -> RedirectResponse:
+ """Refresh the authentication token."""
+ refresh_url = request.app.state.auth_manager.get_url_refresh()
Review Comment:
`refresh_url` can be `None`. In that case, we should redirect to logout?
This scenario is when a user gets an expired token, they are redirected here
but the auth manager the environment is using does not support refresh token.
##########
airflow-core/src/airflow/api_fastapi/auth/managers/base_auth_manager.py:
##########
@@ -132,6 +132,15 @@ def get_url_logout(self) -> str | None:
"""
return None
+ def get_url_refresh(self) -> str | None:
+ """
+ Return the URL to refresh the authentication token.
+
+ This is used to refresh the authentication token when it expires.
+ The default implementation returns None, which means that no refresh
URL is provided.
Review Comment:
```suggestion
The default implementation returns None, which means that the auth
manager does not support refresh token.
```
##########
providers/keycloak/src/airflow/providers/keycloak/auth_manager/routes/login.py:
##########
@@ -70,3 +72,33 @@ def login_callback(request: Request):
secure = bool(conf.get("api", "ssl_cert", fallback=""))
response.set_cookie(COOKIE_NAME_JWT_TOKEN, token, secure=secure)
return response
+
+
+@login_router.get("/refresh")
+def refresh(
+ request: Request, user: Annotated[KeycloakAuthManagerUser,
Depends(get_user)]
+) -> RedirectResponse:
+ """Refresh the token."""
+ client = KeycloakAuthManager.get_keycloak_client()
+
+ if not user or not user.refresh_token:
+ raise HTTPException(
+ status_code=400, detail="User is not a valid Keycloak user or has
no refresh token"
+ )
+
+ tokens = client.refresh_token(user.refresh_token)
+ userinfo = client.userinfo(tokens["access_token"])
Review Comment:
Do we need it? Does it change between tokens? We already have his
information in `user` right?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]