Skip to content

Commit

Permalink
✅ Add tests for sessionrefresh flow
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenbal committed Jul 1, 2024
1 parent f6f9e2c commit bcd89a2
Show file tree
Hide file tree
Showing 6 changed files with 1,231 additions and 0 deletions.
3 changes: 3 additions & 0 deletions testapp/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
OIDCAuthenticationRequestView,
)

from .views import custom_callback_view_init

urlpatterns = [
path("admin/login/failure/", AdminLoginFailure.as_view(), name="admin-oidc-error"),
path("admin/", admin.site.urls),
path("login", OIDCAuthenticationRequestView.as_view(), name="login"),
path("oidc/", include("mozilla_django_oidc.urls")),
path("custom-init-login/", custom_callback_view_init, name="custom-init-login"),
] + staticfiles_urlpatterns()

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def mock_state_and_nonce(mocker):
"mozilla_django_oidc.views.get_random_string",
return_value="not-a-random-string",
)
mocker.patch(
"mozilla_django_oidc.middleware.get_random_string",
return_value="not-a-random-string",
)


@pytest.fixture
Expand Down
48 changes: 48 additions & 0 deletions tests/test_integration_multiple_configs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from django.test import Client
from django.urls import reverse

import pytest
from requests import Session

from mozilla_django_oidc_db.models import OpenIDConnectConfig

from .utils import keycloak_login


@pytest.mark.vcr
@pytest.mark.oidcconfig(make_users_staff=True)
def test_use_config_class_from_state_over_config_class_from_session(
keycloak_config: OpenIDConnectConfig,
mock_state_and_nonce,
client: Client,
):
"""
When using two different OIDC configs, ensure that their state doesn't get mixed up.
First, we authenticate in the django admin, this is the config that uses the
session refresh, and the config set up through fixtures.
Second, we have another OIDC config that uses another provider. The state of the
first authentication may not affect the second authentication flow.
"""
session = Session()
# login to the admin
login_url = reverse("login")
django_login_response = client.get(login_url)
redirect_uri = keycloak_login(django_login_response["Location"], session=session)
callback_response = client.get(redirect_uri, follow=True)
# sanity check
assert callback_response.wsgi_request.path == reverse("admin:index")

# set up an authentication flow & state with another config - all the credentials
# are otherwise the same - the only difference is where the callback redirects after
# succesful authentication
login_url2 = reverse("custom-init-login")
django_login_response2 = client.get(login_url2)
# we expect to still be authenticated in the keycloak session, so we can fetch the
# URL directly - and perform a sanity check!
_response = session.get(django_login_response2["Location"], allow_redirects=False)
redirect_uri2 = _response.headers["Location"]
assert redirect_uri2.startswith("http://testserver/")
callback_response2 = client.get(redirect_uri2, follow=True)
assert callback_response2.wsgi_request.path == "/custom-success-url"
61 changes: 61 additions & 0 deletions tests/test_integration_oidc_flow_variants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from urllib.parse import parse_qs, urlparse

from django.urls import reverse

import pytest
import requests

from mozilla_django_oidc_db.models import (
OpenIDConnectConfig,
Expand Down Expand Up @@ -110,3 +113,61 @@ def test_return_jwt_from_userinfo_endpoint(

# a user was created
assert django_user_model.objects.count() == 1


@pytest.mark.vcr
@pytest.mark.oidcconfig(make_users_staff=True)
def test_session_refresh(
keycloak_config,
settings,
mock_state_and_nonce,
client,
django_user_model,
vcr,
mocker,
):
session = requests.Session()
settings.MIDDLEWARE = settings.MIDDLEWARE + [
"mozilla_django_oidc_db.middleware.SessionRefresh"
]
settings.OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = 60
login_url = reverse("login")

django_login_response = client.get(login_url)
# simulate login to Keycloak
redirect_uri = keycloak_login(django_login_response["Location"], session=session)
# complete the login flow on our end
callback_response = client.get(redirect_uri)

assert callback_response.status_code == 302
assert callback_response["Location"] == "/admin/"

admin_response = client.get("/admin/")

# User was successfully logged in
assert admin_response.status_code == 200

# when the user refreshes the admin index, SessionRefresh should be called and
# should redirect user to Keycloak
mocker.patch("mozilla_django_oidc.middleware.time.time", lambda: 10**11)

admin_response = client.get("/admin/")

assert "/realms/test/protocol/openid-connect/auth" in admin_response["Location"]

# Following the Keycloak response should redirect the user to the callback immediately,
# because the user still has an active session with Keycloak
keycloak_response = session.get(admin_response["Location"], allow_redirects=False)

assert keycloak_response.status_code == 302
assert "/oidc/callback/" in keycloak_response.headers["Location"]

app_response = client.get(keycloak_response.headers["Location"])

assert app_response.status_code == 302
assert app_response.url == "/admin/"

admin_response = client.get("/admin/")

# User can reach the admin index again
assert admin_response.status_code == 200

0 comments on commit bcd89a2

Please sign in to comment.