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,40 @@ 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 = pub_key .get ("key" , "" )
57+ pub_key = '-----BEGIN PUBLIC KEY-----\n {pkey}\n -----END PUBLIC KEY-----' \
58+ .format (pkey = pub_key )
59+ decoded_token = jwt .decode (token , pub_key , algorithms = ['RS256' ])
60+ except jwt .InvalidTokenError :
61+ current_app .logger .error ("Auth token couldn't be decoded for public key: {}"
62+ .format (pub_key ))
63+ decoded_token = None
64+
65+ if decoded_token :
66+ break
67+
68+ if not decoded_token :
69+ raise jwt .InvalidTokenError ('Auth token cannot be verified.' )
70+
71+ return decoded_token
72+
73+
4174def get_token_from_auth_header ():
4275 """Get the authorization token read from the request header."""
4376 return request .headers .get ('Authorization' )
@@ -62,7 +95,37 @@ def wrapper(*args, **kwargs):
6295 lgr = current_app .logger
6396
6497 try :
65- decoded = decode_token (get_token_from_auth_header ())
98+ decoded = decode_user_token (get_token_from_auth_header ())
99+ if not decoded :
100+ lgr .exception ('Provide an Authorization token with the API request' )
101+ raise HTTPError (401 , 'Authentication failed - token missing' )
102+
103+ lgr .info ('Successfuly authenticated user {e} using JWT' .
104+ format (e = decoded .get ('email' )))
105+ except jwt .ExpiredSignatureError as exc :
106+ lgr .exception ('Expired JWT token' )
107+ raise HTTPError (401 , 'Authentication failed - token has expired' ) from exc
108+ except Exception as exc :
109+ lgr .exception ('Failed decoding JWT token' )
110+ raise HTTPError (401 , 'Authentication failed - could not decode JWT token' ) from exc
111+
112+ return view (* args , ** kwargs )
113+
114+ return wrapper
115+
116+
117+ def service_token_required (view ): # pragma: no cover
118+ """Check if the request contains a valid service token."""
119+ @wraps (view )
120+ def wrapper (* args , ** kwargs ):
121+ # Disable authentication for local setup
122+ if getenv ('DISABLE_AUTHENTICATION' ) in ('1' , 'True' , 'true' ):
123+ return view (* args , ** kwargs )
124+
125+ lgr = current_app .logger
126+
127+ try :
128+ decoded = decode_service_token (get_token_from_auth_header ())
66129 if not decoded :
67130 lgr .exception ('Provide an Authorization token with the API request' )
68131 raise HTTPError (401 , 'Authentication failed - token missing' )
0 commit comments