A minimal OAuth 2.0 authentication tool for Claude Code using TypeScript and Bun.
- OAuth 2.0 + PKCE: Secure authentication flow with Proof Key for Code Exchange
- GitHub Actions Integration: Ready-to-use workflow for automated authentication
- TypeScript: Fully typed with strict mode enabled
- Minimal Dependencies: Uses only Node.js built-ins and fetch API
Install dependencies:
bun installGenerate login URL:
bun run index.tsExchange authorization code for tokens:
bun run index.ts <authorization_code>This repository includes a GitHub Action for easy OAuth authentication in CI/CD workflows.
This action requires a Personal Access Token (PAT) to securely store OAuth credentials as GitHub secrets. Follow these steps:
-
Go to Settings → Developer settings → Personal access tokens → Fine-grained tokens → "Generate new token"
-
Configure the token:
- Resource owner: Choose the organization or user that owns the repository
- Repository access: Select "Only select repositories" and choose the repository/repositories that will run this action
- Permissions:
- Repository → Secrets: Write (this automatically includes read permission)
- Expiration: Set the shortest practical lifetime (30-60 days) and add a calendar reminder to renew it
- Name: Give it a descriptive name like
actions-secret-sync-<repo>
-
Click "Generate token" and copy the value immediately (GitHub will never show it again)
- In your repository, go to Settings → Secrets and variables → Actions
- Click "New repository secret"
- Add the secret:
- Name:
SECRETS_ADMIN_PAT - Value: Paste your PAT from step 1
- Name:
- Click "Add secret"
Note: You do NOT need the wide-open
reposcope of a classic token. Fine-grained tokens with onlysecrets:writepermission are more secure.
If using the published action from GitHub Marketplace, create .github/workflows/claude-oauth.yml:
name: Claude OAuth
on:
workflow_dispatch:
inputs:
code:
description: 'Authorization code (leave empty for step 1)'
required: false
permissions:
actions: write # Required for cache management
contents: read # Required for basic repository access
jobs:
auth:
runs-on: ubuntu-latest
steps:
- uses: grll/claude-code-login@v1
with:
code: ${{ inputs.code }}
secrets_admin_pat: ${{ secrets.SECRETS_ADMIN_PAT }}For local development or customization:
name: Claude OAuth
on:
workflow_dispatch:
inputs:
code:
description: 'Authorization code (leave empty for step 1)'
required: false
jobs:
auth:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./
with:
code: ${{ inputs.code }}
secrets_admin_pat: ${{ secrets.SECRETS_ADMIN_PAT }}-
Step 1 - Generate Login URL
- Go to your repository's Actions tab
- Select Claude OAuth workflow
- Click Run workflow (leave code field empty)
- Copy the generated login URL
-
Step 2 - Complete Authentication
- Open the URL and sign in to Claude
- Copy the authorization code from the redirect URL
- Run the workflow again with the code
- OAuth tokens will be stored as GitHub secrets
After successful authentication, the OAuth tokens are stored as repository secrets:
CLAUDE_ACCESS_TOKEN- OAuth access token for Claude APICLAUDE_REFRESH_TOKEN- OAuth refresh token for token renewalCLAUDE_EXPIRES_AT- Token expiration timestamp (milliseconds)
To use these credentials in other workflows:
name: Claude PR Assistant
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude-code-action:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && contains(github.event.issue.body, '@claude'))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude PR Action
uses: grll/claude-code-action@beta
with:
use_oauth: true
claude_access_token: ${{ secrets.CLAUDE_ACCESS_TOKEN }}
claude_refresh_token: ${{ secrets.CLAUDE_REFRESH_TOKEN }}
claude_expires_at: ${{ secrets.CLAUDE_EXPIRES_AT }}
timeout_minutes: "60"Run the test suite:
bun testType checking:
bunx tsc --noEmitindex.ts- Main OAuth implementationindex.test.ts- Comprehensive test suiteaction.yml- GitHub Action definitionCLAUDE.md- Development guidelines for Claude Codescripts/- Automation scripts for release management
To update the v1 release to include the latest changes:
./scripts/update-v1-release.shThis script automatically:
- Moves the v1 tag to the latest main commit
- Updates the GitHub release with current changes
- Ensures marketplace users get the latest version
This project was created using bun init in bun v1.2.17. Bun is a fast all-in-one JavaScript runtime.