apojwt


Nameapojwt JSON
Version 1.7.0 PyPI version JSON
download
home_page
SummaryJWT Authentication Functions and Decorators. Built for Intent's Apogee Microservices
upload_time2022-12-09 17:14:55
maintainer
docs_urlNone
authorIsaac Kuhlmann
requires_python>=3.6
licenseCopyright 2022 In10t Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
keywords apojwt jwt apogee
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # ApoJWT
The `apojwt` Package was created with the intention of providing JWT support to Intent's Apogee Microservices. These services require a hierarchy of permissions that vary across all endpoints. As such, this package aims to provide decorators that can be attached with route declarations to ensure a valid JWT with proper permissions is being sent in the request headers. The package is intended to be used alongside a Python API framework such as Flask or FastAPI.

---


## ApoJWT Class
The ApoJWT class has the following constructor:
```python
(
    self,
    secret: str,
    exp_period: int=900,
    iss: str="",
    server_audience: list=[],
    algorithm: str="HS256",
    template=None,
    async_framework: bool=False,
    token_finder=None,
    exception_handler=None)
"""
Keyword Arguments (those with asterisks are functions):

JWT Validation
    secret: 
        Secret string used to encode and decode the access JWT
    
    exp_period: 
        Length of time in seconds access tokens is valid for. 
        Default 900 (15 minutes) 
    
    iss: 
        Issuer string used for additional security.
        Default ""
    
    server_audience:
        Audience names of expected HTTP hosts. 
        Audience names are typically base address URLs.
        Ex: https://example.com
    
    algorithm: 
        The algorithm to use when encoding/decoding.
        Default HS256
    
    admin_permission: 
        Optional full admin permission
        JWTs carrying this will always be authorized
    

Framework Configuration
    async_framework:
        If True - ApoJWT awaits the decorated function
        (FastAPI needs this True)
        Default: False

    token_finder: 
        Returns the Access JWT (framework specific)
        Typically found as the "Authorization" header
        Default: None

        Expected Function Structure: 
            (*args, **kwargs) -> str


    exception_handler: 
        HTTP error handling function (framework specific)
        Expected Function Structure: 
        (code: int, msg: str) -> None
"""
```
<br>
<br>

## Higher Order Functionality in ApoJWT
---

### **Token Finder**
The token_finder function must be passed to the higher order constructor (if a template is not given) for decorated token validation to succeed. The function must return the JWT string, which can usually be found in the HTTP request headers with the key 'Authorization'. It is standard for JWTs to be prefixed with the word 'Bearer'. It will be up to this function to remove this substring.

Expected Function Structure: `(*args, **kwargs) -> str`

NOTE: `args` and `kwargs` are the same arguments given to the HTTP request handler and could be optional

***Example***
```python
# Flask's request object
request.headers["Authorization"]
>>> 'Bearer <token>'
request.headers["Authorization"].replace("Bearer ", "")
>>> '<token>'
```
```python
"""Token Finder: used to locate and return the JWT"""
# FastAPI
token_finder = lambda **kwargs: kwargs["Authorization"].replace("Bearer ", "")
ajwt = ("secret", iss="issuer", async_framework=True, token_finder=token_finder)
## NOTE: async_framework is True for FastAPI

# Flask
token_finder = lambda: request.headers["Authorization"].replace("Bearer ", "")
ajwt = ("secret", iss="issuer", token_finder=token_finder)
## NOTE: async_framework defaults to False for Flask
```
<br />

### **Exception Handler**
The exception handler is optional, but allows for decorated validation to properly be handled with an HTTP error response provided by the HTTP framework in use.

Expected Function Structure: `(code: int, msg: str, *args, **kwargs) -> None`

***Example***
```python
"""Exception Handler"""
# FastAPI
def exception_handler(code: int, msg: str, *args, **kwargs):
    raise HTTPException(status_code=code, detail=msg)
ajwt = ("secret", iss="issuer", async_framework=True, token_finder=..., exception_handler=exception_handler)

# Flask
def exception_handler(code: int, msg: str):
    abort(code, msg)

ajwt = ("secret", iss="issuer", token_finder=..., exception_handler=exception_handler)
```

