Skip to content

Commit 76e0d82

Browse files
committed
replace GitHub with Github
1 parent 680c9b5 commit 76e0d82

1 file changed

Lines changed: 26 additions & 26 deletions

File tree

  • src/content/post/2026/02-07-github-login-fastapi-nextjs

src/content/post/2026/02-07-github-login-fastapi-nextjs/index.mdx

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
2-
title: GitHub login with FastAPI and Next.js
2+
title: Github login with FastAPI and Next.js
33
description: |
4-
A practical example of implementing GitHub OAuth in FastAPI, and why Next.js server actions and API routes are convenient for managing cookies and domains.
4+
A practical example of implementing Github OAuth in FastAPI, and why Next.js server actions and API routes are convenient for managing cookies and domains.
55
publishDate: 2026-02-07
66
heroImage: '../../../../content/post/2026/02-07-github-login-fastapi-nextjs/_images/github-login-architecture-16-9.png'
7-
heroAlt: GitHub login architecture diagram
7+
heroAlt: Github login architecture diagram
88
tags:
99
- next.js
1010
- fastapi
@@ -23,30 +23,30 @@ import OAuth2FlowDiagramImage from '../../../../content/post/2026/02-07-github-l
2323

2424
## Introduction
2525

26-
In this article, we will show how to implement GitHub login in a FastAPI and Next.js application. We use GitHub in this particular case, but the same approach applies to any OAuth provider, you only need to adjust the FastAPI redirect and callback endpoints. Since this is a Next.js app using server components, we will store the session in an HttpOnly cookie. We will dig into implementation details such as domains, cookies, redirects, and overall structuring to achieve a clean, maintainable, and robust solution.
26+
In this article, we will show how to implement Github login in a FastAPI and Next.js application. We use Github in this particular case, but the same approach applies to any OAuth provider, you only need to adjust the FastAPI redirect and callback endpoints. Since this is a Next.js app using server components, we will store the session in an HttpOnly cookie. We will dig into implementation details such as domains, cookies, redirects, and overall structuring to achieve a clean, maintainable, and robust solution.
2727

2828
## OAuth flow reminder
2929

