{"id":37080573,"url":"https://github.com/washed-out/asgi-gssapi","last_synced_at":"2026-01-14T09:46:53.144Z","repository":{"id":42455817,"uuid":"418233045","full_name":"washed-out/asgi-gssapi","owner":"washed-out","description":"ASGI Kerberos/GSSAPI Authentication Middleware with delegation support","archived":false,"fork":false,"pushed_at":"2022-04-05T07:49:20.000Z","size":28,"stargazers_count":3,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-27T09:27:38.392Z","etag":null,"topics":["asgi-middleware","gssapi","kerberos","spnego"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/washed-out.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-10-17T19:20:00.000Z","updated_at":"2024-11-06T21:19:48.000Z","dependencies_parsed_at":"2022-08-27T17:10:21.932Z","dependency_job_id":null,"html_url":"https://github.com/washed-out/asgi-gssapi","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/washed-out/asgi-gssapi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/washed-out%2Fasgi-gssapi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/washed-out%2Fasgi-gssapi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/washed-out%2Fasgi-gssapi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/washed-out%2Fasgi-gssapi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/washed-out","download_url":"https://codeload.github.com/washed-out/asgi-gssapi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/washed-out%2Fasgi-gssapi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28416120,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:38:59.149Z","status":"ssl_error","status_checked_at":"2026-01-14T08:38:43.588Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asgi-middleware","gssapi","kerberos","spnego"],"created_at":"2026-01-14T09:46:52.284Z","updated_at":"2026-01-14T09:46:53.136Z","avatar_url":"https://github.com/washed-out.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"ASGI-GSSAPI\n==============\n\nASGI-GSSAPI is `ASGI`_ Middleware which implements `Kerberos`_ authentication.\nIt makes it easy to add Kerberos authentication to any ASGI application.\n\nIts only dependency is `python-gssapi`_ and it's been tested up to version 1.7.2\n\nUnfortunately, as is the case with most things kerberos, it requires a kerberos\nenvironment as well as a keytab. Setting that up is outside the scope of this\ndocument.\n\nThe official copy of this documentation is available at `Read the Docs`_.\n\nInstallation\n------------\n\nInstall the extension with pip:\n\n    $ pip install ASGI-GSSAPI\n\nHow to Use\n----------\n\nTo integrate ``ASGI-GSSAPI`` into your application you'll need to generate\nyour keytab and set the environment variable ``KRB5_KTNAME`` in your shell to\nthe location of the keytab file.\n\nAfter that, it should be as easy as passing your application to the\n``SPNEGOAuthMiddleware`` constructor.  All requests destined for the\napplication will first be authenticated by the middleware, and the authenticated\nusers principal will be available as the ``principal`` key in the ASGI\nscope dictionary, under ``gssapi`` key.\n\nFor example::\n\n    import uvicorn\n    from asgi_gssapi import SPNEGOAuthMiddleware\n\n    async def example(scope, receive, send):\n        await send({\n            'type': 'http.response.start',\n            'status': 200,\n            'headers': [\n                [b'content-type', b'text/plain'],\n            ],\n        })\n        await send({\n            'type': 'http.response.body',\n            'body': b'Hello, {}'.format(scope['gssapi']['principal']),\n        })\n\n    app = SPNEGOAuthMiddleware(example)\n\n    if __name__ == '__main__':\n        uvicorn.run(app, port=8080)\n\n\n``ASGI-GSSAPI`` assumes that every request should be authenticated. If this is\nnot the case, you can override it by passing in a callback named\n``auth_required_callback`` to the\n``SPNEGOAuthMiddleware`` constructor. This callback will be called for every\nrequest and passed the ASGI scope dictionary::\n\n    import uvicorn\n    from asgi_gssapi import SPNEGOAuthMiddleware\n\n    async def example(scope, receive, send):\n        ... # same as above\n\n    def authenticate(scope):\n        return scope['path'].startswith('/protected')\n\n    app = SPNEGOAuthMiddleware(example,\n                               auth_required_callback=authenticate)\n\n    if __name__ == '__main__':\n        uvicorn.run(app, port=8080)\n\n\nBy default, when ``ASGI-GSSAPI`` responds with a ``401`` to indicate that\nauthentication is required, it generates a very simple page with a\n``Content-Type`` of ``text/plain`` that includes the string ``Unauthorized``.\n\nSimilarly, when it responds with a ``403`` indicating that authentication has\nfailed, it generates another simple page with a ``Content-Type`` of\n``text/plain`` that includes the string ``Forbidden``.\n\nThese can be customized::\n\n    import uvicorn\n    from asgi_gssapi import SPNEGOAuthMiddleware\n\n    async def example(scope, receive, send):\n        ... # same as above\n\n    app = SPNEGOAuthMiddleware(example,\n                               unauthorized='Authentication Required',\n                               forbidden='Authentication Failed')\n\n    if __name__ == '__main__':\n        uvicorn.run(app, port=8080)\n\nYou can also change the ``Content-Types`` by passing in full ASGI event\ntuples::\n\n    import uvicorn\n    from asgi_gssapi import SPNEGOAuthMiddleware\n\n    async def example(scope, receive, send):\n        ... # same as above\n\n    forbidden=({\n        'type': 'http.response.start',\n        'status': 403,\n        'headers': [\n            [b'content-type', b'text/html'],\n        ],\n    }, {\n        'type': 'http.response.body',\n        'body': b'\u003chtml\u003e\u003cbody\u003e\u003ch1\u003eGO AWAY\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e'\n    })\n\n    unauthorized=({\n        'type': 'http.response.start',\n        'status': 401,\n        'headers': [\n            [b'content-type', b'text/html'],\n            [b'www-authenticate', b'negotiate'],\n        ],\n    }, {\n        'type': 'http.response.body',\n        'body': b'\u003chtml\u003e\u003cbody\u003e\u003ch1\u003eLOGIN FIRST\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e'\n    })\n\n    app = SPNEGOAuthMiddleware(example,\n                               unauthorized=unauthorized,\n                               forbidden=forbidden)\n\n    if __name__ == '__main__':\n        uvicorn.run(app, port=8080)\n\nHopefully, you are not using raw ASGI, and your framework of choice provides\na saner alternatives to full event definitions (like Starlette's Response class).\n\n\n``ASGI-GSSAPI`` will authenticate the request using auto-resolved hostname.\nYou can change it, by providing the ``hostname`` argument to the constructor,\nor defer to any hostname, present in keytab file, by providing an empty \nstring ``hostname`` argument to the constructor::\n\n    import uvicorn\n    from asgi_gssapi import SPNEGOAuthMiddleware\n\n    async def example(scope, receive, send):\n        ... # same as above\n\n    app = SPNEGOAuthMiddleware(example, hostname='example.com')\n\n    if __name__ == '__main__':\n        uvicorn.run(app, port=8080)\n\n\n``ASGI-GSSAPI`` provides support for delegation. You do not need to\nconfigure anything server-side, and it's up to the client to delegate the credentials.\nWhen it does so, you'll receive a ``gssapi.Credentials`` object in ASGI scope dictionary,\nunder ``delegate_creds`` key::\n\n    async def example(scope, receive, send):\n        creds = scope['gssapi']['delegate_creds']\n\nSuch creds can be exported to a token cache file (using the ``.export()`` method), \ne.g. ``/tmp/krb5cc_0``, or passed directly to any other GSSAPI-powered python code, \nsuch as `requests-gssapi`_!\n\n\nHow it works\n------------\n\nWhen an application which uses the middleware is accessed by a client, it will\ncheck to see if the request includes authentication credentials in an\n``Authorization`` header. If there are no such credentials, the application will\nrespond immediately with a ``401 Unauthorized`` response which includes a\n``WWW-Authenticate`` header field with a value of ``Negotiate`` indicating to\nthe client that they are currently unauthorized, but that they can authenticate\nusing Negotiate authentication.\n\nIf credentials are presented in the ``Authorization`` header, the credentials\nwill be validated, the principal of the authenticating user will be extracted\nand added to the ASGI scope using the key ``principal`` in the ``gssapi`` dictionary,\nand the application will be called to serve the request. Send event will be hijacked\nto append ``WWW-Authenticate`` header which identifies the server to\nthe client.  This allows ``ASGI-GSSAPI`` to support mutual authentication.\n\n\nFull Example\n------------\n\nTo see a simple example, you can download the code `from github\n\u003chttp://github.com/washed-out/asgi-gssapi\u003e`_. It is in the example directory.\n\nChanges\n-------\n\n0.1.0 (2021-10-16)\n``````````````````\n\n-     initial implementation\n\n\nAPI References\n--------------\n\nThe full API reference:\n\n\n.. automodule:: asgi_gssapi\n   :members:\n\n.. _ASGI: http://asgi.readthedocs.org/en/latest/\n.. _Kerberos: http://wikipedia.org/wiki/Kerberos_(protocol)\n.. _python-gssapi: https://pypi.org/project/gssapi\n.. _Read the Docs: https://asgi-gssapi.readthedocs.org/\n.. _requests-gssapi: https://github.com/pythongssapi/requests-gssapi\n\nHistory\n=======\nAlthough this plugin shares no code with `WSGI-Kerberos\n\u003chttps://github.com/deshaw/wsgi-kerberos\u003e`_ ,\nwhole repository layout, including the README file you're reading now,\nwas shamelessly stolen from it. Thus, I'm keeping contributors and license intact.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwashed-out%2Fasgi-gssapi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwashed-out%2Fasgi-gssapi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwashed-out%2Fasgi-gssapi/lists"}