<br />

## Decorators
---
Decorators are the main use case of the ApoJWT package after initialization. They allow any endpoint to be secured with a single simple line of code. 
```python
ajwt = ApoJWT(secret, iss, token_finder=lambda: ..., ...)


@ajwt.token_required
"""Validates JWT

Can return 'token_data' and 'token_subject' as kwargs to HTTP handler
"""


@ajwt.permission_required(permission_name: str)
"""Validates JWT and ensures permission_name is among the token permissions

permission_name: a permission string

Can return 'token_data' and 'token_subject' as kwargs to HTTP handler
"""
```
Both decorators return `token_data` and `token_subject` as keyword arguments to the HTTP handler that is being decorated. With these arguments, the additional data stored in the JWT and the JWT's subject are both accessible. 
<br />

***Example***
```python
# fast api
@app.get("/some/endpoint")
@ajwt.token_required
def some_endpoint(
    authorization=Header(None), # required
    token_data: Optional[dict] = Body(None), # optional
    token_subject: Optional[str] = Body(None) # optional
):
...

# flask
@app.route("/some/endpoint", methods=["GET"])
@ajwt.permission_required("permission")
def some_endpoint(
    token_data: dict, # optional
    token_subject: str # optional
):
...
```
<br />

## Functions
---
```python
ajwt = ApoJWT(...)

ajwt.create_token(
    self,
    sub: str="",
    permissions: list[str]=[],
    data: dict=dict(),
    refresh_data: dict=dict()
):
        """Encodes access and refresh* JWT(s)
            *if configured

        sub: 
            Subject of the JWT
            (typically a reference to the user of JWT)

        permissions:
            List of permissions to assign to token

        data:
            Any additional data that is needed

        refresh_data:
            IF refresh is configured:
            Additional data stored with the refresh token

        JWT will contain the following claims:
            - exp: Expiration Time
            - nbf: Not Before Time
            - iss: Issuer
            - aud: Audience
            - iat: Issued At
        """
```
<br>
<br>

## Refresh Tokens
---
ApoJWT 1.5.0 introduced Refresh Token functionality. This feature is highly recommended to provide an extra layer of security to applications. To read up on Refresh Tokens and their benefits, check out [this Auth0 article](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/) for more information. The Refresh functionality in ApoJWT is activated with the following function:

```python
ajwt.config_refresh(refresh_secret: str, refresh_exp_period: int=86400, refresh_finder=None):
        """Configures ApoJWT for use with refresh tokens

        refresh_secret:
            Secret used to encode and decode the refresh JWT

        refresh_exp_period: 
            Number of seconds refresh JWT is valid
            Default 86400 (1 day)

        refresh_finder: 
            Function to retrieve the refresh JWT
            Default None
        """
```
The function `refresh_finder` is a similar function to `token_finder` in that it must return the refresh token. The main difference is that `refresh_finder`, in most cases, should find the refresh token in an http-only secure cookie instead of the HTTP Authorization header. 

Expected `refresh_finder` Function Structure: `(*args, **kwargs) -> str`

Once this function is called and initialized, ApoJWT is equipped to handle Refresh Tokens.
<br>

***Refresh Functionality***

The `create_token` function will now return a tuple containing the access token and the refresh token 
```python
access, refresh = ajwt.create_token(...)
```

Typically, this refresh token can then be stored in an HTTP-only cookie.

From there, the `@ajwt.refresh` decorator can be placed on any endpoint where a refresh should occur. This will return the refresh_data stored in the token. This can be another reference to the user which could be used to reauthorize.

***Example***
```python
# Here, the refresh data stores a user_id

# Fast Api
@app.get("/some/endpoint")
@ajwt.refresh
def refresh(refresh_data: dict):
    user_id = refresh_data["user_id"]
    user_permissions = get_user_permissions(user_id)
    
    ref_data = dict(user_id=user_id)
    access_token, refresh_token = ajwt.create_token(
        sub=user_id,
        permissions=user_permissions,
        refresh_data=ref_data
    )
```
<br>
<br>

