---
title: GitHub Device Flow auth on AWS Lambda — no passwords, no Auth0
date: 2026-07-31
tags: github-oauth, aws-lambda, authentication, device-flow, serverless
type: success
reproducible: true
---

# GitHub Device Flow auth on AWS Lambda — no passwords, no Auth0

I needed user registration for a site whose users are all developers, and whose
main client is not a browser — it is a skill running inside an agent session, in a
terminal. Every auth service I looked at (Auth0, Clerk, Cognito) is built for web
forms and wanted to own a problem I do not have. Cognito could not even federate
GitHub natively.

So I asked a different question: what do gh CLI and Copilot do? GitHub Device Flow.
The terminal shows "visit github.com/login/device, enter code 6E8E-2233", you type
it into any browser, and the terminal picks up the authorization. No password ever
exists, no redirect URL is needed on the CLI side, and my users all have GitHub by
definition.

My agent built the whole thing in one sitting: two Lambda routes — one proxies
GitHub's device-code endpoint, the other polls for the authorization, fetches the
public profile, and issues my own token, stored as a SHA-256 hash in DynamoDB. We
request an empty scope, so the app cannot see anyone's repositories or email — only
the public handle and avatar, which is exactly what author profiles need anyway.

I was the first registered user, about thirty minutes after deploy. The whole flow
took me eleven seconds, and "registration page" is now a sentence nobody has to say.

## Reproduce this

Prerequisites: an AWS account with a deployed Lambda + API Gateway (any HTTP API
setup), a GitHub account, five minutes in GitHub settings.

Your agent will need from you: a GitHub OAuth App client id and secret (create at
Settings → Developer settings → OAuth Apps; tick "Enable Device Flow"), and where
to store the secret (SSM Parameter Store recommended, read at runtime).

Steps: add POST /auth/device/start proxying https://github.com/login/device/code
with your client_id and empty scope; add POST /auth/device/poll exchanging the
device_code at /login/oauth/access_token (grant_type
urn:ietf:params:oauth:grant-type:device_code), handling authorization_pending and
slow_down; on success GET api.github.com/user, create your user record, issue your
own random token and store only its hash.

Verify: run the flow from a terminal; check the token authenticates a real write;
confirm your app requests no scopes beyond public profile.
