GitHub OAuth 2.0 for CLI Applications
Implement GitHub OAuth 2.0 authentication in a command-line tool using the Device Code flow. Includes complete flow walkthrough.
Real-World Flows
Detailed Explanation
GitHub OAuth 2.0 for CLI Tools
GitHub supports the Device Code flow, making it ideal for CLI applications where users cannot enter credentials directly. The CLI displays a code and URL, the user authenticates in their browser, and the CLI receives the token.
GitHub OAuth Endpoints
| Endpoint | URL |
|---|---|
| Device Authorization | https://github.com/login/device/code |
| Token | https://github.com/login/oauth/access_token |
| User API | https://api.github.com/user |
Step 1: Create GitHub OAuth App
- Go to GitHub Settings > Developer settings > OAuth Apps
- Register a new application
- Note: GitHub OAuth Apps do not have a client_secret requirement for the device flow
- Save the
client_id
Step 2: Request Device Code
curl -X POST https://github.com/login/device/code \
-H "Accept: application/json" \
-d "client_id=YOUR_CLIENT_ID&scope=repo%20user"
Response:
{
"device_code": "3584d83530557fdd1f46af8289938c8ef79f9dc5",
"user_code": "WDJB-MJHT",
"verification_uri": "https://github.com/login/device",
"expires_in": 899,
"interval": 5
}
Step 3: Display to User
! First copy your one-time code: WDJB-MJHT
- Press Enter to open github.com in your browser...
Step 4: Poll for Token
curl -X POST https://github.com/login/oauth/access_token \
-H "Accept: application/json" \
-d "client_id=YOUR_CLIENT_ID\
&device_code=3584d83530557fdd1f46af8289938c8ef79f9dc5\
&grant_type=urn:ietf:params:oauth:grant-type:device_code"
Pending response: {"error": "authorization_pending"}
Success response:
{
"access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
"token_type": "bearer",
"scope": "repo,user"
}
Step 5: Use the Token
curl -H "Authorization: Bearer gho_16C7e42F292c6912E7710c838347Ae178B4a" \
https://api.github.com/user
Node.js Implementation
const open = require("open");
async function githubDeviceAuth(clientId) {
// Step 1: Get device code
const codeRes = await fetch("https://github.com/login/device/code", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
},
body: `client_id=${clientId}&scope=repo user`,
}).then(r => r.json());
console.log(`Enter code: ${codeRes.user_code}`);
await open(codeRes.verification_uri);
// Step 2: Poll for token
while (true) {
await new Promise(r => setTimeout(r, codeRes.interval * 1000));
const tokenRes = await fetch(
"https://github.com/login/oauth/access_token",
{
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded",
},
body: `client_id=${clientId}&device_code=${codeRes.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code`,
}
).then(r => r.json());
if (tokenRes.access_token) return tokenRes.access_token;
if (tokenRes.error === "authorization_pending") continue;
throw new Error(tokenRes.error_description || tokenRes.error);
}
}
Common GitHub Scopes
| Scope | Access |
|---|---|
repo |
Full control of private repositories |
repo:status |
Read/write commit statuses |
user |
Read user profile data |
read:org |
Read org membership |
gist |
Create gists |
workflow |
Update GitHub Actions workflows |
Use Case
Building a CLI tool (like GitHub CLI, Terraform, or a custom deployment tool) that needs to authenticate with GitHub. The device code flow lets users authenticate securely in their browser without pasting tokens into the terminal.