Cloudflare Worker for GitHub Gist OAuth

November 28, 2024 ยท View on GitHub

human (Nov 28, 2024, 07:06 AM)

Write a Cloudflare worker that implements an oauth flow with GitHub to get a token scoped for gist read and write only

Landing on the worker page redirects to GitHub for the oauth - GitHub sends back to the same page, which then outputs a script block that sets the access key in localstorage

assistant (Nov 28, 2024, 07:06 AM)

I'll help you create a Cloudflare Worker that implements the GitHub OAuth flow for gist permissions.

Cloudflare Worker for GitHub OAuth

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const clientId = env.GITHUB_CLIENT_ID;
    const clientSecret = env.GITHUB_CLIENT_SECRET;
    const redirectUri = url.origin;
    
    // If we have a code, exchange it for an access token
    if (url.searchParams.has('code')) {
      const code = url.searchParams.get('code');
      
      // Exchange the code for an access token
      const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        },
        body: JSON.stringify({
          client_id: clientId,
          client_secret: clientSecret,
          code: code,
          redirect_uri: redirectUri
        })
      });
      
      const tokenData = await tokenResponse.json();
      
      // Return HTML that stores the token and closes the window
      return new Response(`
        <!DOCTYPE html>
        <html>
          <head>
            <title>GitHub OAuth Success</title>
          </head>
          <body>
            <script>
              localStorage.setItem('github_token', '${tokenData.access_token}');
              document.body.innerHTML = 'Authentication successful! You can close this window.';
            </script>
          </body>
        </html>
      `, {
        headers: {
          'Content-Type': 'text/html'
        }
      });
    }
    
    // If no code, redirect to GitHub OAuth
    const githubAuthUrl = new URL('https://github.com/login/oauth/authorize');
    githubAuthUrl.searchParams.set('client_id', clientId);
    githubAuthUrl.searchParams.set('redirect_uri', redirectUri);
    githubAuthUrl.searchParams.set('scope', 'gist');
    githubAuthUrl.searchParams.set('state', crypto.randomUUID());
    
    return Response.redirect(githubAuthUrl.toString(), 302);
  }
};

To use this worker, you'll need to:

  1. Create a GitHub OAuth application at https://github.com/settings/developers

    • Set the homepage URL to your worker's URL
    • Set the callback URL to your worker's URL
  2. Set up two environment variables in your worker:

    • GITHUB_CLIENT_ID: Your OAuth app's client ID
    • GITHUB_CLIENT_SECRET: Your OAuth app's client secret

The flow works like this:

  1. User visits the worker URL
  2. They're redirected to GitHub's OAuth page
  3. After authorizing, GitHub redirects back to your worker with a code
  4. The worker exchanges the code for an access token
  5. The token is stored in localStorage with key 'github_token'

The token will be scoped only to gist read/write permissions since we specifically request the 'gist' scope.

Would you like me to add any additional features like:

  • Error handling
  • CSRF protection
  • Token validation
  • Specific response types for different scenarios?