Skip to content

feat: set up github login test #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
SUPABASE_URL="http://localhost:8000"
SUPABASE_KEY="<your_key>"

NUXT_GITHUB_CLIENT_ID=your_client_id_here
NUXT_GITHUB_CLIENT_SECRET=your_client_secret_here
214 changes: 214 additions & 0 deletions app/pages/test/github-login.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<template>
<UContainer class="py-12">
<div class="max-w-2xl mx-auto space-y-8">
<!-- Header -->
<div class="text-center">
<h1 class="text-3xl font-bold mb-4">GitHub OAuth Test Page</h1>
<p class="text-gray-600">Test GitHub login functionality without Supabase</p>
</div>

<!-- Login Button -->
<div v-if="!isLoading && !userData" class="text-center">
<UButton
color="primary"
size="lg"
icon="i-lucide-github"
label="Login with GitHub"
@click="handleLogin"
/>
</div>

<!-- Loading State -->
<div v-if="isLoading" class="text-center py-12">
<UProgress class="mx-auto" indeterminate />
<p class="mt-2 text-gray-600">{{ loadingMessage }}</p>
</div>

<!-- Error Display -->
<UAlert
v-if="error"
color="error"
:title="error.title"
:description="error.message"
/>

<!-- User Data Display -->
<div v-if="userData" class="space-y-6">
<!-- User Profile -->
<div class="flex items-center space-x-4 p-4 bg-gray-50 rounded-lg">
<img
:src="userData.avatar_url"
:alt="userData.login"
class="w-16 h-16 rounded-full"
/>
<div>
<h2 class="text-xl font-bold">{{ userData.name }}</h2>
<p class="text-gray-600">{{ userData.login }}</p>
<p class="text-sm text-gray-500">{{ userData.email }}</p>
</div>
</div>

<!-- Access Token -->
<div class="space-y-2">
<h3 class="font-bold">Access Token:</h3>
<div class="bg-gray-50 p-4 rounded-lg break-all font-mono text-sm">
{{ accessToken }}
</div>
</div>

<!-- Raw Data -->
<div class="space-y-2">
<h3 class="font-bold">Raw User Data:</h3>
<pre class="bg-gray-50 p-4 rounded-lg overflow-auto text-sm">{{ JSON.stringify(userData, null, 2) }}</pre>
</div>

<!-- Logout Button -->
<div class="text-center">
<UButton
color="error"
variant="soft"
label="Clear Data"
@click="clearData"
/>
</div>
</div>
</div>
</UContainer>
</template>

<script setup lang="ts">
// Types
interface GitHubError {
title: string
message: string
}

interface GitHubUser {
login: string
name: string
email: string
avatar_url: string
[key: string]: unknown
}

interface GitHubResponse {
error?: string
error_description?: string
access_token?: string
}

// State
const config = useRuntimeConfig()
const route = useRoute()
const isLoading = ref(false)
const loadingMessage = ref('')
const error = ref<GitHubError | null>(null)
const userData = ref<GitHubUser | null>(null)
const accessToken = ref<string | null>(null)

// Constants
const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize'
const GITHUB_USER_URL = 'https://api.github.com/user'

// Methods
const handleLogin = () => {
const params = new URLSearchParams()
params.append('client_id', config.public.githubClientId as string)
params.append('redirect_uri', window.location.href)
params.append('scope', 'read:user user:email')
window.location.href = `${GITHUB_AUTH_URL}?${params}`
}

const getAccessToken = async (code: string) => {
try {
isLoading.value = true
loadingMessage.value = 'Getting access token...'

const response = await fetch('/api/github/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ code }),
})

const data = await response.json() as GitHubResponse
if (data.error) {
throw new Error(data.error_description || 'Failed to get access token')
}
if (!data.access_token) {
throw new Error('No access token received')
}

return data.access_token
} catch (err) {
if (err instanceof Error) {
error.value = {
title: 'Authentication Error',
message: err.message,
}
}
return null
}
}

