Skip to content

Commit cf4aabe

Browse files
committed
Add OpenID Connect Token authenticator
Signed-off-by: Patrick Uiterwijk <[email protected]>
1 parent d717c05 commit cf4aabe

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

custodia/httpd/authenticators.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from custodia import log
88
from custodia.plugin import HTTPAuthenticator, PluginOption
99

10+
import requests
11+
1012

1113
class SimpleCredsAuth(HTTPAuthenticator):
1214
uid = PluginOption('pwd_uid', -1, "User id or name, -1 ignores user")
@@ -131,3 +133,62 @@ def handle(self, request):
131133
self.audit_svc_access(log.AUDIT_SVC_AUTH_FAIL,
132134
request['client_id'], dn)
133135
return False
136+
137+
138+
class OpenIDCTokenAuth(HTTPAuthenticator):
139+
token_info_url = PluginOption(str, 'token_info_url',
140+
'URL for getting token information')
141+
client_id = PluginOption(str, 'client_id',
142+
'Client ID for verifying tokens')
143+
client_secret = PluginOption(str, 'client_secret',
144+
'Client Secret for verifying tokens')
145+
scope = PluginOption(str, 'scope', 'OAuth2 scope to require')
146+
147+
def _get_token_info(self, token):
148+
return requests.post(self.token_info_url,
149+
data={'client_id': self.client_id,
150+
'client_secret': self.client_secret,
151+
'token': token,
152+
'token_type_hint': 'Bearer'}).json()
153+
154+
def handle(self, request):
155+
token = None
156+
if 'Authorization' in request['headers']:
157+
hdr = request['headers']['Authorization']
158+
if hdr.startswith('Bearer '):
159+
self.logger.debug('Bearer token provided in header')
160+
token = hdr[len('Bearer '):]
161+
else:
162+
self.logger.debug('Unrecognized Authorization header')
163+
return False
164+
elif request.get('access_token'):
165+
self.logger.debug('Token provided in form')
166+
token = request.get('access_token')
167+
else:
168+
self.logger.debug('Missing any credentials in request')
169+
return False
170+
171+
if not token:
172+
self.logger.debug('No token')
173+
return False
174+
175+
try:
176+
tokeninfo = self._get_token_info(token)
177+
except:
178+
self.logger.debug('Error getting token information',
179+
exc_info=True)
180+
return False
181+
182+
aud = tokeninfo['aud']
183+
if isinstance(aud, list) and self.client_id not in aud:
184+
self.logger.debug('My client ID not in audience list')
185+
return False
186+
elif self.client_id != aud:
187+
self.logger.debug('Client ID is not audience')
188+
return False
189+
190+
if self.scope not in tokeninfo['scope'].split(' '):
191+
self.logger.debug('Required scope not found')
192+
return False
193+
194+
return True

docs/source/plugins/authenticators.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ Authenticators
2929
:undoc-members:
3030
:show-inheritance:
3131

32+
.. autoclass:: custodia.httpd.authenticators.OpenIDCTokenAuth
33+
:members:
34+
:undoc-members:
35+
:show-inheritance:
36+

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def run(self):
7171
'SimpleAuthKeys = custodia.httpd.authenticators:SimpleAuthKeys',
7272
('SimpleClientCertAuth = '
7373
'custodia.httpd.authenticators:SimpleClientCertAuth'),
74+
'OIDCTokenAuth = custodia.httpd.authenticators.OpenIDCTokenAuth',
7475
]
7576

7677
custodia_authorizers = [

0 commit comments

Comments
 (0)