Examples
The complete, runnable apps live in the repository’s
examples/ directory.
The test suite smoke-tests these examples so documentation snippets do
not have to be maintained separately.
Single-file application
Use this pattern for a small app with a module-level Flask application.
GitHub(app) initializes the extension immediately:
"""The smallest app: one file, module-level ``app``.
Run it with::
export GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... SECRET_KEY=...
flask --app examples/single_file run
"""
import os
import flask
import flask_githubkit
app = flask.Flask(__name__)
app.config.update(
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 = flask_githubkit.GitHub(app)
@app.route("/")
def index():
return '<a href="/login">Sign in with GitHub</a>'
@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 "Access was denied.", 403
return flask.redirect("/")
Run it from the repository root after setting SECRET_KEY,
GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET:
flask --app examples/single_file run
Application factory
An application factory creates the Flask app inside a function. This
pattern is useful for tests, multiple app configurations, and larger
projects. Create the extension once, then call init_app(app) inside
the factory:
"""App factory: create the extension once, attach it in ``create_app``.
Run it with::
export GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... SECRET_KEY=...
flask --app "examples/factory:create_app" run
"""
import os
import flask
import flask_githubkit
# Normally this lives in extensions.py so any module can import it.
github = flask_githubkit.GitHub()
def create_app() -> flask.Flask:
app = flask.Flask(__name__)
app.config.update(
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",
# Optional. Both can also be passed to GitHub(...).
GITHUB_SCOPE="read:user user:email",
GITHUB_LOGIN_VIEW="login",
)
github.init_app(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 "Access was denied.", 403
return flask.redirect("/")
@app.route("/")
def index():
return '<a href="/login">Sign in with GitHub</a>'
return app
Blueprint
Blueprints give routes an endpoint prefix. In the following example,
the login endpoint is auth.login. The extension discovers this from
@github.login, so no GITHUB_LOGIN_VIEW setting is needed:
"""Blueprint: login and callback live under ``/auth``.
``@github.login`` marks the view, so after an expired refresh token the
user is sent to ``auth.login`` without any ``login_view`` setting. The
callback URL registered in your OAuth App must be
``http://localhost:5000/auth/callback``.
Run it with::
export GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... SECRET_KEY=...
flask --app "examples/blueprint:create_app" run
"""
import os
import flask
import flask_githubkit
github = flask_githubkit.GitHub()
auth = flask.Blueprint("auth", __name__, url_prefix="/auth")
@auth.route("/login")
@github.login
def login():
"""Replaced by a redirect to GitHub."""
@auth.route("/callback")
@github.callback
def callback(access_token):
if access_token is None:
return "Access was denied.", 403
return flask.redirect("/")
@auth.route("/logout")
def logout():
github.logout()
return flask.redirect("/")
def create_app() -> flask.Flask:
app = flask.Flask(__name__)
app.config.update(
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/auth/callback",
)
github.init_app(app)
app.register_blueprint(auth)
return app
The callback URL registered with GitHub must include the Blueprint
prefix, for example http://localhost:5000/auth/callback.
Call the API from a route
github.client() returns a GitHubKit client for the signed-in user, or
None when no token is available. Use the client’s typed REST methods:
"""Calling the GitHub API from a route with ``github.client()``.
``client()`` returns a GitHubKit client for the signed-in user, or
``None`` when nobody is signed in. Expired access tokens are refreshed
for you. If the refresh token has also expired, the user is sent to
the login view.
Run it with::
export GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... SECRET_KEY=...
flask --app "examples/routes:create_app" run
"""
import collections.abc
import functools
import os
import typing
import flask
import flask_githubkit
github = flask_githubkit.GitHub()
def login_required(
view: collections.abc.Callable[..., typing.Any],
) -> collections.abc.Callable[..., typing.Any]:
@functools.wraps(view)
def wrapper(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
if github.client() is None:
return flask.redirect(flask.url_for("login"))
return view(*args, **kwargs)
return wrapper
def create_app() -> flask.Flask:
app = flask.Flask(__name__)
app.config.update(
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.init_app(app)
@app.route("/login")
def login():
# The call form, for when you want to run code before redirecting.
return github.login()
@app.route("/callback")
@github.callback
def callback(access_token):
if access_token is None:
return "Access was denied.", 403
return flask.redirect(flask.url_for("profile"))
@app.route("/profile")
@login_required
def profile():
user = github.client().rest.users.get_authenticated().parsed_data
return flask.jsonify(login=user.login, name=user.name)
@app.route("/logout")
def logout():
github.logout()
return flask.redirect(flask.url_for("login"))
return app
Prefer API calls over reading the access token directly. If server-side
integration code needs the token, it is available as
client.auth.token. It is a bearer credential: do not return it to the
browser, include it in a URL, or write it to logs. An API request may
refresh the token, so read the value after using the client if you need
the refreshed credential.