Implementing GitHub Login in Vue with Appwrite
Introduction
In modern web application development, social media login has become a standard feature. This article will demonstrate how to quickly implement GitHub login functionality in a Vue project using Appwrite cloud services. Appwrite is an open-source Backend-as-a-Service (BaaS) platform that provides rich APIs and SDKs, helping developers easily implement user authentication, database, storage, and other functionalities.
Prerequisites
1. GitHub OAuth Application Setup
- Log into GitHub and navigate to Settings > Developer settings > OAuth Apps
- Click “New OAuth App” to create a new application
- Fill in the application information:
- Application name: Your application name
- Homepage URL: Application homepage URL (e.g., http://localhost:3000)
- Authorization callback URL: Authentication callback URL (e.g., http://localhost:3000/auth/callback)
- After creation, note down the Client ID and Client Secret
2. Appwrite Platform Configuration
- Register and log into the Appwrite console
- Create a new project
- Add platform in project settings:
- Select Web platform
- Add project domain (use localhost for development)
- In Authentication settings:
- Enable GitHub OAuth provider
- Configure GitHub Client ID and Client Secret
3. Vue Project Environment Setup
# Create Vue project (if it's a new project)
npm create vue@latest my-vue-app
# Install Appwrite SDK
npm install appwrite
# Install Vue Router (if needed)
npm install vue-router
Implementation Steps
1. Configure Appwrite Service
Create src/services/appwrite.js file:
import { Client, Account } from 'appwrite';
// Initialize Appwrite client
const client = new Client()
.setEndpoint('Your Appwrite API Endpoint') // e.g., https://cloud.appwrite.io/v1
.setProject('Your Project ID');
// Export Account instance for authentication operations
export const account = new Account(client);
2. Create Login Component
Create src/components/GitHubLogin.vue component:
<template>
<div class="github-login">
<button @click="handleLogin" :disabled="loading">
{{ loading ? 'Logging in...' : 'Login with GitHub' }}
</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { account } from '../services/appwrite';
import { useRouter } from 'vue-router';
const router = useRouter();
const loading = ref(false);
const handleLogin = async () => {
try {
loading.value = true;
// Create GitHub OAuth session
await account.createOAuth2Session(
'github',
'http://localhost:3000/auth/callback', // Success callback URL
'http://localhost:3000/auth/error' // Error callback URL
);
} catch (error) {
console.error('GitHub login failed:', error);
loading.value = false;
}
};
</script>
<style scoped>
.github-login button {
padding: 10px 20px;
background-color: #24292e;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.github-login button:disabled {
background-color: #6e7681;
cursor: not-allowed;
}
</style>
3. Create Callback Handler Component
Create src/components/AuthCallback.vue component:
<template>
<div class="auth-callback">
<p v-if="loading">Processing login...</p>
<p v-if="error">{{ error }}</p>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { account } from '../services/appwrite';
import { useRouter } from 'vue-router';
const router = useRouter();
const loading = ref(true);
const error = ref('');
onMounted(async () => {
try {
// Get current session information
const session = await account.getSession('current');
// Get user information
const user = await account.get();
// Store user information locally
localStorage.setItem('user', JSON.stringify(user));
// Login successful, redirect to homepage
router.push('/');
} catch (err) {
error.value = 'Authentication failed: ' + err.message;
} finally {
loading.value = false;
}
});
</script>
4. Configure Router
Add route configuration in src/router/index.js:
import { createRouter, createWebHistory } from 'vue-router';
import GitHubLogin from '../components/GitHubLogin.vue';
import AuthCallback from '../components/AuthCallback.vue';
const routes = [
{
path: '/login',
component: GitHubLogin,
},
{
path: '/auth/callback',
component: AuthCallback,
},
{
path: '/auth/error',
component: () => import('../components/AuthError.vue'),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
Usage and Testing
- Start Vue development server:
npm run dev
-
Visit the login page (e.g., http://localhost:3000/login)
-
Click “Login with GitHub” button
-
Authorize GitHub access
-
Wait for callback processing to complete, automatic redirect to homepage
Security Considerations
- Environment Variables: Store Appwrite configuration in
.envfile:
VITE_APPWRITE_ENDPOINT=Your Appwrite API Endpoint
VITE_APPWRITE_PROJECT_ID=Your Project ID
- Session Management:
- Regularly check session status
- Implement logout functionality
- Handle session expiration
// Check session status
const checkSession = async () => {
try {
await account.getSession('current');
return true;
} catch {
return false;
}
};
// Logout functionality
const logout = async () => {
try {
await account.deleteSession('current');
localStorage.removeItem('user');
router.push('/login');
} catch (error) {
console.error('Logout failed:', error);
}
};
Common Issues
-
Cross-Origin Issues: Ensure correct domain is added in Appwrite platform configuration
-
Callback URL Error: Verify callback URL configuration matches between GitHub OAuth application and Appwrite platform
-
Safari ITP Impact on Login: Safari’s Intelligent Tracking Prevention (ITP) may affect login functionality
-
Issue Principle:
- Safari’s ITP feature limits third-party cookie access
- Appwrite as a third-party service needs to store authentication cookies in the browser
- ITP blocks websites from reading these third-party cookies, causing session state loss
-
Solutions:
- Prompt users to temporarily disable “Prevent Cross-Site Tracking” in Safari
- Deploy Appwrite service with a custom domain as same-domain service
- Implement alternative local storage authentication scheme
- Session State Anomalies: Implement retry mechanism
const retryOperation = async (operation, maxAttempts = 3) => {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxAttempts) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
};
Summary
Implementing GitHub login through Appwrite cloud service has several advantages:
- Quick Integration: No need to build backend services
- Secure and Reliable: Uses standard OAuth2.0 protocol
- Easy Maintenance: Appwrite provides complete management interface
- Scalability: Support for adding other social login methods
The implementation approach described in this article is suitable for most small to medium-sized Vue projects. For larger projects, more complex authentication flows and user permission management may need to be considered.