py-identity-model


Namepy-identity-model JSON
Version 0.13.0 PyPI version JSON
download
home_pageNone
SummaryOAuth2.0 and OpenID Connect Client Library
upload_time2024-09-14 00:32:06
maintainerNone
docs_urlNone
authorjamescrowley321
requires_python<4.0,>=3.12
licenseApache 2.0
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # py-identity-model
![Build](https://github.com/jamescrowley321/py-identity-model/workflows/Build/badge.svg)
![License](https://img.shields.io/pypi/l/py-identity-model)

OIDC/OAuth2.0 helper library for decoding JWTs and creating JWTs utilizing the `client_credentials` grant. This project is very limited in functionality, but it has been used in production for years as the foundation of Flask/FastAPI middleware implementations.

The use case for the library in its current form is limited to the following
* Discovery endpoint is utilized
* JWKS endpoint is utilized
* Authorization servers with multiple active keys

While you can manually construct the validation configs required to manually bypass automated discovery, the library does not currently test those scenarios.

For more information on the lower level configuration options for token validation, refer to the official [PyJWT Docs](https://pyjwt.readthedocs.io/en/stable/index.html)

Does not currently support opaque tokens.

Inspired By:

* [IdentityModel](https://github.com/IdentityModel/IdentityModel)
* [cognitojwt](https://github.com/borisrozumnuk/cognitojwt)

## Examples

### Discovery

Only a subset of fields is currently mapped.

```python
import os

from py_identity_model import DiscoveryDocumentRequest, get_discovery_document

DISCO_ADDRESS = os.environ["DISCO_ADDRESS"]
    
disco_doc_request = DiscoveryDocumentRequest(address=DISCO_ADDRESS)
disco_doc_response = get_discovery_document(disco_doc_request)    
print(disco_doc_response)
```

### JWKs

```python
import os

from py_identity_model import (
    DiscoveryDocumentRequest, 
    get_discovery_document,
    JwksRequest, 
    get_jwks,
)

DISCO_ADDRESS = os.environ["DISCO_ADDRESS"]
    
disco_doc_request = DiscoveryDocumentRequest(address=DISCO_ADDRESS)
disco_doc_response = get_discovery_document(disco_doc_request)  

jwks_request = JwksRequest(address=disco_doc_response.jwks_uri)
jwks_response = get_jwks(jwks_request)
print(jwks_response)
```

### Basic Token Validation

Token validation validates the signature of a JWT against the values provided from an OIDC discovery document. The function will throw an exception if the token is expired or signature validation fails.

Token validation is simply a wrapper on top of the [jose.jwt.decode](https://python-jose.readthedocs.io/en/latest/jwt/api.html#jose.jwt.decode). The configuration object is mapped to the input parameters of `jose.jwt.decode`. 

```python
@dataclass
class TokenValidationConfig:
    perform_disco: bool
    key: Optional[dict] = None
    audience: Optional[str] = None
    algorithms: Optional[List[str]] = None
    issuer: Optional[List[str]] = None
    subject: Optional[str] = None
    options: Optional[dict] = None
```



```python
import os

from py_identity_model import PyIdentityModelException, validate_token

DISCO_ADDRESS = os.environ["DISCO_ADDRESS"]

token = get_token() # Get the token in the manner best suited to your application

validation_options = {
    "verify_signature": True,
     "verify_aud": True,
     "verify_iat": True,
     "verify_exp": True,
     "verify_nbf": True,
     "verify_iss": True,
     "verify_sub": True,
     "verify_jti": True,
     "verify_at_hash": True,
     "require_aud": False,
     "require_iat": False,
     "require_exp": False,
     "require_nbf": False,
     "require_iss": False,
     "require_sub": False,
     "require_jti": False,
     "require_at_hash": False,
     "leeway": 0,
}

validation_config = TokenValidationConfig(
     perform_disco=True,
     audience=TEST_AUDIENCE,
     options=validation_options
)

claims = validate_token(jwt=token, disco_doc_address=DISCO_ADDRESS)
print(claims)
```

### Token Generation

The only current supported flow is the `client_credentials` flow. Load configuration parameters in the method your application supports. Environment variables are used here for demonstration purposes.

Example:

```python
import os

from py_identity_model import (
    ClientCredentialsTokenRequest,
    request_client_credentials_token,
    get_discovery_document,
    DiscoveryDocumentRequest,
)

DISCO_ADDRESS = os.environ["DISCO_ADDRESS"]
CLIENT_ID = os.environ["CLIENT_ID"]
CLIENT_SECRET = os.environ["CLIENT_SECRET"]
SCOPE = os.environ["SCOPE"]

disco_doc_response = get_discovery_document(
    DiscoveryDocumentRequest(address=DISCO_ADDRESS)
)

client_creds_req = ClientCredentialsTokenRequest(
	client_id=CLIENT_ID,
    client_secret=CLIENT_SECRET,
    address=disco_doc_response.token_endpoint,
    scope=SCOPE,
)
client_creds_token = request_client_credentials_token(client_creds_req)
print(client_creds_token)
```

## Roadmap
These are in no particular order of importance. I am working on this project to bring a library as capable as IdentityModel to the Python ecosystem and will most likely focus on the needful and most used features first.
* Protocol abstractions and constants
* Discovery Endpoint
* Token Endpoint
* Token Introspection Endpoint
* Token Revocation Endpoint
* UserInfo Endpoint
* Dynamic Client Registration
* Device Authorization Endpoint
* Token Validation
* Example integrations with popular providers
* Example middleware implementations for Flask and FastApi
* async Support
* Setup documentation
* Opaque tokens

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "py-identity-model",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.12",
    "maintainer_email": null,
    "keywords": null,
    "author": "jamescrowley321",
    "author_email": "jamescrowley151@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/5f/f7/ea76ddc088ba83b6caf7a7a2a6b18a52b7ecaf0d6f8727bd7344145ec511/py_identity_model-0.13.0.tar.gz",
    "platform": null,
    "description": "# py-identity-model\n![Build](https://github.com/jamescrowley321/py-identity-model/workflows/Build/badge.svg)\n![License](https://img.shields.io/pypi/l/py-identity-model)\n\nOIDC/OAuth2.0 helper library for decoding JWTs and creating JWTs utilizing the `client_credentials` grant. This project is very limited in functionality, but it has been used in production for years as the foundation of Flask/FastAPI middleware implementations.\n\nThe use case for the library in its current form is limited to the following\n* Discovery endpoint is utilized\n* JWKS endpoint is utilized\n* Authorization servers with multiple active keys\n\nWhile you can manually construct the validation configs required to manually bypass automated discovery, the library does not currently test those scenarios.\n\nFor more information on the lower level configuration options for token validation, refer to the official [PyJWT Docs](https://pyjwt.readthedocs.io/en/stable/index.html)\n\nDoes not currently support opaque tokens.\n\nInspired By:\n\n* [IdentityModel](https://github.com/IdentityModel/IdentityModel)\n* [cognitojwt](https://github.com/borisrozumnuk/cognitojwt)\n\n## Examples\n\n### Discovery\n\nOnly a subset of fields is currently mapped.\n\n```python\nimport os\n\nfrom py_identity_model import DiscoveryDocumentRequest, get_discovery_document\n\nDISCO_ADDRESS = os.environ[\"DISCO_ADDRESS\"]\n    \ndisco_doc_request = DiscoveryDocumentRequest(address=DISCO_ADDRESS)\ndisco_doc_response = get_discovery_document(disco_doc_request)    \nprint(disco_doc_response)\n```\n\n### JWKs\n\n```python\nimport os\n\nfrom py_identity_model import (\n    DiscoveryDocumentRequest, \n    get_discovery_document,\n    JwksRequest, \n    get_jwks,\n)\n\nDISCO_ADDRESS = os.environ[\"DISCO_ADDRESS\"]\n    \ndisco_doc_request = DiscoveryDocumentRequest(address=DISCO_ADDRESS)\ndisco_doc_response = get_discovery_document(disco_doc_request)  \n\njwks_request = JwksRequest(address=disco_doc_response.jwks_uri)\njwks_response = get_jwks(jwks_request)\nprint(jwks_response)\n```\n\n### Basic Token Validation\n\nToken validation validates the signature of a JWT against the values provided from an OIDC discovery document. The function will throw an exception if the token is expired or signature validation fails.\n\nToken validation is simply a wrapper on top of the [jose.jwt.decode](https://python-jose.readthedocs.io/en/latest/jwt/api.html#jose.jwt.decode). The configuration object is mapped to the input parameters of `jose.jwt.decode`. \n\n```python\n@dataclass\nclass TokenValidationConfig:\n    perform_disco: bool\n    key: Optional[dict] = None\n    audience: Optional[str] = None\n    algorithms: Optional[List[str]] = None\n    issuer: Optional[List[str]] = None\n    subject: Optional[str] = None\n    options: Optional[dict] = None\n```\n\n\n\n```python\nimport os\n\nfrom py_identity_model import PyIdentityModelException, validate_token\n\nDISCO_ADDRESS = os.environ[\"DISCO_ADDRESS\"]\n\ntoken = get_token() # Get the token in the manner best suited to your application\n\nvalidation_options = {\n    \"verify_signature\": True,\n     \"verify_aud\": True,\n     \"verify_iat\": True,\n     \"verify_exp\": True,\n     \"verify_nbf\": True,\n     \"verify_iss\": True,\n     \"verify_sub\": True,\n     \"verify_jti\": True,\n     \"verify_at_hash\": True,\n     \"require_aud\": False,\n     \"require_iat\": False,\n     \"require_exp\": False,\n     \"require_nbf\": False,\n     \"require_iss\": False,\n     \"require_sub\": False,\n     \"require_jti\": False,\n     \"require_at_hash\": False,\n     \"leeway\": 0,\n}\n\nvalidation_config = TokenValidationConfig(\n     perform_disco=True,\n     audience=TEST_AUDIENCE,\n     options=validation_options\n)\n\nclaims = validate_token(jwt=token, disco_doc_address=DISCO_ADDRESS)\nprint(claims)\n```\n\n### Token Generation\n\nThe only current supported flow is the `client_credentials` flow. Load configuration parameters in the method your application supports. Environment variables are used here for demonstration purposes.\n\nExample:\n\n```python\nimport os\n\nfrom py_identity_model import (\n    ClientCredentialsTokenRequest,\n    request_client_credentials_token,\n    get_discovery_document,\n    DiscoveryDocumentRequest,\n)\n\nDISCO_ADDRESS = os.environ[\"DISCO_ADDRESS\"]\nCLIENT_ID = os.environ[\"CLIENT_ID\"]\nCLIENT_SECRET = os.environ[\"CLIENT_SECRET\"]\nSCOPE = os.environ[\"SCOPE\"]\n\ndisco_doc_response = get_discovery_document(\n    DiscoveryDocumentRequest(address=DISCO_ADDRESS)\n)\n\nclient_creds_req = ClientCredentialsTokenRequest(\n\tclient_id=CLIENT_ID,\n    client_secret=CLIENT_SECRET,\n    address=disco_doc_response.token_endpoint,\n    scope=SCOPE,\n)\nclient_creds_token = request_client_credentials_token(client_creds_req)\nprint(client_creds_token)\n```\n\n## Roadmap\nThese are in no particular order of importance. I am working on this project to bring a library as capable as IdentityModel to the Python ecosystem and will most likely focus on the needful and most used features first.\n* Protocol abstractions and constants\n* Discovery Endpoint\n* Token Endpoint\n* Token Introspection Endpoint\n* Token Revocation Endpoint\n* UserInfo Endpoint\n* Dynamic Client Registration\n* Device Authorization Endpoint\n* Token Validation\n* Example integrations with popular providers\n* Example middleware implementations for Flask and FastApi\n* async Support\n* Setup documentation\n* Opaque tokens\n",
    "bugtrack_url": null,
    "license": "Apache 2.0",
    "summary": "OAuth2.0 and OpenID Connect Client Library",
    "version": "0.13.0",
    "project_urls": null,
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2c43100710d0b8d53763659b90b6598ec9bc37ae1a1bd5817fd7c9750915dcf5",
                "md5": "6f9c6690556f7e11dc7c4a2430577b17",
                "sha256": "47da264fcf3b2ea2306186fa67428ca761320b8fef37ed8638213ace782b5b84"
            },
            "downloads": -1,
            "filename": "py_identity_model-0.13.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6f9c6690556f7e11dc7c4a2430577b17",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.12",
            "size": 11977,
            "upload_time": "2024-09-14T00:32:05",
            "upload_time_iso_8601": "2024-09-14T00:32:05.138381Z",
            "url": "https://files.pythonhosted.org/packages/2c/43/100710d0b8d53763659b90b6598ec9bc37ae1a1bd5817fd7c9750915dcf5/py_identity_model-0.13.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5ff7ea76ddc088ba83b6caf7a7a2a6b18a52b7ecaf0d6f8727bd7344145ec511",
                "md5": "0d5062ffdf9033c9870a0138168952c3",
                "sha256": "59d08ca34e1098f7c1156198262d76d21705d47e7b92c9965cfbd9ba689550e1"
            },
            "downloads": -1,
            "filename": "py_identity_model-0.13.0.tar.gz",
            "has_sig": false,
            "md5_digest": "0d5062ffdf9033c9870a0138168952c3",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.12",
            "size": 10608,
            "upload_time": "2024-09-14T00:32:06",
            "upload_time_iso_8601": "2024-09-14T00:32:06.499103Z",
            "url": "https://files.pythonhosted.org/packages/5f/f7/ea76ddc088ba83b6caf7a7a2a6b18a52b7ecaf0d6f8727bd7344145ec511/py_identity_model-0.13.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-14 00:32:06",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "py-identity-model"
}
        
Elapsed time: 0.33779s