const getUserData = async (token: string) => {
try {
loadingMessage.value = 'Fetching user data...'

const response = await fetch(GITHUB_USER_URL, {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
},
})

if (!response.ok) {
throw new Error('Failed to fetch user data')
}

return await response.json() as GitHubUser
} catch (err) {
if (err instanceof Error) {
error.value = {
title: 'User Data Error',
message: err.message,
}
}
return null
}
}

const clearData = () => {
userData.value = null
accessToken.value = null
error.value = null
// Remove code from URL without page reload
window.history.replaceState({}, document.title, window.location.pathname)
}

// Handle OAuth callback
onMounted(async () => {
const code = route.query.code as string
if (code) {
isLoading.value = true
error.value = null

// Get access token
const token = await getAccessToken(code)
if (token) {
accessToken.value = token

// Get user data
const user = await getUserData(token)
if (user) {
userData.value = user
}
}

isLoading.value = false
// Remove code from URL without page reload
window.history.replaceState({}, document.title, window.location.pathname)
}
})
</script>
87 changes: 87 additions & 0 deletions docs/github-oauth-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# GitHub OAuth Test Page Setup

This guide explains how to set up and test GitHub OAuth login functionality without Supabase integration.

## 1. Create GitHub OAuth App

1. Go to GitHub Developer Settings:
- Visit <https://github.com/settings/developers>
- Click "OAuth Apps" in the sidebar
- Click "New OAuth App"

2. Fill in the application details:
- **Application name**: `Code for Philly Dev`
- **Homepage URL**: `http://localhost:3000`
- **Authorization callback URL**: `http://localhost:3000/test/github-login`
- Click "Register application"

3. After registration:
- You'll see your Client ID immediately
- Click "Generate a new client secret"
- Save both the Client ID and Client Secret

## 2. Configure Environment Variables

1. Add these variables to your `.env` file:

```bash
# GitHub OAuth
NUXT_GITHUB_CLIENT_ID=your_client_id_here
NUXT_GITHUB_CLIENT_SECRET=your_client_secret_here
```

## 3. Implementation Details

The test page is located at `/test/github-login` and implements:

1. Initial state with login button
2. OAuth redirect handling
3. Display of user data after successful login

### How it Works

1. User clicks "Login with GitHub"
2. GitHub OAuth flow initiates
3. After authorization, GitHub redirects back with a code
4. The page exchanges the code for an access token
5. User data is fetched and displayed

### Testing

1. Start the development server:

```bash
npm run dev
```

2. Visit <http://localhost:3000/test/github-login>

3. Click "Login with GitHub"

4. After authorizing, you'll see:
- Access token
- User profile data
- Raw API response

## Security Notes

- This is a test implementation
- Never commit OAuth secrets to version control
- The client secret should be kept secure in production
- This implementation is for local testing only

## Troubleshooting

1. If the login button doesn't work:
- Verify your OAuth app settings
- Check that environment variables are set
- Ensure the callback URL matches exactly

2. If you get a redirect error:
- Verify the callback URL in GitHub matches exactly
- Check for any typos in the environment variables

3. If no user data displays:
- Check browser console for errors
- Verify the access token is being received
- Check GitHub API response in the Network tab
7 changes: 7 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ export default defineNuxtConfig({
'@nuxtjs/tailwindcss'
],

runtimeConfig: {
public: {
githubClientId: process.env.NUXT_GITHUB_CLIENT_ID,
githubClientSecret: process.env.NUXT_GITHUB_CLIENT_SECRET,
}
},

ssr: true,

supabase: {
Expand Down
23 changes: 23 additions & 0 deletions server/api/github/token.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineEventHandler, readBody } from 'h3'
import { useRuntimeConfig } from '#imports'

export default defineEventHandler(async (event) => {
const body = await readBody(event)
const config = useRuntimeConfig()

const response = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_id: config.public.githubClientId,
client_secret: config.public.githubClientSecret,
code: body.code,
}),
})

const data = await response.json()
return data
})