forked from Github-Mirrors/canaille
fix: post_logout_redirect_uris was ignored during OIDC dynamic registration
This commit is contained in:
parent
52ce547a54
commit
06b60e1747
9 changed files with 85 additions and 33 deletions
|
@ -12,6 +12,7 @@ Fixed
|
|||
*****
|
||||
|
||||
- Correctly set up Client audience during OIDC dynamic registration.
|
||||
- ``post_logout_redirect_uris`` was ignored during OIDC dynamic registration.
|
||||
|
||||
[0.0.40] - 2023-12-22
|
||||
=====================
|
||||
|
|
|
@ -3,6 +3,7 @@ import uuid
|
|||
|
||||
from authlib.integrations.flask_oauth2 import current_token
|
||||
from authlib.jose import jwt
|
||||
from authlib.jose.errors import JoseError
|
||||
from authlib.oauth2 import OAuth2Error
|
||||
from canaille import csrf
|
||||
from canaille.app import models
|
||||
|
@ -273,9 +274,18 @@ def end_session():
|
|||
return render_template("logout.html", form=form, client=client, menu=False)
|
||||
|
||||
if data.get("id_token_hint"):
|
||||
try:
|
||||
id_token = jwt.decode(
|
||||
data["id_token_hint"], current_app.config["OIDC"]["JWT"]["PUBLIC_KEY"]
|
||||
)
|
||||
except JoseError as exc:
|
||||
return jsonify(
|
||||
{
|
||||
"status": "error",
|
||||
"message": str(exc),
|
||||
}
|
||||
)
|
||||
|
||||
if not id_token["iss"] == get_issuer():
|
||||
return jsonify(
|
||||
{
|
||||
|
|
|
@ -398,6 +398,10 @@ class ClientRegistrationEndpoint(ClientManagementMixin, _ClientRegistrationEndpo
|
|||
|
||||
def save_client(self, client_info, client_metadata, request):
|
||||
client = models.Client(
|
||||
# this won't be needed when OIDC RP Initiated Logout is
|
||||
# directly implemented in authlib:
|
||||
# https://gitlab.com/yaal/canaille/-/issues/157
|
||||
post_logout_redirect_uris=request.data.get("post_logout_redirect_uris"),
|
||||
**self.client_convert_data(**client_info, **client_metadata)
|
||||
)
|
||||
client.audience = [client]
|
||||
|
|
|
@ -13,23 +13,10 @@ from flask import session
|
|||
from flask import url_for
|
||||
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
app.config.from_envvar("CONFIG")
|
||||
app.static_folder = "../../canaille/static"
|
||||
oauth = OAuth()
|
||||
|
||||
oauth = OAuth()
|
||||
oauth.init_app(app)
|
||||
oauth.register(
|
||||
name="canaille",
|
||||
client_id=app.config["OAUTH_CLIENT_ID"],
|
||||
client_secret=app.config["OAUTH_CLIENT_SECRET"],
|
||||
server_metadata_url=get_well_known_url(
|
||||
app.config["OAUTH_AUTH_SERVER"], external=True
|
||||
),
|
||||
client_kwargs={"scope": "openid profile email phone address groups"},
|
||||
)
|
||||
|
||||
def setup_routes(app):
|
||||
@app.route("/")
|
||||
@app.route("/tos")
|
||||
@app.route("/policy")
|
||||
|
@ -40,10 +27,12 @@ def create_app():
|
|||
|
||||
@app.route("/login")
|
||||
def login():
|
||||
return oauth.canaille.authorize_redirect(url_for("authorize", _external=True))
|
||||
return oauth.canaille.authorize_redirect(
|
||||
url_for("login_callback", _external=True)
|
||||
)
|
||||
|
||||
@app.route("/authorize")
|
||||
def authorize():
|
||||
@app.route("/login_callback")
|
||||
def login_callback():
|
||||
try:
|
||||
token = oauth.canaille.authorize_access_token()
|
||||
session["user"] = token.get("userinfo")
|
||||
|
@ -56,13 +45,6 @@ def create_app():
|
|||
|
||||
@app.route("/logout")
|
||||
def logout():
|
||||
try:
|
||||
del session["user"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
flash("You have been successfully logged out", "success")
|
||||
|
||||
oauth.canaille.load_server_metadata()
|
||||
end_session_endpoint = oauth.canaille.server_metadata.get(
|
||||
"end_session_endpoint"
|
||||
|
@ -71,10 +53,41 @@ def create_app():
|
|||
end_session_endpoint,
|
||||
client_id=current_app.config["OAUTH_CLIENT_ID"],
|
||||
id_token_hint=session["id_token"],
|
||||
post_logout_redirect_uri=url_for("index", _external=True),
|
||||
post_logout_redirect_uri=url_for("logout_callback", _external=True),
|
||||
)
|
||||
return redirect(end_session_url)
|
||||
|
||||
@app.route("/logout_callback")
|
||||
def logout_callback():
|
||||
try:
|
||||
del session["user"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
flash("You have been successfully logged out", "success")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
def setup_oauth(app):
|
||||
oauth.init_app(app)
|
||||
oauth.register(
|
||||
name="canaille",
|
||||
client_id=app.config["OAUTH_CLIENT_ID"],
|
||||
client_secret=app.config["OAUTH_CLIENT_SECRET"],
|
||||
server_metadata_url=get_well_known_url(
|
||||
app.config["OAUTH_AUTH_SERVER"], external=True
|
||||
),
|
||||
client_kwargs={"scope": "openid profile email phone address groups"},
|
||||
)
|
||||
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
app.config.from_envvar("CONFIG")
|
||||
app.static_folder = "../../canaille/static"
|
||||
|
||||
setup_routes(app)
|
||||
setup_oauth(app)
|
||||
return app
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
SECRET_KEY="46bf9fb5-88d5-489b-9312-899588377ff0"
|
||||
NAME = "Client 1"
|
||||
SESSION_COOKIE_NAME="client1-session"
|
||||
SERVER_NAME="localhost:5001"
|
||||
|
||||
OAUTH_CLIENT_ID="1JGkkzCbeHpGtlqgI5EENByf"
|
||||
OAUTH_CLIENT_SECRET="2xYPSReTQRmGG1yppMVZQ0ASXwFejPyirvuPbKhNa6TmKC5x"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
SECRET_KEY="8e953ecc-13be-497b-806f-c65faa1e328f"
|
||||
NAME = "Client 2"
|
||||
SESSION_COOKIE_NAME="client2-session"
|
||||
SERVER_NAME="localhost:5002"
|
||||
|
||||
OAUTH_CLIENT_ID="gn4yFN7GDykL7QP8v8gS9YfV"
|
||||
OAUTH_CLIENT_SECRET="ouFJE5WpICt6hxTyf8icXPeeklMektMY4gV0Rmf3aY60VElA"
|
||||
|
|
|
@ -107,8 +107,8 @@ def populate(app):
|
|||
client_name="Client1",
|
||||
contacts=["admin@mydomain.tld"],
|
||||
client_uri="http://localhost:5001",
|
||||
redirect_uris=["http://localhost:5001/authorize"],
|
||||
post_logout_redirect_uris=["http://localhost:5001/"],
|
||||
redirect_uris=["http://localhost:5001/login_callback"],
|
||||
post_logout_redirect_uris=["http://localhost:5001/logout_callback"],
|
||||
tos_uri="http://localhost:5001/tos",
|
||||
policy_uri="http://localhost:5001/policy",
|
||||
grant_types=["authorization_code", "refresh_token"],
|
||||
|
@ -126,8 +126,8 @@ def populate(app):
|
|||
client_name="Client2",
|
||||
contacts=["admin@mydomain.tld"],
|
||||
client_uri="http://localhost:5002",
|
||||
redirect_uris=["http://localhost:5002/authorize"],
|
||||
post_logout_redirect_uris=["http://localhost:5002/"],
|
||||
redirect_uris=["http://localhost:5002/login_callback"],
|
||||
post_logout_redirect_uris=["http://localhost:5002/logout_callback"],
|
||||
tos_uri="http://localhost:5002/tos",
|
||||
policy_uri="http://localhost:5002/policy",
|
||||
grant_types=["authorization_code", "refresh_token"],
|
||||
|
|
|
@ -19,6 +19,9 @@ def test_client_registration_with_authentication_static_token(
|
|||
"https://client.example.org/callback",
|
||||
"https://client.example.org/callback2",
|
||||
],
|
||||
"post_logout_redirect_uris": [
|
||||
"https://client.example.org/logout_callback",
|
||||
],
|
||||
"client_name": "My Example Client",
|
||||
"token_endpoint_auth_method": "client_secret_basic",
|
||||
"logo_uri": "https://client.example.org/logo.webp",
|
||||
|
@ -53,6 +56,9 @@ def test_client_registration_with_authentication_static_token(
|
|||
"https://client.example.org/callback",
|
||||
"https://client.example.org/callback2",
|
||||
]
|
||||
assert client.post_logout_redirect_uris == [
|
||||
"https://client.example.org/logout_callback",
|
||||
]
|
||||
assert client.token_endpoint_auth_method == "client_secret_basic"
|
||||
assert client.logo_uri == "https://client.example.org/logo.webp"
|
||||
assert client.jwks_uri == "https://client.example.org/my_public_keys.jwks"
|
||||
|
|
|
@ -284,6 +284,22 @@ def test_client_hint_mismatch(testclient, backend, logged_user, client):
|
|||
}
|
||||
|
||||
|
||||
def test_end_session_bad_id_token(testclient, backend, logged_user, client, id_token):
|
||||
post_logout_redirect_url = "https://mydomain.tld/disconnected"
|
||||
res = testclient.get(
|
||||
"/oauth/end_session",
|
||||
params={
|
||||
"id_token_hint": "invalid",
|
||||
"logout_hint": logged_user.identifier,
|
||||
"client_id": client.client_id,
|
||||
"post_logout_redirect_uri": post_logout_redirect_url,
|
||||
"state": "foobar",
|
||||
},
|
||||
)
|
||||
|
||||
assert res.json == {"status": "error", "message": "Invalid input segments length: "}
|
||||
|
||||
|
||||
def test_bad_user_id_token_mismatch(testclient, backend, logged_user, client, admin):
|
||||
testclient.get(f"/profile/{logged_user.identifier}", status=200)
|
||||
|
||||
|
|
Loading…
Reference in a new issue