Skip to content

Commit f5af023

Browse files
committed
[DOP-29772] Fix KeycloakAuthProvider docs
1 parent 3222537 commit f5af023

File tree

8 files changed

+45
-41
lines changed

8 files changed

+45
-41
lines changed

.env.docker

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ SYNCMASTER__AUTH__KEYCLOAK__SERVER_URL=http://keycloak:8080
2626
SYNCMASTER__AUTH__KEYCLOAK__REALM_NAME=manually_created
2727
SYNCMASTER__AUTH__KEYCLOAK__CLIENT_ID=manually_created
2828
SYNCMASTER__AUTH__KEYCLOAK__CLIENT_SECRET=generated_by_keycloak
29-
SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:8000/auth/callback
29+
SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:3000/auth/callback
3030
SYNCMASTER__AUTH__KEYCLOAK__SCOPE=email
3131
SYNCMASTER__AUTH__KEYCLOAK__VERIFY_SSL=False
3232

.env.local

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export SYNCMASTER__AUTH__KEYCLOAK__SERVER_URL=http://localhost:8080
2626
export SYNCMASTER__AUTH__KEYCLOAK__REALM_NAME=manually_created
2727
export SYNCMASTER__AUTH__KEYCLOAK__CLIENT_ID=manually_created
2828
export SYNCMASTER__AUTH__KEYCLOAK__CLIENT_SECRET=generated_by_keycloak
29-
export SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:8000/auth/callback
29+
export SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:3000/auth/callback
3030
export SYNCMASTER__AUTH__KEYCLOAK__SCOPE=email
3131
export SYNCMASTER__AUTH__KEYCLOAK__VERIFY_SSL=False
3232

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Replace 307 redirect to Keycloak auth page with 401 response, due to browser restrictions for redirect + CORS + localhost.

docs/reference/server/auth/keycloak/local_installation.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@ Set ``client_authentication`` **ON** to receive client_secret
7373
Configure Redirect URI
7474
~~~~~~~~~~~~~~~~~~~~~~
7575

76-
To configure the redirect URI where the browser will redirect to exchange the code provided from Keycloak for an access token, set the `SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI` environment variable. The default value for local development is `http://localhost:8000/auth/callback`.
76+
To configure the redirect URI where the browser will redirect to exchange the code provided from Keycloak for an access token, set the `SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI` environment variable. The default value for local development is `http://localhost:3000/auth/callback`.
7777

7878
.. code-block:: console
7979
80-
$ export SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:8000/auth/callback
80+
$ export SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:3000/auth/callback
8181
8282
Configure the client redirect URI
8383
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -112,7 +112,7 @@ After this you can user `KeycloakAuthProvider` in your application with provided
112112
.. code-block:: console
113113
114114
$ export SYNCMASTER__AUTH__KEYCLOAK__SERVER_URL=http://keycloak:8080
115-
$ export SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:8000/auth/callback
115+
$ export SYNCMASTER__AUTH__KEYCLOAK__REDIRECT_URI=http://localhost:3000/auth/callback
116116
$ export SYNCMASTER__AUTH__KEYCLOAK__REALM_NAME=fastapi_realm
117117
$ export SYNCMASTER__AUTH__KEYCLOAK__CLIENT_ID=fastapi_client
118118
$ export SYNCMASTER__AUTH__KEYCLOAK__CLIENT_SECRET=6x6gn8uJdWSBmP8FqbNRSoGdvaoaFeez

syncmaster/server/api/v1/auth.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
# SPDX-FileCopyrightText: 2023-2024 MTS PJSC
22
# SPDX-License-Identifier: Apache-2.0
3-
from http.client import NOT_FOUND
3+
from http.client import NO_CONTENT
44
from typing import Annotated
55

6-
from fastapi import APIRouter, Depends, HTTPException, Request
7-
from fastapi.responses import RedirectResponse
6+
from fastapi import APIRouter, Depends, Request, Response
87
from fastapi.security import OAuth2PasswordRequestForm
98

109
from syncmaster.errors.registration import get_error_responses
@@ -17,7 +16,6 @@
1716
DummyAuthProvider,
1817
KeycloakAuthProvider,
1918
)
20-
from syncmaster.server.utils.state import validate_state
2119

