import pytest @pytest.mark.asyncio async def test_deep_link_redirects_to_login_with_next(client): resp = await client.get("/scopes") assert resp.status_code == 302 assert resp.headers["location"] == "/login?next=%2Fscopes" @pytest.mark.asyncio async def test_deep_link_preserves_query_string(client): resp = await client.get("/scopes?description=alpha") assert resp.status_code == 302 assert resp.headers["location"] == "/login?next=%2Fscopes%3Fdescription%3Dalpha" @pytest.mark.asyncio async def test_root_redirect_has_no_next(client): resp = await client.get("/") assert resp.status_code == 302 assert resp.headers["location"] == "/login" @pytest.mark.asyncio async def test_login_page_renders_next_field(client): resp = await client.get("/login?next=/scopes") assert resp.status_code == 200 assert 'name="next" value="/scopes"' in resp.text @pytest.mark.asyncio async def test_login_redirects_to_next_on_success(client, make_user): await make_user("u@example.com") resp = await client.post( "/login", data={"email": "u@example.com", "password": "secret123", "next": "/scopes"}, ) assert resp.status_code == 303 assert resp.headers["location"] == "/scopes" @pytest.mark.asyncio async def test_login_defaults_to_root_without_next(client, make_user): await make_user("u2@example.com") resp = await client.post( "/login", data={"email": "u2@example.com", "password": "secret123"} ) assert resp.status_code == 303 assert resp.headers["location"] == "/" @pytest.mark.asyncio async def test_login_rejects_unsafe_next(client, make_user): await make_user("u3@example.com") for bad in ["https://evil.com", "//evil.com", "/\\evil.com", "/login"]: resp = await client.post( "/login", data={"email": "u3@example.com", "password": "secret123", "next": bad}, ) assert resp.status_code == 303 assert resp.headers["location"] == "/", bad @pytest.mark.asyncio async def test_failed_login_preserves_next(client, make_user): await make_user("u4@example.com") resp = await client.post( "/login", data={"email": "u4@example.com", "password": "wrong", "next": "/scopes"}, ) assert resp.status_code == 401 assert 'name="next" value="/scopes"' in resp.text