Skip to content

Commit 9fc731a

Browse files
authored
Merge pull request #204 from querti/add-token-oidc
Add Client Credentials Flow OIDC authentication
2 parents f74943e + 03e86bc commit 9fc731a

File tree

7 files changed

+97
-4
lines changed

7 files changed

+97
-4
lines changed

kobo/admin/templates/client/__project_name__.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Hub XML-RPC address.
44
HUB_URL = "http://localhost:8000/xmlrpc"
55

6-
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc
6+
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc, token_oidc
77
AUTH_METHOD = ""
88

99
# Username and password

kobo/admin/templates/worker/__project_name__.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Hub XML-RPC address.
44
HUB_URL = "http://localhost.localdomain:8000/xmlrpc"
55

6-
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc
6+
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc, token_oidc
77
AUTH_METHOD = ""
88

99
# Username and password

kobo/client/__init__.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,48 @@ def _login_krbv(self):
308308
req_enc = encode_func(req)
309309

310310
self._hub.auth.login_krbv(req_enc)
311+
312+
def _login_token_oidc(self):
313+
"""Login using OIDC endpoint (with Client Credentials Flow - using tokens)"""
314+
login_url = urlparse.urljoin(self._hub_url, "auth/tokenoidclogin/")
315+
client_id = self._conf.get("OIDC_CLIENT_ID")
316+
client_secret = self._conf.get("OIDC_CLIENT_SECRET")
317+
auth_server_token_url = self._conf.get("OIDC_AUTH_SERVER_TOKEN_URL")
318+
request_args = {}
319+
320+
import requests
321+
322+
token_data = {
323+
"grant_type": "client_credentials",
324+
"client_id": client_id,
325+
"client_secret": client_secret,
326+
"scope": "openid"
327+
}
328+
token_response = requests.post(auth_server_token_url, data=token_data, timeout=30)
329+
token_response.raise_for_status()
330+
headers = {"Authorization": f"Bearer {token_response.json()['access_token']}"}
331+
332+
# NOTE behavior difference from hub proxy overall:
333+
# HubProxy by default DOES NOT verify https connections :(
334+
# See the constructor. It could be repeated here by defaulting verify to False,
335+
# but let's not do that, instead you must have an unbroken SSL setup to
336+
# use this auth method.
337+
if self._conf.get("CA_CERT"):
338+
request_args["verify"] = self._conf["CA_CERT"]
339+
340+
with requests.Session() as s:
341+
s.cookies = self._transport.cookiejar
342+
response = s.get(
343+
login_url,
344+
headers=headers,
345+
**request_args
346+
)
347+
348+
self._logger and self._logger.debug(
349+
"Login response: %s %s", response, response.headers
350+
)
351+
response.raise_for_status()
352+
311353

312354
def _login_gssapi(self):
313355
"""Login using kerberos credentials (uses gssapi)."""

kobo/client/default.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Hub xml-rpc address.
22
HUB_URL = "https://localhost/hub/xmlrpc"
33

4-
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc
4+
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc, token_oidc
55
AUTH_METHOD = "krbv"
66

77
# Username and password

kobo/hub/urls/auth.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
url(r"^login/$", kobo.hub.views.LoginView.as_view(), name="auth/login"),
1515
url(r"^krb5login/$", kobo.hub.views.krb5login, name="auth/krb5login"),
1616
url(r"^oidclogin/$", kobo.hub.views.oidclogin, name="auth/oidclogin"),
17+
url(r"^tokenoidclogin/$", kobo.hub.views.oidclogin, name="auth/tokenoidclogin"),
1718
url(r'^logout/$', kobo.hub.views.LogoutView.as_view(), name="auth/logout"),
1819
]
1920

@@ -22,5 +23,6 @@
2223
url(r"^login/$", kobo.hub.views.login, name="auth/login"),
2324
url(r"^krb5login/$", kobo.hub.views.krb5login, name="auth/krb5login"),
2425
url(r"^oidclogin/$", kobo.hub.views.oidclogin, name="auth/oidclogin"),
26+
url(r"^tokenoidclogin/$", kobo.hub.views.oidclogin, name="auth/tokenoidclogin"),
2527
url(r'^logout/$', kobo.hub.views.logout, name="auth/logout"),
2628
]

kobo/worker/default.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Hub xml-rpc address.
22
HUB_URL = "https://localhost/hub/xmlrpc"
33

4-
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc
4+
# Hub authentication method. Example: krbv, gssapi, password, worker_key, oidc, token_oidc
55
AUTH_METHOD = "krbv"
66

77
# Username and password

tests/test_hubproxy.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,55 @@ def request(self, host, path, request, verbose=False):
4040
return []
4141

4242

43+
def test_login_token_oidc(requests_session):
44+
"""Login with OIDC client credentials flow."""
45+
46+
hub_url = "https://example.com/myapp/endpoint"
47+
login_url = "https://example.com/myapp/auth/tokenoidclogin/"
48+
token_url = "https://sso.example.com/protocol/openid-connect/token"
49+
50+
conf = PyConfigParser()
51+
conf.load_from_dict(
52+
{
53+
"HUB_URL": hub_url,
54+
"AUTH_METHOD": "token_oidc",
55+
"OIDC_CLIENT_ID": "test-client",
56+
"OIDC_CLIENT_SECRET": "secret-token",
57+
"OIDC_AUTH_SERVER_TOKEN_URL": token_url,
58+
"CA_CERT": "/path/to/ca-bundle.crt"
59+
}
60+
)
61+
62+
transport = FakeTransport()
63+
proxy = HubProxy(conf, transport=transport)
64+
65+
with mock.patch("requests.post") as mock_post:
66+
mock_post.return_value.json.return_value = {"access_token": "secret-token"}
67+
68+
# Force a login
69+
proxy._login(force=True)
70+
71+
mock_post.assert_called_once_with(
72+
"https://sso.example.com/protocol/openid-connect/token",
73+
data={
74+
"grant_type": "client_credentials",
75+
"client_id": "test-client",
76+
"client_secret": "secret-token",
77+
"scope": "openid"
78+
},
79+
timeout=30
80+
)
81+
82+
# Cookies should have been shared between session and transport
83+
assert requests_session.return_value.cookies is transport.cookiejar
84+
85+
requests_session.return_value.get.assert_called_once_with(
86+
"https://example.com/myapp/auth/tokenoidclogin/",
87+
headers={"Authorization": "Bearer secret-token"},
88+
verify="/path/to/ca-bundle.crt"
89+
)
90+
91+
4392
def test_login_gssapi(requests_session):
4493
"""Login with gssapi method obtains session cookie via SPNEGO & krb5login."""
4594

0 commit comments

Comments
 (0)