This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
105 lines
2.5 KiB
TypeScript
105 lines
2.5 KiB
TypeScript
/**
|
|
* OAuth Hooks
|
|
* React Query hooks for OAuth authentication flows
|
|
*/
|
|
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import {
|
|
getOAuthProviders,
|
|
getOAuthConnections,
|
|
initiateOAuth,
|
|
handleOAuthCallback,
|
|
disconnectOAuth,
|
|
OAuthProvider,
|
|
OAuthConnection,
|
|
OAuthTokenResponse,
|
|
} from '../api/oauth';
|
|
import { setCookie } from '../utils/cookies';
|
|
|
|
/**
|
|
* Hook to get list of enabled OAuth providers
|
|
*/
|
|
export const useOAuthProviders = () => {
|
|
return useQuery<OAuthProvider[], Error>({
|
|
queryKey: ['oauthProviders'],
|
|
queryFn: getOAuthProviders,
|
|
staleTime: 10 * 60 * 1000, // 10 minutes
|
|
refetchOnWindowFocus: false,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to get user's connected OAuth accounts
|
|
*/
|
|
export const useOAuthConnections = () => {
|
|
return useQuery<OAuthConnection[], Error>({
|
|
queryKey: ['oauthConnections'],
|
|
queryFn: getOAuthConnections,
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
refetchOnWindowFocus: false,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to initiate OAuth flow
|
|
*/
|
|
export const useInitiateOAuth = () => {
|
|
return useMutation({
|
|
mutationFn: async (provider: string) => {
|
|
const response = await initiateOAuth(provider);
|
|
return { provider, authorizationUrl: response.authorization_url };
|
|
},
|
|
onSuccess: ({ authorizationUrl }) => {
|
|
// Open OAuth authorization URL in current window
|
|
window.location.href = authorizationUrl;
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to handle OAuth callback and exchange code for tokens
|
|
*/
|
|
export const useOAuthCallback = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
provider,
|
|
code,
|
|
state,
|
|
}: {
|
|
provider: string;
|
|
code: string;
|
|
state: string;
|
|
}) => {
|
|
return handleOAuthCallback(provider, code, state);
|
|
},
|
|
onSuccess: (data: OAuthTokenResponse) => {
|
|
// Store tokens in cookies
|
|
setCookie('access_token', data.access, 7);
|
|
setCookie('refresh_token', data.refresh, 7);
|
|
|
|
// Set user in cache
|
|
queryClient.setQueryData(['currentUser'], data.user);
|
|
|
|
// Invalidate OAuth connections to refetch
|
|
queryClient.invalidateQueries({ queryKey: ['oauthConnections'] });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to disconnect OAuth account
|
|
*/
|
|
export const useDisconnectOAuth = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: disconnectOAuth,
|
|
onSuccess: () => {
|
|
// Invalidate connections list to refetch
|
|
queryClient.invalidateQueries({ queryKey: ['oauthConnections'] });
|
|
},
|
|
});
|
|
};
|