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