forked from Github-Mirrors/canaille
405 lines
12 KiB
Python
405 lines
12 KiB
Python
from authlib.oidc.core.grants.util import generate_id_token
|
|
from canaille.oidc.oauth import generate_user_info
|
|
from canaille.oidc.oauth import get_jwt_config
|
|
|
|
|
|
def test_end_session(testclient, backend, logged_user, client, id_token):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=302,
|
|
)
|
|
|
|
assert res.location == f"{post_logout_redirect_url}?state=foobar"
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_end_session_no_client_id(testclient, backend, logged_user, client, id_token):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=302,
|
|
)
|
|
|
|
assert res.location == f"{post_logout_redirect_url}?state=foobar"
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_no_redirect_uri_no_redirect(
|
|
testclient, backend, logged_user, client, id_token
|
|
):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"state": "foobar",
|
|
},
|
|
status=302,
|
|
)
|
|
|
|
assert res.location == "/"
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_bad_redirect_uri_no_redirect(
|
|
testclient, backend, logged_user, client, id_token
|
|
):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/invalid-uri"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=302,
|
|
)
|
|
|
|
assert res.location == "/"
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_no_client_hint_no_redirect(testclient, backend, logged_user, client, id_token):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"logout_hint": logged_user.identifier,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
res = res.form.submit(name="answer", value="logout", status=302)
|
|
res = res.follow(status=302)
|
|
|
|
assert res.location == "/"
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_end_session_invalid_client_id(testclient, backend, logged_user, client):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"logout_hint": logged_user.identifier,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"client_id": "invalid_client_id",
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
res = res.form.submit(name="answer", value="logout", status=302)
|
|
res = res.follow(status=302)
|
|
|
|
assert res.location == "/"
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_client_hint_invalid(testclient, backend, logged_user, client):
|
|
id_token = generate_id_token(
|
|
{},
|
|
generate_user_info(logged_user, client.scope),
|
|
aud="invalid-client-id",
|
|
**get_jwt_config(None),
|
|
)
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=302,
|
|
)
|
|
|
|
assert res.location == "/"
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_no_jwt_logout(testclient, backend, logged_user, client):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
res = res.form.submit(name="answer", value="logout", status=302)
|
|
res = res.follow(status=302)
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
assert res.location == f"{post_logout_redirect_url}?state=foobar"
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_no_jwt_no_logout(testclient, backend, logged_user, client):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
res = res.form.submit(name="answer", value="stay", status=302)
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert sess.get("user_id")
|
|
|
|
assert res.location == "/"
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
|
|
def test_jwt_not_issued_here(testclient, backend, logged_user, client, id_token):
|
|
testclient.app.config["OIDC"]["JWT"]["ISS"] = "https://foo.bar"
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
assert res.json == {
|
|
"message": "id_token_hint has not been issued here",
|
|
"status": "error",
|
|
}
|
|
|
|
|
|
def test_client_hint_mismatch(testclient, backend, logged_user, client):
|
|
id_token = generate_id_token(
|
|
{},
|
|
generate_user_info(logged_user, client.scope),
|
|
aud="another_client_id",
|
|
**get_jwt_config(None),
|
|
)
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
assert res.json == {
|
|
"message": "id_token audience and client_id don't match",
|
|
"status": "error",
|
|
}
|
|
|
|
|
|
def test_bad_user_id_token_mismatch(testclient, backend, logged_user, client, admin):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
id_token = generate_id_token(
|
|
{},
|
|
generate_user_info(admin, client.scope),
|
|
aud=client.client_id,
|
|
**get_jwt_config(None),
|
|
)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
res = res.form.submit(name="answer", value="logout", status=302)
|
|
res = res.follow(status=302)
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
assert res.location == f"{post_logout_redirect_url}?state=foobar"
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_bad_user_hint(testclient, backend, logged_user, client, id_token, admin):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": admin.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
res = res.form.submit(name="answer", value="logout", status=302)
|
|
res = res.follow(status=302)
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
assert res.location == f"{post_logout_redirect_url}?state=foobar"
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|
|
|
|
|
|
def test_no_jwt_bad_csrf(testclient, backend, logged_user, client):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
form = res.form
|
|
form["csrf_token"] = "foobar"
|
|
res = form.submit(name="answer", value="logout", status=400)
|
|
|
|
|
|
def test_end_session_already_disconnected(testclient, backend, user, client, id_token):
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
"state": "foobar",
|
|
},
|
|
status=302,
|
|
)
|
|
|
|
assert res.location == "/"
|
|
|
|
|
|
def test_end_session_no_state(testclient, backend, logged_user, client, id_token):
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
|
|
|
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
|
res = testclient.get(
|
|
"/oauth/end_session",
|
|
params={
|
|
"id_token_hint": id_token,
|
|
"logout_hint": logged_user.identifier,
|
|
"client_id": client.client_id,
|
|
"post_logout_redirect_uri": post_logout_redirect_url,
|
|
},
|
|
status=302,
|
|
)
|
|
|
|
assert res.location == post_logout_redirect_url
|
|
|
|
with testclient.session_transaction() as sess:
|
|
assert not sess.get("user_id")
|
|
|
|
testclient.get(f"/profile/{logged_user.identifier}", status=403)
|