Quick start
This tutorial builds a small Flask app with GitHub login, a callback, a profile route that calls GitHub, and logout.
Before you start
Set its callback URL to
http://localhost:5000/callback.
For local development, provide the OAuth credentials and a Flask secret in your shell:
export SECRET_KEY="$(python -c 'import secrets; print(secrets.token_hex(32))')"
export GITHUB_CLIENT_ID="your-oauth-app-client-id"
export GITHUB_CLIENT_SECRET="your-oauth-app-client-secret"
The generated SECRET_KEY is for local development. Keep it stable while
you use the app; changing it invalidates Flask’s signed session cookies.
For deployed applications, supply secrets through your platform’s secret
manager or environment configuration.
Create app.py
import os
import flask
import flask_githubkit
app = flask.Flask(__name__)
app.config.from_mapping(
SECRET_KEY=os.environ["SECRET_KEY"],
GITHUB_CLIENT_ID=os.environ["GITHUB_CLIENT_ID"],
GITHUB_CLIENT_SECRET=os.environ["GITHUB_CLIENT_SECRET"],
GITHUB_REDIRECT_URI="http://localhost:5000/callback",
GITHUB_SCOPE="read:user",
)
github = flask_githubkit.GitHub(app)
@app.route("/")
def index():
return (
'<a href="/login">Sign in with GitHub</a> '
'<a href="/profile">View profile</a> '
'<a href="/logout">Sign out</a>'
)
@app.route("/login")
@github.login
def login():
"""The decorator redirects to GitHub; this body is not called."""
@app.route("/callback")
@github.callback
def callback(access_token):
if access_token is None:
return "GitHub authorization was denied.", 401
return flask.redirect(flask.url_for("profile"))
@app.route("/profile")
def profile():
client = github.client()
if client is None:
return flask.redirect(flask.url_for("login"))
user = client.rest.users.get_authenticated().parsed_data
return flask.jsonify(login=user.login)
@app.route("/logout")
def logout():
github.logout()
return flask.redirect(flask.url_for("index"))
Run the app
From the directory containing app.py, start Flask:
flask --app app run
Open http://localhost:5000/ and choose Sign in with GitHub.
Completing the flow requires a working OAuth App and network access to
GitHub.
Note
On recent macOS versions, AirPlay Receiver can occupy port 5000. If Flask
cannot bind to it, disable AirPlay Receiver or run on another port, and
use the same port in GITHUB_REDIRECT_URI and the OAuth App’s callback URL.
What happens during login
@github.loginredirects the browser to GitHub and stores a randomstatevalue in the Flask session.GitHub sends the browser back to
GITHUB_REDIRECT_URIwith an authorization code and thestate.@github.callbackchecks and consumes thestate. If it is valid, the extension exchanges the code for a token, saves the token in the session, and callscallback(access_token).The profile route calls
github.client(). The extension loads the session token and returns a GitHubKit client for the request.GitHubKit refreshes an expired access token when the client is used. flask-githubkit saves the updated token values to the session after the request.
The profile route does not need the access_token argument from the
callback. The extension persists the token in the session so later routes
can use github.client(). Prefer GitHubKit API methods over handling the
raw token.
Denied and invalid callbacks
If the user denies authorization, the decorated callback view is called
with None, allowing your app to show a message or redirect. A callback
with missing or invalid state is rejected with HTTP 400 before the view
runs. The one-time state value is consumed on the first callback attempt,
so a replay is rejected.
For application factories and Blueprints, see complete examples.