Skip to content

Commit cdf69bc

Browse files
committed
Support OIDC client authorization_parameters #861
1 parent c226509 commit cdf69bc

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

openeo/rest/auth/oidc.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ def __init__(
256256
title: str = None,
257257
default_clients: Union[List[dict], None] = None,
258258
requests_session: Optional[requests.Session] = None,
259+
authorization_parameters: Optional[dict] = None,
259260
):
260261
# TODO: id and title are required in the openEO API spec.
261262
self.id = provider_id
@@ -280,6 +281,7 @@ def __init__(
280281
self._scopes = {"openid"}.union(scopes or []).intersection(self._supported_scopes)
281282
log.debug(f"Scopes: provider supported {self._supported_scopes} & backend desired {scopes} -> {self._scopes}")
282283
self.default_clients = default_clients
284+
self.authorization_parameters = authorization_parameters or {}
283285

284286
@classmethod
285287
def from_dict(cls, data: dict) -> OidcProviderInfo:
@@ -289,6 +291,7 @@ def from_dict(cls, data: dict) -> OidcProviderInfo:
289291
issuer=data["issuer"],
290292
scopes=data.get("scopes"),
291293
default_clients=data.get("default_clients"),
294+
authorization_parameters=data.get("authorization_parameters"),
292295
)
293296

294297
def get_scopes_string(self, request_refresh_token: bool = False) -> str:
@@ -563,6 +566,7 @@ def _get_auth_code(self, request_refresh_token: bool = False) -> AuthCodeResult:
563566
"nonce": nonce,
564567
"code_challenge": pkce.code_challenge,
565568
"code_challenge_method": pkce.code_challenge_method,
569+
**self._client_info.provider.authorization_parameters,
566570
}
567571
),
568572
)
@@ -855,6 +859,7 @@ def _get_verification_info(self, request_refresh_token: bool = False) -> Verific
855859
if self._pkce:
856860
post_data["code_challenge"] = self._pkce.code_challenge
857861
post_data["code_challenge_method"] = self._pkce.code_challenge_method
862+
post_data.update(self._client_info.provider.authorization_parameters)
858863
resp = self._requests.post(url=self._device_code_url, data=post_data)
859864
if resp.status_code != 200:
860865
raise OidcException(

tests/rest/auth/test_oidc.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import re
33
import time
4+
import urllib.parse
45
from io import BytesIO
56
from queue import Queue
67

@@ -818,3 +819,95 @@ def post_token(request, context):
818819

819820
assert tokens.access_token == "6cce5-t0k3n"
820821
assert len(adapter.request_history) == 2
822+
823+
824+
class TestOidcProviderInfoAuthorizationParameters:
825+
"""Tests for the authorization_parameters feature (openEO API 1.3.0, issue #534)"""
826+
827+
def test_from_dict_with_authorization_parameters(self, requests_mock):
828+
requests_mock.get("https://authit.test/.well-known/openid-configuration", json={"scopes_supported": ["openid"]})
829+
data = {
830+
"id": "google",
831+
"title": "Google",
832+
"issuer": "https://authit.test",
833+
"scopes": ["openid"],
834+
"authorization_parameters": {"access_type": "offline", "prompt": "consent"},
835+
}
836+
info = OidcProviderInfo.from_dict(data)
837+
assert info.authorization_parameters == {"access_type": "offline", "prompt": "consent"}
838+
839+
def test_from_dict_without_authorization_parameters(self, requests_mock):
840+
requests_mock.get("https://authit.test/.well-known/openid-configuration", json={"scopes_supported": ["openid"]})
841+
data = {
842+
"id": "egi",
843+
"title": "EGI",
844+
"issuer": "https://authit.test",
845+
}
846+
info = OidcProviderInfo.from_dict(data)
847+
assert info.authorization_parameters == {}
848+
849+
def test_device_code_request_includes_authorization_parameters(self, requests_mock):
850+
"""Checks whether the authorization_parameters ends up in the device code POST body."""
851+
oidc_issuer = "https://authit.test"
852+
requests_mock.get(
853+
f"{oidc_issuer}/.well-known/openid-configuration",
854+
json={
855+
"scopes_supported": ["openid"],
856+
"device_authorization_endpoint": f"{oidc_issuer}/device_code",
857+
"token_endpoint": f"{oidc_issuer}/token",
858+
},
859+
)
860+
device_code_mock = requests_mock.post(
861+
f"{oidc_issuer}/device_code",
862+
json={
863+
"device_code": "d3v1c3",
864+
"user_code": "US3R",
865+
"verification_uri": f"{oidc_issuer}/dc",
866+
"interval": 5,
867+
},
868+
)
869+
provider = OidcProviderInfo(
870+
issuer=oidc_issuer,
871+
authorization_parameters={"access_type": "offline", "prompt": "consent"},
872+
)
873+
authenticator = OidcDeviceAuthenticator(
874+
client_info=OidcClientInfo(client_id="myclient", provider=provider, client_secret="s3cr3t"),
875+
)
876+
authenticator._get_verification_info()
877+
878+
assert device_code_mock.call_count == 1
879+
post_body = urllib.parse.parse_qs(device_code_mock.last_request.text)
880+
assert post_body["client_id"] == ["myclient"]
881+
assert post_body["access_type"] == ["offline"]
882+
assert post_body["prompt"] == ["consent"]
883+
884+
def test_device_code_request_without_authorization_parameters(self, requests_mock):
885+
"""Verify no extra params when authorization_parameters is empty."""
886+
oidc_issuer = "https://authit.test"
887+
requests_mock.get(
888+
f"{oidc_issuer}/.well-known/openid-configuration",
889+
json={
890+
"scopes_supported": ["openid"],
891+
"device_authorization_endpoint": f"{oidc_issuer}/device_code",
892+
"token_endpoint": f"{oidc_issuer}/token",
893+
},
894+
)
895+
device_code_mock = requests_mock.post(
896+
f"{oidc_issuer}/device_code",
897+
json={
898+
"device_code": "d3v1c3",
899+
"user_code": "US3R",
900+
"verification_uri": f"{oidc_issuer}/dc",
901+
"interval": 5,
902+
},
903+
)
904+
provider = OidcProviderInfo(issuer=oidc_issuer)
905+
authenticator = OidcDeviceAuthenticator(
906+
client_info=OidcClientInfo(client_id="myclient", provider=provider, client_secret="s3cr3t"),
907+
)
908+
authenticator._get_verification_info()
909+
910+
post_body = urllib.parse.parse_qs(device_code_mock.last_request.text)
911+
assert "access_type" not in post_body
912+
assert "prompt" not in post_body
913+
assert set(post_body.keys()) == {"client_id", "scope"}

0 commit comments

Comments
 (0)