flask-githubkit
A Flask extension for GitHub OAuth login, built on
GitHubKit. It handles expiring-token
refresh and stores tokens in flask.session.
Installation
Install the release directly from GitHub:
pip install "flask-githubkit @ git+https://github.com/LJFCoding/flask-githubkit.git@v0.1.2"
It is not published on PyPI yet, so pip install flask-githubkit will not
find it. It requires Python 3.10+ and installs Flask, GitHubKit and httpx.
For local development, install the test dependencies with
pip install -e ".[test]".
How it works
github.login()redirects the user to GitHub with a randomstatevalue that is kept inflask.session.- GitHub redirects back to your callback URL with a
codeand thestate. @github.callbackchecks thestate(blocking login CSRF), exchanges thecodefor tokens through GitHubKit, saves them inflask.session, and calls your view with the access token.- In any later request,
github.client()returns a GitHubKit client for that user. GitHubKit refreshes an expired access token itself, and the new token values are saved back to the session when the request finishes.
The names follow Flask-GitHub, but
the API is smaller and the HTTP calls are made by
GitHubKit, so you get its typed
rest methods instead of raw get/post helpers. Only synchronous Flask
apps are supported.
Quick start
Register a GitHub OAuth App and set its callback URL to the value of
GITHUB_REDIRECT_URI. Enable Expire user authorization tokens in the
OAuth App settings if you want GitHub to issue refresh tokens.
import flask
import flask_githubkit
app = flask.Flask(__name__)
app.config.update(
SECRET_KEY="load-this-from-a-secret-manager",
GITHUB_CLIENT_ID="your-client-id",
GITHUB_CLIENT_SECRET="load-this-from-a-secret-manager",
GITHUB_REDIRECT_URI="https://example.com/callback",
GITHUB_SCOPE="read:user",
)
github = flask_githubkit.GitHub(app)
@app.route("/login")
@github.login
def login():
"""Replaced by a redirect to GitHub."""
@app.route("/callback")
@github.callback
def callback(access_token):
if access_token is None:
return flask.jsonify(
{"message": "GitHub authorization was denied"}
), 401
return flask.jsonify({"message": "Signed in"})
@app.route("/me")
def me():
api = github.client()
if api is None:
return flask.redirect(flask.url_for("login"))
user = api.rest.users.get_authenticated().parsed_data
return flask.jsonify({"login": user.login})
callback validates the OAuth state, exchanges the authorization
code, stores the token data, and calls the decorated view with the access token.
If the user denies access, the view receives None. Protect callback handling
with the decorator as shown; it is responsible for validating state.
With an application factory, create the extension once and initialize it for each app:
github = flask_githubkit.GitHub()
def create_app():
app = flask.Flask(__name__)
app.config.from_mapping(
SECRET_KEY="load-this-from-a-secret-manager",
GITHUB_CLIENT_ID="your-client-id",
GITHUB_CLIENT_SECRET="load-this-from-a-secret-manager",
GITHUB_REDIRECT_URI="https://example.com/callback",
GITHUB_SCOPE="read:user",
)
github.init_app(app)
return app
Examples
Each file in examples/ is a complete app. They are checked by the test suite, which confirms the login route redirects to GitHub.
| File | Shows |
|---|---|
| single_file.py | A module-level app with GitHub(app) |
| factory.py | An app factory with init_app and the optional config keys |
| blueprint.py | Login routes in a blueprint; the auth.login endpoint is found from @github.login |
| routes.py | A login_required decorator and a route that calls the GitHub API |
Set GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET and SECRET_KEY in your
environment, then run one with, for example,
flask --app examples/single_file run.
Calling the API goes through GitHubKit, so see its
documentation for everything
client().rest offers.
Coming from Flask-GitHub
| Flask-GitHub | flask-githubkit |
|---|---|
GitHub(app) |
GitHub(app) or init_app(app) |
github.authorize() |
@github.login, or github.login() in a view |
@github.authorized_handler |
@github.callback |
@github.access_token_getter |
Not needed: tokens are kept in flask.session |
github.get("user") |
github.client().rest.users.get_authenticated() |
authorize(scope=..., redirect_uri=...) |
Not supported: use scope= in GitHub(...) or GITHUB_SCOPE, and GITHUB_REDIRECT_URI |
Configuration and API
| Flask config key | Required | Purpose |
|---|---|---|
GITHUB_CLIENT_ID |
Yes | GitHub OAuth App client ID |
GITHUB_CLIENT_SECRET |
Yes | GitHub OAuth App client secret |
GITHUB_REDIRECT_URI |
Yes | Callback URL registered with the OAuth App |
SECRET_KEY |
Yes | Flask session signing key |
GITHUB_SCOPE |
No | Space-separated OAuth scopes to request. Default read:user. |
GITHUB_LOGIN_VIEW |
No | Endpoint name users are sent to when their refresh token has expired. Usually not needed: see below. It is an endpoint name such as auth.login, not a URL. |
flask_githubkit.GitHub(app=None, *, scope=None, login_view=None) accepts an
optional Flask app. Use init_app(app) to initialize it later.
scope and login_view are optional. Each one is taken from the constructor
if you pass it, otherwise from GITHUB_SCOPE or GITHUB_LOGIN_VIEW in the
Flask config. If neither is set, the scope is read:user. The login view is
the view you decorated with @github.login, with its blueprint prefix
(auth.login), or an endpoint named login if there is no decorated view.
The client ID, client secret, redirect URI and SECRET_KEY always come from
the Flask config.
github = flask_githubkit.GitHub(
scope="read:user repo", login_view="auth.login"
)
@github.loginturns a view into a redirect to GitHub that starts the OAuth flow. You can also callgithub.login()inside a view and return the result, which is useful if you want to run code first. The decorated function's body never runs.@callbackwraps the callback view.client()returns the request-cached GitHubKitGitHubclient, orNonewhen there is no valid stored token.logout()removes the stored GitHub token from the session.
The extension registers an AuthExpiredError handler that clears the stored
token and redirects to the login view. GitHubKit also raises
AuthExpiredError when GitHub rejects the authorization code, so a stale or
reused code sends the user back to login. If GitHub cannot be reached while
exchanging the code (a timeout, a connection error or a 5xx response), the
callback returns a 502 JSON error, Could not reach GitHub., and does not
call your view. If the login view is not a real
endpoint, the handler raises a RuntimeError that says so.
flask_githubkit.Config.from_current_app() reads the configuration keys above.
flask_githubkit.Token serializes token expiry datetimes as ISO-formatted
strings in flask.session.
Sessions, token refresh, and limitations
The default Flask session is a signed, client-side cookie; it is not
encrypted, so users can read its contents. This package stores OAuth token
data there, so use a server-side session backend such as Flask-Session with
Redis in production. The extension only uses flask.session and marks the
session permanent when it saves tokens. Configure your backend and
PERMANENT_SESSION_LIFETIME (for example, 30 days) in your application; sliding
expiry and TTL behavior depend on that backend and are not provided by this
package.
GitHubKit refreshes expired access tokens when the client is used. The extension
writes updated token values back to the session after the request. GitHub
refresh tokens are single-use, so concurrent requests can race while refreshing;
this package does not provide a distributed lock. Errors raised while your own
code uses client() (for example a network failure during an API call) are not
caught by the extension.
This package supports synchronous Flask apps and GitHub OAuth Apps. It does not support GitHub Apps or configure GitHub Enterprise endpoints. Real GitHub code exchange and refresh, Redis operation, and Flask-Session TTL behavior require integration testing against those services and are not verified by the automated tests in this repository.
Tests
pip install -e ".[test]"
pytest
CI also requires 100% line and branch coverage:
coverage run -m pytest
coverage report
Metadata
Release files for flask-githubkit 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| flask_githubkit-0.1.2.tar.gz | 18.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flask_githubkit-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 29.5 kB
Release files / flask_githubkit-0.1.2.tar.gz
| Download URL | flask_githubkit-0.1.2.tar.gz |
|---|---|
| Size | 18.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
62e684dd5dced82c6481b37bf4211f8783bfc9b590076c1f71fe9a231f33d23d
|
|
BLAKE2b-256 checksum How to use checksums |
e511a1f9140cd761f23a7e5a39b8756f3aeced5166746e3f6d8366151f366bc7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logRelease files / flask_githubkit-0.1.2-py3-none-any.whl
| Download URL | flask_githubkit-0.1.2-py3-none-any.whl |
|---|---|
| Size | 10.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3b9786f9cab64bf7f36caa05e1849658190d858dc32e7f9d2a0389d01841402e
|
|
BLAKE2b-256 checksum How to use checksums |
d3186bc0ef3f43ba6ca220c6d32e5ef9026310be1a92fb9ca0351dba87cdf586
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logSecurity for flask-githubkit 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for flask_githubkit-0.1.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.