3030
**OAuth2 flow sequential diagram:** ([Source gist](https://gist.github.com/cseeman/cf1a0cf7d931794d78f570e9f413f4a1))
3131

3232
<Image {...IMAGE_SIZES.FIXED.MDX_MD} src={OAuth2FlowDiagramImage} alt="OAuth2 flow sequential diagram" />
3333

34-
Let's begin with a quick reminder of how OAuth works in very simplified terms. OAuth is built around the principle of a trusted middleman: both we (our app) and the user know who GitHub is (the authorization server) and trust it. This means we can use GitHub to identify the user and obtain their information. For the user, this means GitHub vouches for our app's identity and legitimacy, clearly showing what information the app will access and at what level, so the user can give informed consent. Of course, there are many more implementation details, but this is enough for a high-level overview.
34+
Let's begin with a quick reminder of how OAuth works in very simplified terms. OAuth is built around the principle of a trusted middleman: both we (our app) and the user know who Github is (the authorization server) and trust it. This means we can use Github to identify the user and obtain their information. For the user, this means Github vouches for our app's identity and legitimacy, clearly showing what information the app will access and at what level, so the user can give informed consent. Of course, there are many more implementation details, but this is enough for a high-level overview.
3535

36-
For our app, this practically means we need to register it with GitHub, obtain the app's client ID and client secret, and then, in the backend, use an OAuth client library to implement two endpoints:
36+
For our app, this practically means we need to register it with Github, obtain the app's client ID and client secret, and then, in the backend, use an OAuth client library to implement two endpoints:
3737

38-
1. An endpoint that redirects the user to GitHub, where they can give consent.
39-
2. A callback endpoint where GitHub redirects the user back to us, passing an authorization code that the auth library can exchange for an access token, which is then used to call GitHub APIs and obtain additional information about the user.
38+
1. An endpoint that redirects the user to Github, where they can give consent.
39+
2. A callback endpoint where Github redirects the user back to us, passing an authorization code that the auth library can exchange for an access token, which is then used to call Github APIs and obtain additional information about the user.
4040

4141
Additionally, within the callback endpoint we store the user's information (email, OAuth ID, name, avatar, etc.) in the database and use the autogenerated database user ID to generate a JWT access token, in the same way we do for a regular email/password authenticated user.
4242

43-
In this way, we achieve a unified interface for authenticating users, regardless of whether they log in with GitHub or via email/password.
43+
In this way, we achieve a unified interface for authenticating users, regardless of whether they log in with Github or via email/password.
4444

4545
## Architecture overview
4646

4747
**Architecture diagram:**
4848

49-
<Image {...IMAGE_SIZES.FIXED.MDX_MD} src={GithubLoginArchitectureImage} alt="GitHub login architecture diagram" />
49+
<Image {...IMAGE_SIZES.FIXED.MDX_MD} src={GithubLoginArchitectureImage} alt="Github login architecture diagram" />
5050

5151
The one obvious and constant assumption is that we will use a FastAPI backend and a Next.js frontend. When it comes to authorization, this leaves additional room for deciding how we structure the logic and separate concerns. There is more than one way to do this, and some approaches can be fragile and hard to maintain, which is exactly what we want to avoid.
5252

@@ -60,9 +60,9 @@ Let's go straight to the point and explain the optimal approach that we will use
6060

6161
- Cookies are tied to a domain and are impractical for passing arguments via HTTP responses. Cookies are meant for storing data, not for transmitting it. Consequently, for passing the `access_token` and `expires` values, we will use the response body (for server actions) and URL query parameters (for the OAuth redirect). Response bodies and URL query parameters are domain-independent and are designed for passing data between HTTP requests.
6262

63-
- Separation of concerns on the backend: FastAPI will contain all backend logic, including authorization. This means it will implement both OAuth endpoints (the redirect and callback endpoints). Next.js will handle cookie setting and unsetting logic: via server actions for email/password login, and via Next.js API routes for GitHub login. As a reminder, a server action is essentially a POST endpoint under the hood and can set or unset cookies.
63+
- Separation of concerns on the backend: FastAPI will contain all backend logic, including authorization. This means it will implement both OAuth endpoints (the redirect and callback endpoints). Next.js will handle cookie setting and unsetting logic: via server actions for email/password login, and via Next.js API routes for Github login. As a reminder, a server action is essentially a POST endpoint under the hood and can set or unset cookies.
6464

65-
- The OAuth callback endpoint in FastAPI needs to initiate an uninterrupted redirect chain composed of two steps (FastAPI and Next.js API): `GitHub -> FastAPI callback redirect -> Next.js API redirect -> Next.js home page`. During this process, the `access_token` and `expires` values need to be passed as query parameters appended to the URL. Redirects are mandatory because the entire flow is driven by the browser, and we do not want the user's browser to just land on a raw API response, but rather on the website's home page as a successfully logged-in user.
65+
- The OAuth callback endpoint in FastAPI needs to initiate an uninterrupted redirect chain composed of two steps (FastAPI and Next.js API): `Github -> FastAPI callback redirect -> Next.js API redirect -> Next.js home page`. During this process, the `access_token` and `expires` values need to be passed as query parameters appended to the URL. Redirects are mandatory because the entire flow is driven by the browser, and we do not want the user's browser to just land on a raw API response, but rather on the website's home page as a successfully logged-in user.
6666

6767
### Suboptimal approaches and their problems
6868

@@ -82,11 +82,11 @@ There is some ambiguity caused by the redundancy of options, which can lead to s
8282

8383
That was a lot of text but still no code. On the other hand when we have clear mental model and worked out plan implementation is straight forward.
8484

85-
### Create OAuth app on GitHub
85+
### Create OAuth app on Github
8686

87-
Like with any OAuth provider we need to register our app on GitHub and obtain client id and client secret. One GitHub specific is that you can have set only one redirect URL per app, so if you want multiple deployments you will need to create a separate app for each of them.
87+
Like with any OAuth provider we need to register our app on Github and obtain client id and client secret. One Github specific is that you can have set only one redirect URL per app, so if you want multiple deployments you will need to create a separate app for each of them.
8888

89-
It's a straight forward process, go to your GitHub profile and open the following menus: `GitHub (top-right avatar) -> Settings -> Developer settings (bottom of the left sidebar) -> OAuth Apps -> New OAuth App`. Fill in your app info, including redirect URL where you should set the URL of your FastAPI callback endpoint, e.g. `https://api.my-website.com/api/v1/auth/github/callback`.
89+
It's a straight forward process, go to your Github profile and open the following menus: `Github (top-right avatar) -> Settings -> Developer settings (bottom of the left sidebar) -> OAuth Apps -> New OAuth App`. Fill in your app info, including redirect URL where you should set the URL of your FastAPI callback endpoint, e.g. `https://api.my-website.com/api/v1/auth/github/callback`.
9090

9191
Then copy `Client ID` and `Client secret` and set inside the backend `.env` file.
9292

@@ -140,13 +140,13 @@ oauth = create_oauth()
140140

141141
We can then use the instantiated OAuth client to implement the OAuth redirect and callback endpoints.
142142

143-
The redirect endpoint is quite simple, almost trivial. When the user hits this endpoint, they are redirected to the GitHub login page, where they can give consent. The `redirect_uri` variable contains the absolute URL of our callback endpoint, which we define next.
143+
The redirect endpoint is quite simple, almost trivial. When the user hits this endpoint, they are redirected to the Github login page, where they can give consent. The `redirect_uri` variable contains the absolute URL of our callback endpoint, which we define next.
144144

145145
```py title="backend/app/api/routes/login.py"
146146
@router.get("/login/github")
147147
async def login_github(request: Request):
148148
"""
149-
Redirect to GitHub login page
149+
Redirect to Github login page
150150
Must initiate OAuth flow from backend
151151
"""
152152
redirect_uri = request.url_for("auth_github_callback") # matches function name
@@ -158,9 +158,9 @@ async def login_github(request: Request):
158158
return await security.oauth.github.authorize_redirect(request, redirect_uri)
159159
```
160160

161-
Now we can define the callback endpoint, which is where GitHub sends the user after they have logged in on GitHub. This part is a bit more complex.
161+
Now we can define the callback endpoint, which is where Github sends the user after they have logged in on Github. This part is a bit more complex.
162162

163-
GitHub includes an authorization code as a URL parameter, which we use to obtain an OAuth access token. We then use this token to call two separate GitHub APIs: one to retrieve the user’s profile information (full name, username, and OAuth ID), and another to retrieve the user's primary email address. Next, we find or create the user in our database. Finally, we use the user's database ID to create a JWT token, in exactly the same way as we do for a regular email/password user.
163+
Github includes an authorization code as a URL parameter, which we use to obtain an OAuth access token. We then use this token to call two separate Github APIs: one to retrieve the user’s profile information (full name, username, and OAuth ID), and another to retrieve the user's primary email address. Next, we find or create the user in our database. Finally, we use the user's database ID to create a JWT token, in exactly the same way as we do for a regular email/password user.
164164

165165
Next, we calculate the `expires` value for the session cookie so that it matches the JWT `access_token` expiration. We then attach the `access_token` and `expires` values as query parameters to the redirect URL. The redirect URL is constructed as `f"{settings.SITE_URL}/api/auth/set-cookie"`, pointing to a Next.js API endpoint (which we define next) that is responsible for actually setting the cookie. Finally, we redirect the user.
166166

@@ -172,20 +172,20 @@ async def auth_github_callback(
172172
request: Request, session: SessionDep
173173
) -> RedirectResponse:
174174
"""
175-
GitHub OAuth callback, GitHub will call this endpoint
175+
Github OAuth callback, Github will call this endpoint
176176
"""
177177
# Exchange code for access token
178178
token = await security.oauth.github.authorize_access_token(request)
179179

180-
# Get user profile GitHub API
180+
# Get user profile Github API
181181
user_info = await security.oauth.github.get("user", token=token)
182182
profile = user_info.json()
183183

184-
# Get primary email GitHub API
184+
# Get primary email Github API
185185
emails = await security.oauth.github.get("user/emails", token=token)
186186
primary_email = next((e["email"] for e in emails.json() if e["primary"]), None)
187187

188-
logger.info(f"Primary GitHub email: {primary_email}")
188+
logger.info(f"Primary Github email: {primary_email}")
189189

190190
# Authenticate or create user
191191
user = crud.authenticate_github(
@@ -228,7 +228,7 @@ class Token(SQLModel):
228228

229229
### Set cookie - Next.js API endpoint
230230

231-
Now that we have identified the user on GitHub and created the JWT `access_token`, the only remaining step is to set the cookie. As mentioned earlier, in the OAuth flow this is done in a Next.js API endpoint.
231+
Now that we have identified the user on Github and created the JWT `access_token`, the only remaining step is to set the cookie. As mentioned earlier, in the OAuth flow this is done in a Next.js API endpoint.
232232

233233
Below is the complete endpoint implementation. As you can see, it is not too complicated. We simply parse the `access_token` and `expires` values from the URL query parameters, use them to construct the cookie, and attach the cookie to a redirect response that sends the user to the home page. This final step sets the cookie, and that's it.
234234

@@ -345,7 +345,7 @@ export const logoutAction = async (): Promise<void> => {
345345

346346
So what is different between the email/password and OAuth flows?
347347

348-
The OAuth flow is based on two consecutive redirects: `FastAPI callback endpoint -> Next.js API set-cookie endpoint -> Home page`. This is mandatory because the callback endpoint is the only thing GitHub provides us, and the `access_token` and `expires` values must be passed as query parameters attached to the redirect responses.
348+
The OAuth flow is based on two consecutive redirects: `FastAPI callback endpoint -> Next.js API set-cookie endpoint -> Home page`. This is mandatory because the callback endpoint is the only thing Github provides us, and the `access_token` and `expires` values must be passed as query parameters attached to the redirect responses.
349349

350350
In contrast, the email/password flow is based on an HTML form and a server action, which follows a standard request/response pattern. This allows the `access_token` and `expires` values to be sent directly in the response body.
351351

@@ -384,7 +384,7 @@ Let's conclude this article by summarizing the upsides and downsides of choosing
384384
- The frontend and backend are fully independent. We can use any domains for both by simply setting the `SITE_URL` and `API_URL` environment variables.
385385
- We can deploy to platforms like `vercel.app` without needing any additional modifications.
386386
- We maintain a single, unified interface for both email/password and OAuth logins.
387-
- The approach is applicable to any OAuth provider, not just GitHub. You only need to define the appropriate redirect and callback endpoints in FastAPI.
387+
- The approach is applicable to any OAuth provider, not just Github. You only need to define the appropriate redirect and callback endpoints in FastAPI.
388388

389389
**Downsides:**
390390

0 commit comments

Comments
 (0)