55import jwt
66from os import getenv
77
8-
98from exceptions import HTTPError
10- from utils import fetch_public_key
9+ from utils import fetch_public_key , fetch_service_public_keys
1110
1211
13- def decode_token (token ):
12+ def decode_user_token (token ):
1413 """Decode the authorization token read from the request header."""
1514 if token is None :
1615 return {}
@@ -38,6 +37,39 @@ def decode_token(token):
3837 return decoded_token
3938
4039
40+ def decode_service_token (token ): # pragma: no cover
41+ """Decode OSIO service token."""
42+ # TODO: Merge this function and user token function once audience is removed from user tokens.
43+ if token is None :
44+ return {}
45+
46+ if token .startswith ('Bearer ' ):
47+ _ , token = token .split (' ' , 1 )
48+
49+ pub_keys = fetch_service_public_keys (current_app )
50+ decoded_token = None
51+
52+ # Since we have multiple public keys, we need to verify against every public key.
53+ # Token can be decoded by any one of the available public keys.
54+ for pub_key in pub_keys :
55+ try :
56+ pub_key = '-----BEGIN PUBLIC KEY-----\n {pkey}\n -----END PUBLIC KEY-----' \
57+ .format (pkey = pub_key )
58+ decoded_token = jwt .decode (token , pub_key , algorithms = ['RS256' ])
59+ except jwt .InvalidTokenError :
60+ current_app .logger .error ("Auth token couldn't be decoded for public key: {}"
61+ .format (pub_key ))
62+ decoded_token = None
63+
64+ if decoded_token :
65+ break
66+
67+ if not decoded_token :
68+ raise jwt .InvalidTokenError ('Auth token cannot be verified.' )
69+
70+ return decoded_token
71+
72+
4173def get_token_from_auth_header ():
4274 """Get the authorization token read from the request header."""
4375 return request .headers .get ('Authorization' )
@@ -62,7 +94,37 @@ def wrapper(*args, **kwargs):
6294 lgr = current_app .logger
6395
6496 try :
65- decoded = decode_token (get_token_from_auth_header ())
97+ decoded = decode_user_token (get_token_from_auth_header ())
98+ if not decoded :
99+ lgr .exception ('Provide an Authorization token with the API request' )
100+ raise HTTPError (401 , 'Authentication failed - token missing' )
101+
102+ lgr .info ('Successfuly authenticated user {e} using JWT' .
103+ format (e = decoded .get ('email' )))
104+ except jwt .ExpiredSignatureError as exc :
105+ lgr .exception ('Expired JWT token' )
106+ raise HTTPError (401 , 'Authentication failed - token has expired' ) from exc
107+ except Exception as exc :
108+ lgr .exception ('Failed decoding JWT token' )
109+ raise HTTPError (401 , 'Authentication failed - could not decode JWT token' ) from exc
110+
111+ return view (* args , ** kwargs )
112+
113+ return wrapper
114+
115+
116+ def service_token_required (view ): # pragma: no cover
117+ """Check if the request contains a valid service token."""
118+ @wraps (view )
119+ def wrapper (* args , ** kwargs ):
120+ # Disable authentication for local setup
121+ if getenv ('DISABLE_AUTHENTICATION' ) in ('1' , 'True' , 'true' ):
122+ return view (* args , ** kwargs )
123+
124+ lgr = current_app .logger
125+
126+ try :
127+ decoded = decode_service_token (get_token_from_auth_header ())
66128 if not decoded :
67129 lgr .exception ('Provide an Authorization token with the API request' )
68130 raise HTTPError (401 , 'Authentication failed - token missing' )
0 commit comments