## Usage Examples
---
### Constructing ApoJWT
```python
# FastAPI
ajwt = (
    "secret", 
    iss="issuer", 
    template="fastapi" # configures ApoJWT for fastapi
)
```
```python
# Flask
ajwt = (
    "secret", 
    iss="issuer", 
    template="fastapi" # configures ApoJWT for flask
)

```

### Validating JWT with Decorators
```python
# fast api
@app.get("/some/endpoint")
@ajwt.permission_required("permission")
def some_endpoint(authorization=Header(None)):
...

# flask
@app.route("/some/endpoint", methods=["GET"])
@ajwt.token_required
def some_endpoint()
...
```

### Refresh Configuration
```python
# flask
ajwt.config_refresh(
    "refresh_secret",
    refresh_finder=lambda: request.cookies.get('refresh_token')
)

# fast api
def refresh_finder(
    refresh_token: Union[str, None] = Cookie(default=None)
):
    return refresh_token

ajwt.config_refresh(
    "refresh_secret",
    refresh_finder=refresh_finder
)


```

### Creating a New JWT
```python
"""Permissions will be assigned to the new token"""

sub = "user_id_1"
permissions = ["permission", ...]
data = dict(...=...)


# If refresh IS NOT configured
# NOTE: all arguments are optional
token = ajwt.create_token(
    sub=sub,
    permissions=permissions,
    data=data
)

# If refresh IS configured
refresh_data = dict(...=...)
access, refresh = ajwt.create_token(
    sub=sub, 
    permissions=permissions, 
    data=data, 
    refresh_data=refresh_data
)
```

