---
title: "Github Oauth Callback"
method: GET
path: "/api/v1/auth/github/callback"
tags: ["auth"]
---

# Github Oauth Callback

`GET /api/v1/auth/github/callback`

## Request

- Base URL: `https://api.badgeia.com`
- URL: `https://api.badgeia.com/api/v1/auth/github/callback`
- Auth: none declared

Handle the GitHub OAuth callback (GET — GitHub redirects the user
here with ?code=... after they authorize the app on github.com).

Post-PR-G the callback sets `badge_session` + `badge_refresh` cookies
on the redirect response itself and lands the browser on
`{FRONTEND_URL}/auth/callback` *without* the URL fragment. The SPA's
callback page used to parse a JWT out of `window.location.hash`; now
it just calls `/auth/me` (which auto-attaches the new cookie) and
redirects into the app.

Putting the JWT in the URL fragment was a pragmatic workaround when
we were stuck with localStorage and didn't want the token in server
access logs — but fragments still leak via document.referrer to
third-party scripts on the callback page, and to the browser's
history. HttpOnly cookies have neither problem.

NOTE: this endpoint was previously @router.post — GitHub does GET
redirects, so the old wiring returned 405. Verified 2026-05-17.

BRIEF-LP-000 §6.3 — `signup_completed` (canonical) fires on the new-
user branch. The login branch (`is_new=False`) fires `user_logged_in`
only, since the user already had a signup event from a prior session.

v0.1.29: the legacy `user_signed_up` parallel-fire event was removed
after the v0.1.24 deprecation window.

## Query parameters

- `code` string
- `state` string
- `dev` boolean
- `error` string

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/badgeia/apis/badge.md) · [All operations](https://skmtc.dev/badgeia/apis/badge/llms.txt) · [OpenAPI document](https://skmtc.dev/badgeia/apis/badge/revisions/7e7f7fee8b77?raw)
