# 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
1. [Install the package](installation.md).
2. [Create a GitHub OAuth App](installation.md#register-a-github-oauth-app).
3. Set its callback URL to `http://localhost:5000/callback`.
For local development, provide the OAuth credentials and a Flask secret in
your shell:
```console
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`
```python
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 (
'Sign in with GitHub '
'View profile '
'Sign out'
)
@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:
```console
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
1. `@github.login` redirects the browser to GitHub and stores a random
`state` value in the Flask session.
2. GitHub sends the browser back to `GITHUB_REDIRECT_URI` with an
authorization code and the `state`.
3. `@github.callback` checks and consumes the `state`. If it is valid, the
extension exchanges the code for a token, saves the token in the session,
and calls `callback(access_token)`.
4. The profile route calls `github.client()`. The extension loads the
session token and returns a GitHubKit client for the request.
5. 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](examples.md).