Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/78.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow to specify userinfo endpoint method. @remdub
3 changes: 2 additions & 1 deletion src/pas/plugins/oidc/browser/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def __call__(self):
args, state = utils.parse_authorization_response(
self.context, qs, client, session
)
method = self.context.getProperty("userinfo_endpoint_method", "POST")
if self.context.getProperty("use_modified_openid_schema"):
IdToken.c_param.update({
"email_verified": utils.SINGLE_OPTIONAL_BOOLEAN_AS_STRING,
Expand All @@ -126,7 +127,7 @@ def __call__(self):

# The response you get back is an instance of an AccessTokenResponse
# or again possibly an ErrorResponse instance.
user_info = utils.get_user_info(client, state, args)
user_info = utils.get_user_info(client, state, args, method)
if user_info:
self.context.rememberIdentity(user_info)
self.request.response.setHeader(
Expand Down
8 changes: 8 additions & 0 deletions src/pas/plugins/oidc/controlpanel/classic.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ def identity_domain_name(self):
def identity_domain_name(self, value):
self.settings.identity_domain_name = value

@property
def userinfo_endpoint_method(self):
return self.settings.userinfo_endpoint_method

@userinfo_endpoint_method.setter
def userinfo_endpoint_method(self, value):
self.settings.userinfo_endpoint_method = value


class OIDCSettingsForm(controlpanel.RegistryEditForm):
schema = IOIDCSettings
Expand Down
7 changes: 7 additions & 0 deletions src/pas/plugins/oidc/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ class IOIDCSettings(Interface):
default="",
)

userinfo_endpoint_method = schema.Choice(
title=_("Userinfo Endpoint Method"),
description=_("HTTP Method to use for the userinfo endpoint"),
values=["GET", "POST"],
default="POST",
)


class IOIDCControlpanel(IControlpanel):
"""OIDC Control panel"""
10 changes: 9 additions & 1 deletion src/pas/plugins/oidc/locales/de/LC_MESSAGES/pas.plugins.oidc.po
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ msgstr ""
msgid "Group"
msgstr ""

#: pas/plugins/oidc/interfaces.py:141
msgid "HTTP Method to use for the userinfo endpoint"
msgstr ""

#: pas/plugins/oidc/www/oidcPluginForm.zpt:26
msgid "Id"
msgstr ""
Expand All @@ -77,7 +81,7 @@ msgstr "Installiert das Add-on pas.plugins.oidc."
msgid "List of groups that are allowed to log in."
msgstr ""

#: pas/plugins/oidc/controlpanel/classic.py:160
#: pas/plugins/oidc/controlpanel/classic.py:168
msgid "OIDC Plugin Settings"
msgstr ""

Expand Down Expand Up @@ -178,6 +182,10 @@ msgstr ""
msgid "User info property used as userid, default 'sub'."
msgstr ""

#: pas/plugins/oidc/interfaces.py:140
msgid "Userinfo Endpoint Method"
msgstr ""

#: pas/plugins/oidc/configure.zcml:33
msgid "pas.plugins.oidc"
msgstr "pas.plugins.oidc"
Expand Down
10 changes: 9 additions & 1 deletion src/pas/plugins/oidc/locales/en/LC_MESSAGES/pas.plugins.oidc.po
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ msgstr ""
msgid "Group"
msgstr ""

#: pas/plugins/oidc/interfaces.py:141
msgid "HTTP Method to use for the userinfo endpoint"
msgstr ""

#: pas/plugins/oidc/www/oidcPluginForm.zpt:26
msgid "Id"
msgstr ""
Expand All @@ -74,7 +78,7 @@ msgstr ""
msgid "List of groups that are allowed to log in."
msgstr ""

#: pas/plugins/oidc/controlpanel/classic.py:160
#: pas/plugins/oidc/controlpanel/classic.py:168
msgid "OIDC Plugin Settings"
msgstr ""

Expand Down Expand Up @@ -175,6 +179,10 @@ msgstr ""
msgid "User info property used as userid, default 'sub'."
msgstr ""

#: pas/plugins/oidc/interfaces.py:140
msgid "Userinfo Endpoint Method"
msgstr ""

#: pas/plugins/oidc/configure.zcml:33
msgid "pas.plugins.oidc"
msgstr ""
Expand Down
12 changes: 10 additions & 2 deletions src/pas/plugins/oidc/locales/es/LC_MESSAGES/pas.plugins.oidc.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ msgstr ""
"PO-Revision-Date: 2025-04-12 13:28+0200\n"
"Last-Translator: Leonardo J. Caballero G. <leonardocaballero@gmail.com>\n"
"Language-Team: ES <LL@li.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
Expand All @@ -16,6 +15,7 @@ msgstr ""
"Language-Name: Español\n"
"Preferred-Encodings: utf-8\n"
"Domain: pas.plugins.oidc\n"
"Language: es\n"
"X-Is-Fallback-For: es-ar es-bo es-cl es-co es-cr es-do es-ec es-es es-sv es-gt es-hn es-mx es-ni es-pa es-py es-pe es-pr es-us es-uy es-ve\n"
"X-Generator: Poedit 3.5\n"

Expand Down Expand Up @@ -63,6 +63,10 @@ msgstr "Crear usuario/actualizar las propiedades de usuario"
msgid "Group"
msgstr "Grupo"

#: pas/plugins/oidc/interfaces.py:141
msgid "HTTP Method to use for the userinfo endpoint"
msgstr ""

#: pas/plugins/oidc/www/oidcPluginForm.zpt:26
msgid "Id"
msgstr "Id"
Expand All @@ -79,7 +83,7 @@ msgstr "Instala el complemento pas.plugins.oidc."
msgid "List of groups that are allowed to log in."
msgstr "Lista de grupos que pueden iniciar sesión."

#: pas/plugins/oidc/controlpanel/classic.py:160
#: pas/plugins/oidc/controlpanel/classic.py:168
msgid "OIDC Plugin Settings"
msgstr "Configuración del complemento OIDC"

Expand Down Expand Up @@ -180,6 +184,10 @@ msgstr "Propiedad de información de usuario utilizada como groupid, 'groups' po
msgid "User info property used as userid, default 'sub'."
msgstr "Propiedad de información de usuario utilizada como userid, 'sub' por defecto."

#: pas/plugins/oidc/interfaces.py:140
msgid "Userinfo Endpoint Method"
msgstr ""

#: pas/plugins/oidc/configure.zcml:33
msgid "pas.plugins.oidc"
msgstr "pas.plugins.oidc"
Expand Down
12 changes: 10 additions & 2 deletions src/pas/plugins/oidc/locales/pas.plugins.oidc.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2025-03-26 10:49+0000\n"
"POT-Creation-Date: 2025-06-28 18:33+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
Expand Down Expand Up @@ -65,6 +65,10 @@ msgstr ""
msgid "Group"
msgstr ""

#: pas/plugins/oidc/interfaces.py:141
msgid "HTTP Method to use for the userinfo endpoint"
msgstr ""

#: pas/plugins/oidc/www/oidcPluginForm.zpt:26
msgid "Id"
msgstr ""
Expand All @@ -81,7 +85,7 @@ msgstr ""
msgid "List of groups that are allowed to log in."
msgstr ""

#: pas/plugins/oidc/controlpanel/classic.py:160
#: pas/plugins/oidc/controlpanel/classic.py:168
msgid "OIDC Plugin Settings"
msgstr ""

Expand Down Expand Up @@ -182,6 +186,10 @@ msgstr ""
msgid "User info property used as userid, default 'sub'."
msgstr ""

#: pas/plugins/oidc/interfaces.py:140
msgid "Userinfo Endpoint Method"
msgstr ""

#: pas/plugins/oidc/configure.zcml:33
msgid "pas.plugins.oidc"
msgstr ""
Expand Down
12 changes: 12 additions & 0 deletions src/pas/plugins/oidc/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class OIDCPlugin(BasePlugin):
use_modified_openid_schema: bool = False
user_property_as_userid: str = "sub"
identity_domain_name: str = ""
userinfo_endpoint_method: str = "POST"
userinfo_endpoint_method_values: tuple[str, ...] = ("GET", "POST")

_properties: tuple[dict] = (
{"id": "title", "type": "string", "mode": "w", "label": "Title"},
Expand Down Expand Up @@ -220,6 +222,16 @@ class OIDCPlugin(BasePlugin):
"(required for Oracle Authentication Manager) only."
),
},
{
"id": "userinfo_endpoint_method",
"type": "selection",
"select_variable": "userinfo_endpoint_method_values",
"mode": "w",
"label": (
"Userinfo Endpoint Method "
"(HTTP Method to use for the userinfo endpoint)"
),
},
)

