---
title: GitHub OAuth in your Python Flask app
description: A step-by-step guide on building Login with GitHub into your Python apps.
author: andrew_smith
date: '2023-11-21'
tags:
  - python
  - auth
categories:
  - developers
---
In this guide we'll learn how to quickly build an OAuth2.0 integration into a simple Flask app using Supabase-py. This will enable your users to login to your web app using their GitHub account.

## Prerequisites

This article assumes you are familiar with creating an application in Flask. It also assumes that you have read the Supabase documentation and are familiar the with concept of [Authentication](https://supabase.com/docs/guides/auth).

We'll use the following tools:

- [Flask](https://flask.palletsprojects.com/en/3.0.x/) - we used version 2.3.3 for this article
- Supabase Dashboard - [create an account](https://database.new/) if you don't have one already

## Getting started

To begin, inside your Flask application install the `supabase` library using the following command in the terminal:

```bash
pip install supabase
```

## Session storage

Open the project in your preferred code editor and create a file called `flask_storage.py` with the following content:

```python
from gotrue import SyncSupportedStorage
from flask import session

class FlaskSessionStorage(SyncSupportedStorage):
    def __init__(self):
        self.storage = session

    def get_item(self, key: str) -> str | None:
        if key in self.storage:
            return self.storage[key]

    def set_item(self, key: str, value: str) -> None:
        self.storage[key] = value

    def remove_item(self, key: str) -> None:
        if key in self.storage:
            self.storage.pop(key, None)
```

In this file, we're extending the `SyncSupportedStorage` class from the `gotrue` library which comes bundled with the `supabase` library. Here we're telling the Supabase authentication library (`gotrue`) how to retrieve, store and remove a session that will store our JSON Web Token (JWT).

## Initiate the client

Create another file called `supabase_client.py` and in this file, we'll initiate our Supabase client.

```python
import os
from flask import g
from werkzeug.local import LocalProxy
from supabase.client import Client, ClientOptions
from flask_storage import FlaskSessionStorage

url = os.environ.get("SUPABASE_URL", "")
key = os.environ.get("SUPABASE_KEY", "")

def get_supabase() -> Client:
    if "supabase" not in g:
        g.supabase = Client(
            url,
            key,
            options=ClientOptions(
                storage=FlaskSessionStorage(),
                flow_type="pkce"
            ),
        )
    return g.supabase

supabase: Client = LocalProxy(get_supabase)
```

Let's focus on the `get_supabase` function. Here we are checking if we have an instance of the client stored in our global object `g`, if not we create the client and store it in the global object under the `supabase` name. You will notice in the `ClientOptions` that we are specifying the `FlaskSessionStorage` class we created earlier and we are also specifying a very important option that allows us to handle the OAuth flow on the server side, the `flow_type="pkce"`.

## Sign in with GitHub

Supabase Auth supports Sign in with GitHub on the web, native Android applications, and Chrome extensions.

For detailed set up and implementation instructions please refer to the [docs](https://supabase.com/docs/guides/auth/social-login/auth-github).

## Create sign-in route

Inside our application code `app.py`, we can create the sign-in route to trigger the OAuth sign-in request.

```python
@app.route("/signin/github")
def signin_with_github():
    res = supabase.auth.sign_in_with_oauth(
        {
            "provider": "github",
            "options": {
	            "redirect_to": f"{request.host_url}callback"
	        },
        }
    )
    return redirect(res.url)
```

In this function `options` object we specify a `redirect_to` parameter which will point to the callback route we will create in the next step. This function will generate a url for us to use to redirect the user to, in this case we are using `github` as our OAuth provider so we will be redirected to the GitHub OAuth consent screen.

## Create callback route

Let's add another route to our `app.py` file for the callback endpoint we specified in our sign in route.

```python
@app.route("/callback")
def callback():
    code = request.args.get("code")
    next = request.args.get("next", "/")

    if code:
        res = supabase.auth.exchange_code_for_session({"auth_code": code})

    return redirect(next)
```

Here we're getting the `code` query parameter from the request object, if this is available we then exchange the code for a session so that the user will be signed in. Under the hood the `supabase` python library will handle storing this session (JWT) into a cookie and sign the user in.

## Conclusion

In this post we explained how to setup a flask session storage to work with the Supabase python library, setting the `flow_type` to use Proof Key for Code Exchange (PKCE) and creating a sign in and a callback route to handle the user authentication.

## More Resources

- [supabase-py reference docs](https://supabase.com/docs/reference/python/installing)
- [supabase-py GitHub repo](https://github.com/supabase-community/supabase-py)
- [Deep Dive series on auth concepts in Supabase](https://supabase.com/docs/learn/auth-deep-dive/auth-deep-dive-jwts)
