✨ Add composable for handling Discord OAuth integration
This commit is contained in:
138
code/websites/pokedex.online/src/composables/useDiscordOAuth.js
Normal file
138
code/websites/pokedex.online/src/composables/useDiscordOAuth.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* Discord OAuth Composable
|
||||||
|
*
|
||||||
|
* Thin wrapper around useOAuth for Discord-specific flows
|
||||||
|
* Handles Discord user profile fetching and username access
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* const discord = useDiscordOAuth();
|
||||||
|
* discord.login();
|
||||||
|
* // ... OAuth flow ...
|
||||||
|
* const username = discord.discordUsername;
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useOAuth } from './useOAuth.js';
|
||||||
|
|
||||||
|
// Shared Discord user profile data
|
||||||
|
const discordUser = ref(null);
|
||||||
|
|
||||||
|
export function useDiscordOAuth() {
|
||||||
|
const oauth = useOAuth('discord');
|
||||||
|
|
||||||
|
const hasDiscordAuth = computed(() => oauth.isAuthenticated.value);
|
||||||
|
|
||||||
|
const discordUsername = computed(() => {
|
||||||
|
return discordUser.value?.username || null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const discordId = computed(() => {
|
||||||
|
return discordUser.value?.id || null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const discordTag = computed(() => {
|
||||||
|
if (!discordUser.value) return null;
|
||||||
|
// Format: username#discriminator or just username (newer Discord)
|
||||||
|
return discordUser.value.discriminator
|
||||||
|
? `${discordUser.value.username}#${discordUser.value.discriminator}`
|
||||||
|
: discordUser.value.username;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch Discord user profile from backend
|
||||||
|
* Backend will use the stored Discord token to fetch from Discord API
|
||||||
|
*
|
||||||
|
* @returns {Promise<Object>} Discord user profile
|
||||||
|
* @throws {Error} If fetch fails
|
||||||
|
*/
|
||||||
|
async function fetchUserProfile() {
|
||||||
|
try {
|
||||||
|
const token = oauth.accessToken.value;
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('Not authenticated with Discord');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch from backend which has the Discord token
|
||||||
|
const response = await fetch('/api/auth/discord/profile', {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(error.error || 'Failed to fetch Discord profile');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
discordUser.value = data.user;
|
||||||
|
|
||||||
|
console.log(`✅ Loaded Discord profile: ${data.user.username}`);
|
||||||
|
return data.user;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to fetch Discord profile:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login with Discord
|
||||||
|
* Uses identify scope only for minimal permissions
|
||||||
|
*
|
||||||
|
* @param {Object} options - Optional options (return_to, etc.)
|
||||||
|
*/
|
||||||
|
function login(options = {}) {
|
||||||
|
oauth.login({
|
||||||
|
...options,
|
||||||
|
scope: 'identify'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout from Discord
|
||||||
|
*/
|
||||||
|
function logout() {
|
||||||
|
oauth.logout();
|
||||||
|
discordUser.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user is allowed to access developer tools
|
||||||
|
* Compares Discord username against backend-managed allowlist
|
||||||
|
*
|
||||||
|
* @param {Object} userPermissions - User permissions object from backend
|
||||||
|
* @returns {boolean} True if user has developer access
|
||||||
|
*/
|
||||||
|
function hasDevAccess(userPermissions = {}) {
|
||||||
|
// Check explicit permission
|
||||||
|
if (userPermissions?.includes?.('developer_tools.view')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend could also return discord_username_allowlist
|
||||||
|
// This would be checked server-side, but frontend can cache it
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// State
|
||||||
|
hasDiscordAuth,
|
||||||
|
discordUser: computed(() => discordUser.value),
|
||||||
|
discordUsername,
|
||||||
|
discordId,
|
||||||
|
discordTag,
|
||||||
|
isExpired: oauth.isExpired,
|
||||||
|
expiresIn: oauth.expiresIn,
|
||||||
|
loading: oauth.loading,
|
||||||
|
error: oauth.error,
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
exchangeCode: oauth.exchangeCode,
|
||||||
|
refreshToken: oauth.refreshToken,
|
||||||
|
getValidToken: oauth.getValidToken,
|
||||||
|
fetchUserProfile,
|
||||||
|
hasDevAccess
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user