def __init__(self, id, title=None): # noQA: A002
Expand Down
3 changes: 2 additions & 1 deletion src/pas/plugins/oidc/services/oidc/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ def reply(self) -> dict:
qs = data.get("qs", "")
qs = qs[1:] if qs.startswith("?") else qs
args, state = utils.parse_authorization_response(plugin, qs, client, session)
method = plugin.getProperty("userinfo_endpoint_method", "POST")
if plugin.getProperty("use_modified_openid_schema"):
IdToken.c_param.update({
"email_verified": utils.SINGLE_OPTIONAL_BOOLEAN_AS_STRING,
Expand All @@ -211,7 +212,7 @@ def reply(self) -> dict:

# The response you get back is an instance of an AccessTokenResponse
# or again possibly an ErrorResponse instance.
user_info = utils.get_user_info(client, state, args)
user_info = utils.get_user_info(client, state, args, method)
if user_info:
alsoProvides(self.request, IDisableCSRFProtection)
action = "login"
Expand Down
4 changes: 2 additions & 2 deletions src/pas/plugins/oidc/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def parse_authorization_response(
return args, aresp["state"]


def get_user_info(client, state, args) -> message.OpenIDSchema | dict:
def get_user_info(client, state, args, method="POST") -> message.OpenIDSchema | dict:
resp = client.do_access_token_request(
state=state,
request_args=args,
Expand All @@ -188,7 +188,7 @@ def get_user_info(client, state, args) -> message.OpenIDSchema | dict:
if client.userinfo_endpoint:
# https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
try:
user_info = client.do_user_info_request(state=state)
user_info = client.do_user_info_request(method=method, state=state)
except RequestError as exc:
logger.error(
"Authentication failed, probably missing openid scope",
Expand Down
5 changes: 3 additions & 2 deletions tests/services/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
import transaction


@pytest.fixture(scope="session")
def keycloak(keycloak_service):
@pytest.fixture(scope="session", params=["GET", "POST"])
def keycloak(keycloak_service, request):
return {
"issuer": f"{keycloak_service}/realms/plone-test",
"client_id": "plone",
Expand All @@ -24,6 +24,7 @@ def keycloak(keycloak_service):
"redirect_uris": ("/login_oidc/oidc",),
"create_restapi_ticket": True,
"identity_domain_name": "blah", # ensure non OAM SP ignores extra params/header
"userinfo_endpoint_method": request.param,
}


Expand Down