### Getting Token Data and Subject from JWT
```python
# flask
@app.route("/...")
@ajwt.token_required
def route(token_data: dict, token_subject: str):
    print(token_subject)
    return token_data


# fastapi
@app.get("/...")
@ajwt.permission_required("...")
def route(
    authorization=Header(None),
    # token_data and token_subject are unexpected to fastapi
    # Optional forces fastapi to ignore unexpected parameters
    token_data: Optional[dict]=Body(None),
    token_subject: Optional[str]=Body(None)
)
```

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "apojwt",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": "",
    "keywords": "apojwt,jwt,apogee",
    "author": "Isaac Kuhlmann",
    "author_email": "",
    "download_url": "https://files.pythonhosted.org/packages/d5/a0/05e7b0fd0b88f2df9336ac324ff0d3d67c3946d470f6ed0dfacf2eff8416/apojwt-1.7.0.tar.gz",
    "platform": null,
    "description": "# ApoJWT\nThe `apojwt` Package was created with the intention of providing JWT support to Intent's Apogee Microservices. These services require a hierarchy of permissions that vary across all endpoints. As such, this package aims to provide decorators that can be attached with route declarations to ensure a valid JWT with proper permissions is being sent in the request headers. The package is intended to be used alongside a Python API framework such as Flask or FastAPI.\n\n---\n\n\n## ApoJWT Class\nThe ApoJWT class has the following constructor:\n```python\n(\n    self,\n    secret: str,\n    exp_period: int=900,\n    iss: str=\"\",\n    server_audience: list=[],\n    algorithm: str=\"HS256\",\n    template=None,\n    async_framework: bool=False,\n    token_finder=None,\n    exception_handler=None)\n\"\"\"\nKeyword Arguments (those with asterisks are functions):\n\nJWT Validation\n    secret: \n        Secret string used to encode and decode the access JWT\n    \n    exp_period: \n        Length of time in seconds access tokens is valid for. \n        Default 900 (15 minutes) \n    \n    iss: \n        Issuer string used for additional security.\n        Default \"\"\n    \n    server_audience:\n        Audience names of expected HTTP hosts. \n        Audience names are typically base address URLs.\n        Ex: https://example.com\n    \n    algorithm: \n        The algorithm to use when encoding/decoding.\n        Default HS256\n    \n    admin_permission: \n        Optional full admin permission\n        JWTs carrying this will always be authorized\n    \n\nFramework Configuration\n    async_framework:\n        If True - ApoJWT awaits the decorated function\n        (FastAPI needs this True)\n        Default: False\n\n    token_finder: \n        Returns the Access JWT (framework specific)\n        Typically found as the \"Authorization\" header\n        Default: None\n\n        Expected Function Structure: \n            (*args, **kwargs) -> str\n\n\n    exception_handler: \n        HTTP error handling function (framework specific)\n        Expected Function Structure: \n        (code: int, msg: str) -> None\n\"\"\"\n```\n<br>\n<br>\n\n## Higher Order Functionality in ApoJWT\n---\n\n### **Token Finder**\nThe token_finder function must be passed to the higher order constructor (if a template is not given) for decorated token validation to succeed. The function must return the JWT string, which can usually be found in the HTTP request headers with the key 'Authorization'. It is standard for JWTs to be prefixed with the word 'Bearer'. It will be up to this function to remove this substring.\n\nExpected Function Structure: `(*args, **kwargs) -> str`\n\nNOTE: `args` and `kwargs` are the same arguments given to the HTTP request handler and could be optional\n\n***Example***\n```python\n# Flask's request object\nrequest.headers[\"Authorization\"]\n>>> 'Bearer <token>'\nrequest.headers[\"Authorization\"].replace(\"Bearer \", \"\")\n>>> '<token>'\n```\n```python\n\"\"\"Token Finder: used to locate and return the JWT\"\"\"\n# FastAPI\ntoken_finder = lambda **kwargs: kwargs[\"Authorization\"].replace(\"Bearer \", \"\")\najwt = (\"secret\", iss=\"issuer\", async_framework=True, token_finder=token_finder)\n## NOTE: async_framework is True for FastAPI\n\n# Flask\ntoken_finder = lambda: request.headers[\"Authorization\"].replace(\"Bearer \", \"\")\najwt = (\"secret\", iss=\"issuer\", token_finder=token_finder)\n## NOTE: async_framework defaults to False for Flask\n```\n<br />\n\n### **Exception Handler**\nThe exception handler is optional, but allows for decorated validation to properly be handled with an HTTP error response provided by the HTTP framework in use.\n\nExpected Function Structure: `(code: int, msg: str, *args, **kwargs) -> None`\n\n***Example***\n```python\n\"\"\"Exception Handler\"\"\"\n# FastAPI\ndef exception_handler(code: int, msg: str, *args, **kwargs):\n    raise HTTPException(status_code=code, detail=msg)\najwt = (\"secret\", iss=\"issuer\", async_framework=True, token_finder=..., exception_handler=exception_handler)\n\n# Flask\ndef exception_handler(code: int, msg: str):\n    abort(code, msg)\n\najwt = (\"secret\", iss=\"issuer\", token_finder=..., exception_handler=exception_handler)\n```\n\n<br />\n\n## Decorators\n---\nDecorators are the main use case of the ApoJWT package after initialization. They allow any endpoint to be secured with a single simple line of code. \n```python\najwt = ApoJWT(secret, iss, token_finder=lambda: ..., ...)\n\n\n@ajwt.token_required\n\"\"\"Validates JWT\n\nCan return 'token_data' and 'token_subject' as kwargs to HTTP handler\n\"\"\"\n\n\n@ajwt.permission_required(permission_name: str)\n\"\"\"Validates JWT and ensures permission_name is among the token permissions\n\npermission_name: a permission string\n\nCan return 'token_data' and 'token_subject' as kwargs to HTTP handler\n\"\"\"\n```\nBoth decorators return `token_data` and `token_subject` as keyword arguments to the HTTP handler that is being decorated. With these arguments, the additional data stored in the JWT and the JWT's subject are both accessible. \n<br />\n\n***Example***\n```python\n# fast api\n@app.get(\"/some/endpoint\")\n@ajwt.token_required\ndef some_endpoint(\n    authorization=Header(None), # required\n    token_data: Optional[dict] = Body(None), # optional\n    token_subject: Optional[str] = Body(None) # optional\n):\n...\n\n# flask\n@app.route(\"/some/endpoint\", methods=[\"GET\"])\n@ajwt.permission_required(\"permission\")\ndef some_endpoint(\n    token_data: dict, # optional\n    token_subject: str # optional\n):\n...\n```\n<br />\n\n## Functions\n---\n```python\najwt = ApoJWT(...)\n\najwt.create_token(\n    self,\n    sub: str=\"\",\n    permissions: list[str]=[],\n    data: dict=dict(),\n    refresh_data: dict=dict()\n):\n        \"\"\"Encodes access and refresh* JWT(s)\n            *if configured\n\n        sub: \n            Subject of the JWT\n            (typically a reference to the user of JWT)\n\n        permissions:\n            List of permissions to assign to token\n\n        data:\n            Any additional data that is needed\n\n        refresh_data:\n            IF refresh is configured:\n            Additional data stored with the refresh token\n\n        JWT will contain the following claims:\n            - exp: Expiration Time\n            - nbf: Not Before Time\n            - iss: Issuer\n            - aud: Audience\n            - iat: Issued At\n        \"\"\"\n```\n<br>\n<br>\n\n## Refresh Tokens\n---\nApoJWT 1.5.0 introduced Refresh Token functionality. This feature is highly recommended to provide an extra layer of security to applications. To read up on Refresh Tokens and their benefits, check out [this Auth0 article](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/) for more information. The Refresh functionality in ApoJWT is activated with the following function:\n\n```python\najwt.config_refresh(refresh_secret: str, refresh_exp_period: int=86400, refresh_finder=None):\n        \"\"\"Configures ApoJWT for use with refresh tokens\n\n        refresh_secret:\n            Secret used to encode and decode the refresh JWT\n\n        refresh_exp_period: \n            Number of seconds refresh JWT is valid\n            Default 86400 (1 day)\n\n        refresh_finder: \n            Function to retrieve the refresh JWT\n            Default None\n        \"\"\"\n```\nThe function `refresh_finder` is a similar function to `token_finder` in that it must return the refresh token. The main difference is that `refresh_finder`, in most cases, should find the refresh token in an http-only secure cookie instead of the HTTP Authorization header. \n\nExpected `refresh_finder` Function Structure: `(*args, **kwargs) -> str`\n\nOnce this function is called and initialized, ApoJWT is equipped to handle Refresh Tokens.\n<br>\n\n***Refresh Functionality***\n\nThe `create_token` function will now return a tuple containing the access token and the refresh token \n```python\naccess, refresh = ajwt.create_token(...)\n```\n\nTypically, this refresh token can then be stored in an HTTP-only cookie.\n\nFrom there, the `@ajwt.refresh` decorator can be placed on any endpoint where a refresh should occur. This will return the refresh_data stored in the token. This can be another reference to the user which could be used to reauthorize.\n\n***Example***\n```python\n# Here, the refresh data stores a user_id\n\n# Fast Api\n@app.get(\"/some/endpoint\")\n@ajwt.refresh\ndef refresh(refresh_data: dict):\n    user_id = refresh_data[\"user_id\"]\n    user_permissions = get_user_permissions(user_id)\n    \n    ref_data = dict(user_id=user_id)\n    access_token, refresh_token = ajwt.create_token(\n        sub=user_id,\n        permissions=user_permissions,\n        refresh_data=ref_data\n    )\n```\n<br>\n<br>\n\n## Usage Examples\n---\n### Constructing ApoJWT\n```python\n# FastAPI\najwt = (\n    \"secret\", \n    iss=\"issuer\", \n    template=\"fastapi\" # configures ApoJWT for fastapi\n)\n```\n```python\n# Flask\najwt = (\n    \"secret\", \n    iss=\"issuer\", \n    template=\"fastapi\" # configures ApoJWT for flask\n)\n\n```\n\n### Validating JWT with Decorators\n```python\n# fast api\n@app.get(\"/some/endpoint\")\n@ajwt.permission_required(\"permission\")\ndef some_endpoint(authorization=Header(None)):\n...\n\n# flask\n@app.route(\"/some/endpoint\", methods=[\"GET\"])\n@ajwt.token_required\ndef some_endpoint()\n...\n```\n\n### Refresh Configuration\n```python\n# flask\najwt.config_refresh(\n    \"refresh_secret\",\n    refresh_finder=lambda: request.cookies.get('refresh_token')\n)\n\n# fast api\ndef refresh_finder(\n    refresh_token: Union[str, None] = Cookie(default=None)\n):\n    return refresh_token\n\najwt.config_refresh(\n    \"refresh_secret\",\n    refresh_finder=refresh_finder\n)\n\n\n```\n\n### Creating a New JWT\n```python\n\"\"\"Permissions will be assigned to the new token\"\"\"\n\nsub = \"user_id_1\"\npermissions = [\"permission\", ...]\ndata = dict(...=...)\n\n\n# If refresh IS NOT configured\n# NOTE: all arguments are optional\ntoken = ajwt.create_token(\n    sub=sub,\n    permissions=permissions,\n    data=data\n)\n\n# If refresh IS configured\nrefresh_data = dict(...=...)\naccess, refresh = ajwt.create_token(\n    sub=sub, \n    permissions=permissions, \n    data=data, \n    refresh_data=refresh_data\n)\n```\n\n### Getting Token Data and Subject from JWT\n```python\n# flask\n@app.route(\"/...\")\n@ajwt.token_required\ndef route(token_data: dict, token_subject: str):\n    print(token_subject)\n    return token_data\n\n\n# fastapi\n@app.get(\"/...\")\n@ajwt.permission_required(\"...\")\ndef route(\n    authorization=Header(None),\n    # token_data and token_subject are unexpected to fastapi\n    # Optional forces fastapi to ignore unexpected parameters\n    token_data: Optional[dict]=Body(None),\n    token_subject: Optional[str]=Body(None)\n)\n```\n",
    "bugtrack_url": null,
    "license": "Copyright 2022 In10t  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
    "summary": "JWT Authentication Functions and Decorators. Built for Intent's Apogee Microservices",
    "version": "1.7.0",
    "split_keywords": [
        "apojwt",
        "jwt",
        "apogee"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "8996777bcff5999447d6a928bb7b8f9d",
                "sha256": "0d5454ae5742fc39087541fba1a6e21eac99e80cca44e04cfe36ba8a8c4f39f2"
            },
            "downloads": -1,
            "filename": "apojwt-1.7.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "8996777bcff5999447d6a928bb7b8f9d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6",
            "size": 9365,
            "upload_time": "2022-12-09T17:14:54",
            "upload_time_iso_8601": "2022-12-09T17:14:54.103665Z",
            "url": "https://files.pythonhosted.org/packages/f6/ad/4652116a75edb2e429da337a979ce5ca41f50470719112c8049513ba6dce/apojwt-1.7.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "ba3edbe1abfa5e7057625ec14f309855",
                "sha256": "17fa3d231b229ed53f18e7820c6ea3c2068b50469f3bc61207ee3eaf9d8e8409"
            },
            "downloads": -1,
            "filename": "apojwt-1.7.0.tar.gz",
            "has_sig": false,
            "md5_digest": "ba3edbe1abfa5e7057625ec14f309855",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 11614,
            "upload_time": "2022-12-09T17:14:55",
            "upload_time_iso_8601": "2022-12-09T17:14:55.901764Z",
            "url": "https://files.pythonhosted.org/packages/d5/a0/05e7b0fd0b88f2df9336ac324ff0d3d67c3946d470f6ed0dfacf2eff8416/apojwt-1.7.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2022-12-09 17:14:55",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "apojwt"
}
        
Elapsed time: 0.02148s