Skip to main content

authorization SDK used to build protected Web APIs

Project description

web-auth-sdk

CI Status PyPI Version License Python Versions

Authorization SDK for building protected Web APIs. It allows your endpoints to perform custom authorization and authentication before the request reaches the view function. Then it passes the consumer object to the view function for further processing.

To access protected APIs, clients should authenticate by passing authorizations. For example, a JWT key can be used as follows:

curl 'http://api.example.com/resources' -H 'Authorization: Bearer eyJ1c2VyX2lkIjoxLCJwZXJtaXNzaW9uX2JpdG'
curl 'http://api.example.com/resources?access_token=eyJ1c2VyX2lkIjoxLCJwZXJtaXNzaW9uX2JpdG'

TIP: When utilizing FastAPI, click the lock symbol on Swagger UI to include your JWT. Run make startup for a quick preview. img.png

Requirements

  • Python 3.8+
  • FastAPI 0.109.0+ (recommended)
  • Django 4.0+ (optional)
  • Flask 2.0+ (optional)

Installation

  • pip install web-auth-sdk
    

    or

  • git clone https://github.com/yangaound/web-auth-sdk
    cd web-auth-sdk && poetry install
    

Permission Representation

  1. Permission list, located in the usr/etc/permissions.json file:

    permissions = [
        {'bitmask_idx': 0, 'codename': 'add_order', 'name': 'Can add order', 'service': 'order'},
        {'bitmask_idx': 1, 'codename': 'change_order', 'name': 'Can change order', 'service': 'order'},
        {'bitmask_idx': 2, 'codename': 'delete_order', 'name': 'Can delete order', 'service': 'order'},
        {'bitmask_idx': 3, 'codename': 'view_order', 'name': 'Can view order', 'service': 'order'},
        {'bitmask_idx': 4, 'codename': 'add_tickettype', 'name': 'Can add ticket type', 'service': 'order'},
        {'bitmask_idx': 5, 'codename': 'change_tickettype', 'name': 'Can change ticket type', 'service': 'order'},
        {'bitmask_idx': 6, 'codename': 'view_tickettype', 'name': 'Can view ticket type', 'service': 'order'},
        {'bitmask_idx': 7, 'codename': 'delete_tickettype', 'name': 'Can delete ticket type', 'service': 'order'},
    ]
    
  2. How to grant permissions?

    Permissions are encoded using a bitmask of length n that is a multiple of 24. Each permission is represented by a 1 at the corresponding bitmask_idx-th position in the bitmask, indicating that the permission is granted.

  3. Base64-encoded bitmask

    Bitmask Base64-encoded
    111111111111111111111111111111110111111101111111 /////39/
  4. Decoded/Encoded JWT

    Decoded JWT:

    {
      "user_id": 1,
      "permission_bitmask": "/////39/",
      "iat": 1678798980,
      "exp": 1678800187
    }
    

    Encoded JWT:

    eyJ1c2VyX2lkIjoxLCJwZXJtaXNzaW9uX2JpdG1hc2siOiIvLy8vLzM5LyIsImlhdCI6MTY3ODc5ODk4MCwiZXhwIjoxNjc4ODAwMTg3fQ
    