2220
router = APIRouter(
2321
prefix="/auth",
@@ -42,21 +40,17 @@ async def token(
4240
return AuthTokenSchema.model_validate(token)
4341

4442

45-
@router.get("/callback")
43+
@router.get("/callback", status_code=NO_CONTENT)
4644
async def auth_callback(
4745
request: Request,
4846
code: str,
49-
state: str,
5047
auth_provider: Annotated[KeycloakAuthProvider, Depends(Stub(AuthProvider))],
5148
):
52-
original_redirect_url = validate_state(state)
53-
if not original_redirect_url:
54-
raise HTTPException(status_code=NOT_FOUND, detail="Invalid state parameter")
5549
token = await auth_provider.get_token_authorization_code_grant(
5650
code=code,
5751
redirect_uri=auth_provider.settings.keycloak.redirect_uri,
5852
)
5953
request.session["access_token"] = token["access_token"]
6054
request.session["refresh_token"] = token["refresh_token"]
6155

62-
return RedirectResponse(url=original_redirect_url)
56+
return Response(status_code=NO_CONTENT)

syncmaster/server/providers/auth/keycloak_provider.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from syncmaster.server.providers.auth.base_provider import AuthProvider
1414
from syncmaster.server.services.unit_of_work import UnitOfWork
1515
from syncmaster.server.settings.auth.keycloak import KeycloakAuthProviderSettings
16-
from syncmaster.server.utils.state import generate_state
1716

1817
log = logging.getLogger(__name__)
1918

@@ -137,10 +136,8 @@ async def refresh_access_token(self, refresh_token: str) -> dict[str, Any]:
137136
return new_tokens
138137

139138
def redirect_to_auth(self, path: str) -> None:
140-
state = generate_state(path)
141139
auth_url = self.keycloak_openid.auth_url(
142140
redirect_uri=self.settings.keycloak.redirect_uri,
143141
scope=self.settings.keycloak.scope,
144-
state=state,
145142
)
146143
raise RedirectException(redirect_url=auth_url)

syncmaster/server/utils/state.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/test_unit/test_auth/test_auth_keycloak.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,18 @@
2424
],
2525
indirect=True,
2626
)
27-
async def test_get_keycloak_user_unauthorized(client: AsyncClient, mock_keycloak_well_known):
27+
async def test_keycloak_get_user_unauthorized(client: AsyncClient, mock_keycloak_well_known):
2828
response = await client.get("/v1/users/some_user_id")
2929

3030
# redirect unauthorized user to Keycloak
3131
assert response.status_code == 401, response.text
32-
33-
details = IsStr()
3432
assert response.json() == {
3533
"error": {
3634
"code": "unauthorized",
3735
"message": "Please authorize using provided URL",
38-
"details": details,
36+
"details": IsStr(regex=r".*protocol/openid-connect/auth\?.*"),
3937
},
4038
}
41-
assert "protocol/openid-connect/auth?" in details
4239

4340

4441
@responses.activate
@@ -54,7 +51,7 @@ async def test_get_keycloak_user_unauthorized(client: AsyncClient, mock_keycloak
5451
],
5552
indirect=True,
5653
)
57-
async def test_get_keycloak_user_authorized(
54+
async def test_keycloak_get_user_authorized(
5855
client: AsyncClient,
5956
simple_user: MockUser,
6057
settings: Settings,
@@ -92,7 +89,7 @@ async def test_get_keycloak_user_authorized(
9289
],
9390
indirect=True,
9491
)
95-
async def test_get_keycloak_user_expired_access_token(
92+
async def test_keycloak_get_user_expired_access_token(
9693
caplog,
9794
client: AsyncClient,
9895
simple_user: MockUser,
@@ -137,7 +134,7 @@ async def test_get_keycloak_user_expired_access_token(
137134
],
138135
indirect=True,
139136
)
140-
async def test_get_keycloak_user_inactive(
137+
async def test_keycloak_get_user_inactive(
141138
client: AsyncClient,
142139
simple_user: MockUser,
143140
inactive_user: MockUser,
@@ -163,3 +160,33 @@ async def test_get_keycloak_user_inactive(
163160
"details": None,
164161
},
165162
}
163+
164+
165+
@responses.activate
166+
@pytest.mark.parametrize(
167+
"settings",
168+
[
169+
{
170+
"auth": {
171+
"provider": KEYCLOAK_PROVIDER,
172+
},
173+
},
174+
],
175+
indirect=True,
176+
)
177+
async def test_keycloak_auth_callback(
178+
client: AsyncClient,
179+
settings: Settings,
180+
mock_keycloak_well_known,
181+
mock_keycloak_realm,
182+
mock_keycloak_token_refresh,
183+
caplog,
184+
):
185+
with caplog.at_level(logging.DEBUG):
186+
response = await client.get(
187+
"/v1/auth/callback",
188+
params={"code": "testcode"},
189+
)
190+
191+
assert response.cookies.get("session"), caplog.text # cookie is set
192+
assert response.status_code == 204, response.json()

0 commit comments

Comments
 (0)