Skip to content

Commit 8fcb309

Browse files
Fixes for /oauth2/userinfo/oidc endpoints
1 parent 173ff08 commit 8fcb309

File tree

5 files changed

+68
-9
lines changed

5 files changed

+68
-9
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# image: tapis/authenticator
2-
FROM tapis/flaskbase:1.7.0
2+
FROM tapis/flaskbase:1.8.0
33

44
COPY requirements.txt /home/tapis/requirements.txt
55
RUN pip install -r /home/tapis/requirements.txt

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
# CONSIDER USING VIM (sigh!) WHICH ACTUALLY HAS GOOD MAKEFILE EDITING SUPPORT.
77

88

9+
10+
# To run tests ensure service_password and token_url in .env
11+
# make clean
12+
# make build
13+
# make init_dbs
14+
# make migrate.upgrade
15+
# make test
16+
917
# it is required that the operator export API_NAME=<name_of_the_api> before using this makefile/
1018
# default to authenticator
1119
API_NAME ?=authenticator

service/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
logger.debug("creating the authenticator tapis service client...")
2020
t = get_service_tapis_client(tenants=auth_tenants,
2121
# todo -- change back once tokens api update is in prod
22-
resource_set='dev'
22+
# resource_set='dev'
2323
)
2424

2525

service/auth.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,48 @@ def authentication():
148148
f"served by this authenticator.")
149149
return True
150150

151+
# token should come from `Authorization: Bearer $token` header. rather than x-tapis-token
152+
# this endpoint takes both, converts Authorization to x-tapis-token for simplicity
153+
if '/v3/oauth2/userinfo/oidc' in request.url_rule.rule:
154+
logger.debug(f"top of /v3/oauth2/userinfo/oidc auth: request.headers: {request.headers}")
155+
156+
auth_token = request.headers.get('Authorization')
157+
if auth_token and auth_token.startswith('Bearer ') and not request.headers.get('X-Tapis-Token'):
158+
try:
159+
# overwrite the headers via wsgi environ. request.headers itself is read-only
160+
tapis_token = auth_token.replace('Bearer ', '')
161+
logger.debug(f"found auth header; setting environ X-Tapis-Token to {tapis_token}")
162+
163+
# tokens might have aud, if jwt.decode in tapisservice doesn't specify expected aud you'll
164+
# get invalid aud. Either we can somehow pop aud or specify to jwt.decode(options={'verify_aud': False})
165+
# Instead of verify = false we can also specify a list of valid auds. Pop aud would require
166+
# re-encoding+sigining key. We don't have private tenant key in auth though. Ignoring for now, only
167+
# bookstack looks for this when running their auth.
168+
# resolve_tenant_id_for_request decode needs aud to expect - https://github.com/jpadilla/pyjwt/blob/master/docs/usage.rst#audience-claim-aud
169+
170+
# modify the WSGI environment directly
171+
# wsgi requires headers be uppercase, no dashes, and prefixed with HTTP_
172+
request.environ['HTTP_X_TAPIS_TOKEN'] = tapis_token
173+
except Exception as e:
174+
logger.error(f"found auth header, but failed to parse it; exception: {e}")
175+
176+
# debug logs
177+
try:
178+
headers = request.headers
179+
logger.debug(f"before auth.authentication(). request.headers: {headers}")
180+
except Exception as e:
181+
pass
182+
183+
auth.authentication()
184+
# always resolve the request tenant id based on the URL:
185+
auth.resolve_tenant_id_for_request()
186+
# make sure this request is for a tenant served by this authenticator
187+
if g.request_tenant_id not in conf.tenants:
188+
raise common_errors.PermissionsError(f"The request is for a tenant ({g.request_tenant_id}) that is not "
189+
f"served by this authenticator.")
190+
logger.debug(f"End of v3/oauth2/userinfo/oidc auth: final request_tenant_id: {g.request_tenant_id}")
191+
return True
192+
151193
# the profiles endpoints always use standard Tapis Token auth -
152194
if '/v3/oauth2/profiles' in request.url_rule.rule or \
153195
'/v3/oauth2/userinfo' in request.url_rule.rule:

service/controllers.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,17 +227,25 @@ def _handle_userinfo_request(request, oidc=False):
227227
## Rubin Science place needs
228228
# rubin scope with info via data_rights
229229
# adding data rights for specific users for rubin - test
230-
logger.debug(f"userinfo: {userinfo}")
231-
username = userinfo.get('username')
232-
if oidc and username and username in ["cgarcia", "mpackard", "kprice", "jstubbs"]:
233-
data_rights = get_user_data_rights(username)
234-
if data_rights:
235-
userinfo["data_rights"] = " ".join(data_rights)
230+
logger.debug(f"userinfo: {userinfo.serialize}")
231+
try:
232+
username = userinfo.get('username')
233+
except:
234+
username = "TALKTODEV"
235+
if oidc:
236+
logger.debug(f"inside of oidc userinfo; username: {username}")
237+
## This code still doesn't matter, was attempting some debugging for rubin place
238+
# Kevin got Gafaelfawr to look in the "correct field" to map groups
239+
if username and username in ["cgarcia", "mpackard", "kprice", "jstubbs"]:
240+
data_rights = get_user_data_rights(username)
241+
if data_rights:
242+
userinfo["data_rights"] = " ".join(data_rights)
243+
return jsonify(userinfo.serialize)
236244

237245
return utils.ok(result=userinfo.serialize, msg="User profile retrieved successfully.")
238246

239247

240-
def get_user_data_rights(user):
248+
def get_user_data_rights(username):
241249
# Implement logic to retrieve the list of data releases the user has access to
242250
# This function should return a list of strings representing data releases
243251
return ["release1", "release2", "lsst-sqre", "admin:jupyterlab", "admin", "jupyterlab", "square", "tacc-spherex"]
@@ -1553,6 +1561,7 @@ def _handle_tokens_request(request, oidc=False):
15531561
content['claims']['tapis/idp_id'] = idp_id
15541562
if oidc:
15551563
if client_id:
1564+
# bookstack for example requires aud to match client id
15561565
content['claims']['aud'] = client_id
15571566
content['claims']['iat'] = int(time.time())
15581567
content['claims']['extravar'] = username

0 commit comments

Comments
 (0)