Development

  • FastAPI

    import web_auth
    
      
    @fastapi.get('/tickets')
    @web_auth.permissions('view_ticket') # Iterable[str] are acceptable
    async def list_tickets() -> list: 
        return []
    
  • Django

    import web_auth
    from web_auth.django import DjangoBridge
    
    
    web_auth.configure(bridge_class=DjangoBridge)
    
    @web_auth.permissions('view_ticket')
    def list_tickets(request): 
        pass
    
    urlpatterns = [django.urls.path('list-tickets', list_tickets)]
    
  • Flask

    import web_auth
    from web_auth.flask import FlaskBridge
    
    
    web_auth.configure(bridge_class=FlaskBridge)
    
    @flask.route('/tickets', methods=['GET'])
    @web_auth.permissions('view_ticket')
    def list_tickets() -> list: 
        return []
    
  • Use instanced context

    import web_auth
    
    
    context = web_auth.make_context(bridge_class='web_auth.fastapi.FastapiBridge')
    
    @fastapi.get('/tickets')
    @context.permissions('view_ticket')
    async def list_tickets() -> list: 
        return []
    
  • Retrieve the consumer

    import fastapi
    import web_auth
    
    
    @fastapi.get('/profile')
    @web_auth.permissions(['view_directory'])
    def get_profile(consumer: web_auth.Consumer) -> dict:
        return {
            'user': consumer.user.dict(),
            'directories': 'get_directories(consumer.user.user_id)',
        }
    
  • Implement Fine-Grained Permission Control

    import fastapi
    import web_auth
    
    
    @fastapi.post('/some-action')
    def some_action(request: fastapi.Request):
        # Create a context with fastapi bridge class
        context = web_auth.make_context(bridge_class=web_auth.Config.DEFAULT_BRIDGE_CLASS)
    
        # Authorize access with specific permissions (e.g., 'view_directory')
        # If this request lacks permission, it will raise `web_auth.AuthException`
        _: web_auth.Consumer = context.bridge.access_control(
            request=request, 
            permissions={'view_directory'},
            aggregation_type=web_auth.PermissionAggregationTypeEnum.ALL,
        )
    
        # Do some action
    
  • Customization

    1. Permission Storage
    from typing import Optional
    
    import fastapi
    import requests
    
    from web_auth import make_context, Storage, PermissionModel, Context
    
    
    class RESTStorage(Storage):
        def __init__(self, ttl: int, url: str, context: Optional[Context] = None):
            self.url = url
            super().__init__(ttl=ttl, context=context)
    
        def _load_permissions(self) -> list[PermissionModel]:
            return [PermissionModel(**r) for r in requests.get(self.url).json()]
    
    my_context = make_context(
        storage_class=RESTStorage,
        storage_params={'ttl': 60, 'url': 'http://api.example.com/permissions?format=json'},
    )
    
    @fastapi.get('/tickets')
    @my_context(['view_ticket', 'change_ticket'])
    def get_tickets() -> list[object]:
        pass
    
    1. Authentication and Authenticated Consumer/User
    import pydantic  
    import fastapi
    
    from web_auth import make_context, Consumer
    from web_auth.fastapi import FastapiBridge
    
    
    class AuthenticatedUser(pydantic.BaseModel):
        account: str
    
    
    class MyFastapiBridge(FastapiBridge):
        # Inject your consumer here if it's not inherited from the `web_auth.Consumer`
        consumer_class = Consumer
    
        def authenticate(self, request: fastapi.Request) -> Consumer:
            # Your authenticate logic here
            return Consumer(
                permission_bitmask='11101101111',
                user=AuthenticatedUser(account='52354342/Jack'),
            )
    
    
    my_context = make_context(bridge_class=MyFastapiBridge)
    
    @fastapi.get('/me')
    @my_context([])
    def get_profile(consumer: Consumer) -> AuthenticatedUser:
        return consumer.user
    
    1. Authorization
    import fastapi
    
    from web_auth import make_context, BitmaskAuthorization, Consumer, PermissionAggregationTypeEnum
    from web_auth.fastapi import FastapiBridge 
    
    
    class MyAuthorization(BitmaskAuthorization):
        def authorize(
            self,
            consumer: Consumer,
            permissions: set[str],
            aggregation_type: PermissionAggregationTypeEnum,
        ):
            permission_models = self.context.storage.get_permissions()
            # Checks whether the `consumer` has the `permissions` in `permission_models`
    
    class MyFastapiBridge(FastapiBridge):
        # Inject your Authorization implementation here, it's default to BitmaskAuthorization.
        authorization_class = MyAuthorization
    
    # Configurate your customization
    my_context = make_context(bridge_class=MyFastapiBridge)
    
    @fastapi.get('/tickets')
    @my_context(['view_ticket', 'change_ticket'])
    def get_tickets() -> list[object]:
        pass
    

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

web_auth_sdk-1.2.0rc0.tar.gz (14.0 kB view details)

Uploaded Source

Built Distribution

web_auth_sdk-1.2.0rc0-py3-none-any.whl (17.6 kB view details)

Uploaded Python 3

File details

Details for the file web_auth_sdk-1.2.0rc0.tar.gz.

File metadata

  • Download URL: web_auth_sdk-1.2.0rc0.tar.gz
  • Upload date:
  • Size: 14.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.9.20 Linux/6.8.0-1014-azure

File hashes

Hashes for web_auth_sdk-1.2.0rc0.tar.gz
Algorithm Hash digest
SHA256 19ba8fe92998aa8b41d30795a9bad3fa13206140051a43ff26d0bcfda86948c9
MD5 6bcf31c5d95693078500f87342ec971b
BLAKE2b-256 9547635b5154f0e5ab7c93919ebf8cbcdcd087215c77d1e730f83579c089b927

See more details on using hashes here.

File details

Details for the file web_auth_sdk-1.2.0rc0-py3-none-any.whl.

File metadata

  • Download URL: web_auth_sdk-1.2.0rc0-py3-none-any.whl
  • Upload date:
  • Size: 17.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.9.20 Linux/6.8.0-1014-azure

File hashes

Hashes for web_auth_sdk-1.2.0rc0-py3-none-any.whl
Algorithm Hash digest
SHA256 cbbd3d95cb5c1fd96ac94ce3bc72a32a74958d0acd7219625225e43c00c8a008
MD5 f5724e4b54edd747a470657c86a390d0
BLAKE2b-256 9b18315d1fb04abd7d008eb4ea97dc5194613947168458eb1ef3b1127aed6c0a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page