1from collections.abc import Sequence 2from collections.abc import Set 3from datetime import datetime 4from datetime import timedelta 5from datetime import timezone 6 7from flask import current_app 8from jwt.algorithms import requires_cryptography 9 10 11class _Config(object): 12 """ 13 Helper object for accessing and verifying options in this extension. This 14 is meant for internal use of the application; modifying config options 15 should be done with flasks ```app.config```. 16 17 Default values for the configuration options are set in the jwt_manager 18 object. All of these values are read only. This is simply a loose wrapper 19 with some helper functionality for flasks `app.config`. 20 """ 21 22 @property 23 def is_asymmetric(self): 24 return self.algorithm in requires_cryptography 25 26 @property 27 def encode_key(self): 28 return self._private_key if self.is_asymmetric else self._secret_key 29 30 @property 31 def decode_key(self): 32 return self._public_key if self.is_asymmetric else self._secret_key 33 34 @property 35 def token_location(self): 36 locations = current_app.config["JWT_TOKEN_LOCATION"] 37 if isinstance(locations, str): 38 locations = (locations,) 39 elif not isinstance(locations, (Sequence, Set)): 40 raise RuntimeError("JWT_TOKEN_LOCATION must be a sequence or a set") 41 elif not locations: 42 raise RuntimeError( 43 "JWT_TOKEN_LOCATION must contain at least one " 44 'of "headers", "cookies", "query_string", or "json"' 45 ) 46 for location in locations: 47 if location not in ("headers", "cookies", "query_string", "json"): 48 raise RuntimeError( 49 "JWT_TOKEN_LOCATION can only contain " 50 '"headers", "cookies", "query_string", or "json"' 51 ) 52 return locations 53 54 @property 55 def jwt_in_cookies(self): 56 return "cookies" in self.token_location 57 58 @property 59 def jwt_in_headers(self): 60 return "headers" in self.token_location 61 62 @property 63 def jwt_in_query_string(self): 64 return "query_string" in self.token_location 65 66 @property 67 def jwt_in_json(self): 68 return "json" in self.token_location 69 70 @property 71 def header_name(self): 72 name = current_app.config["JWT_HEADER_NAME"] 73 if not name: 74 raise RuntimeError("JWT_ACCESS_HEADER_NAME cannot be empty") 75 return name 76 77 @property 78 def header_type(self): 79 return current_app.config["JWT_HEADER_TYPE"] 80 81 @property 82 def query_string_name(self): 83 return current_app.config["JWT_QUERY_STRING_NAME"] 84 85 @property 86 def query_string_value_prefix(self): 87 return current_app.config["JWT_QUERY_STRING_VALUE_PREFIX"] 88 89 @property 90 def access_cookie_name(self): 91 return current_app.config["JWT_ACCESS_COOKIE_NAME"] 92 93 @property 94 def refresh_cookie_name(self): 95 return current_app.config["JWT_REFRESH_COOKIE_NAME"] 96 97 @property 98 def access_cookie_path(self): 99 return current_app.config["JWT_ACCESS_COOKIE_PATH"] 100 101 @property 102 def refresh_cookie_path(self): 103 return current_app.config["JWT_REFRESH_COOKIE_PATH"] 104 105 @property 106 def cookie_secure(self): 107 return current_app.config["JWT_COOKIE_SECURE"] 108 109 @property 110 def cookie_domain(self): 111 return current_app.config["JWT_COOKIE_DOMAIN"] 112 113 @property 114 def session_cookie(self): 115 return current_app.config["JWT_SESSION_COOKIE"] 116 117 @property 118 def cookie_samesite(self): 119 return current_app.config["JWT_COOKIE_SAMESITE"] 120 121 @property 122 def json_key(self): 123 return current_app.config["JWT_JSON_KEY"] 124 125 @property 126 def refresh_json_key(self): 127 return current_app.config["JWT_REFRESH_JSON_KEY"] 128 129 @property 130 def csrf_protect(self): 131 return self.jwt_in_cookies and current_app.config["JWT_COOKIE_CSRF_PROTECT"] 132 133 @property 134 def csrf_request_methods(self): 135 return current_app.config["JWT_CSRF_METHODS"] 136 137 @property 138 def csrf_in_cookies(self): 139 return current_app.config["JWT_CSRF_IN_COOKIES"] 140 141 @property 142 def access_csrf_cookie_name(self): 143 return current_app.config["JWT_ACCESS_CSRF_COOKIE_NAME"] 144 145 @property 146 def refresh_csrf_cookie_name(self): 147 return current_app.config["JWT_REFRESH_CSRF_COOKIE_NAME"] 148 149 @property 150 def access_csrf_cookie_path(self): 151 return current_app.config["JWT_ACCESS_CSRF_COOKIE_PATH"] 152 153 @property 154 def refresh_csrf_cookie_path(self): 155 return current_app.config["JWT_REFRESH_CSRF_COOKIE_PATH"] 156 157 @property 158 def access_csrf_header_name(self): 159 return current_app.config["JWT_ACCESS_CSRF_HEADER_NAME"] 160 161 @property 162 def refresh_csrf_header_name(self): 163 return current_app.config["JWT_REFRESH_CSRF_HEADER_NAME"] 164 165 @property 166 def csrf_check_form(self): 167 return current_app.config["JWT_CSRF_CHECK_FORM"] 168 169 @property 170 def access_csrf_field_name(self): 171 return current_app.config["JWT_ACCESS_CSRF_FIELD_NAME"] 172 173 @property 174 def refresh_csrf_field_name(self): 175 return current_app.config["JWT_REFRESH_CSRF_FIELD_NAME"] 176 177 @property 178 def access_expires(self): 179 delta = current_app.config["JWT_ACCESS_TOKEN_EXPIRES"] 180 if type(delta) is int: 181 delta = timedelta(seconds=delta) 182 if delta is not False: 183 try: 184 delta + datetime.now(timezone.utc) 185 except TypeError as e: 186 err = ( 187 "must be able to add JWT_ACCESS_TOKEN_EXPIRES to datetime.datetime" 188 ) 189 raise RuntimeError(err) from e 190 return delta 191 192 @property 193 def refresh_expires(self): 194 delta = current_app.config["JWT_REFRESH_TOKEN_EXPIRES"] 195 if type(delta) is int: 196 delta = timedelta(seconds=delta) 197 if delta is not False: 198 try: 199 delta + datetime.now(timezone.utc) 200 except TypeError as e: 201 err = ( 202 "must be able to add JWT_REFRESH_TOKEN_EXPIRES to datetime.datetime" 203 ) 204 raise RuntimeError(err) from e 205 return delta 206 207 @property 208 def algorithm(self): 209 return current_app.config["JWT_ALGORITHM"] 210 211 @property 212 def decode_algorithms(self): 213 algorithms = current_app.config["JWT_DECODE_ALGORITHMS"] 214 if not algorithms: 215 return [self.algorithm] 216 if self.algorithm not in algorithms: 217 algorithms.append(self.algorithm) 218 return algorithms 219 220 @property 221 def _secret_key(self): 222 key = current_app.config["JWT_SECRET_KEY"] 223 if not key: 224 key = current_app.config.get("SECRET_KEY", None) 225 if not key: 226 raise RuntimeError( 227 "JWT_SECRET_KEY or flask SECRET_KEY " 228 "must be set when using symmetric " 229 'algorithm "{}"'.format(self.algorithm) 230 ) 231 return key 232 233 @property 234 def _public_key(self): 235 key = current_app.config["JWT_PUBLIC_KEY"] 236 if not key: 237 raise RuntimeError( 238 "JWT_PUBLIC_KEY must be set to use " 239 "asymmetric cryptography algorithm " 240 '"{}"'.format(self.algorithm) 241 ) 242 return key 243 244 @property 245 def _private_key(self): 246 key = current_app.config["JWT_PRIVATE_KEY"] 247 if not key: 248 raise RuntimeError( 249 "JWT_PRIVATE_KEY must be set to use " 250 "asymmetric cryptography algorithm " 251 '"{}"'.format(self.algorithm) 252 ) 253 return key 254 255 @property 256 def cookie_max_age(self): 257 # Returns the appropiate value for max_age for flask set_cookies. If 258 # session cookie is true, return None, otherwise return a number of 259 # seconds 1 year in the future 260 return None if self.session_cookie else 31540000 # 1 year 261 262 @property 263 def identity_claim_key(self): 264 return current_app.config["JWT_IDENTITY_CLAIM"] 265 266 @property 267 def exempt_methods(self): 268 return {"OPTIONS"} 269 270 @property 271 def error_msg_key(self): 272 return current_app.config["JWT_ERROR_MESSAGE_KEY"] 273 274 @property 275 def json_encoder(self): 276 return current_app.json_encoder 277 278 @property 279 def decode_audience(self): 280 return current_app.config["JWT_DECODE_AUDIENCE"] 281 282 @property 283 def encode_audience(self): 284 return current_app.config["JWT_ENCODE_AUDIENCE"] 285 286 @property 287 def encode_issuer(self): 288 return current_app.config["JWT_ENCODE_ISSUER"] 289 290 @property 291 def decode_issuer(self): 292 return current_app.config["JWT_DECODE_ISSUER"] 293 294 @property 295 def leeway(self): 296 return current_app.config["JWT_DECODE_LEEWAY"] 297 298 @property 299 def encode_nbf(self): 300 return current_app.config["JWT_ENCODE_NBF"] 301 302 303config = _Config() 304