mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
feat: Integrate auth.sessions with RPC functions
This commit is contained in:
@@ -23,20 +23,15 @@ import type { UserIdentity, OAuthProvider } from '@/types/identity';
|
||||
import { toast as sonnerToast } from '@/components/ui/sonner';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
|
||||
interface DeviceInfo {
|
||||
browser?: string;
|
||||
userAgent?: string;
|
||||
os?: string;
|
||||
device?: string;
|
||||
}
|
||||
|
||||
interface UserSession {
|
||||
interface AuthSession {
|
||||
id: string;
|
||||
device_info: DeviceInfo | null;
|
||||
last_activity: string;
|
||||
created_at: string;
|
||||
expires_at: string;
|
||||
session_token: string;
|
||||
updated_at: string;
|
||||
refreshed_at: string | null;
|
||||
user_agent: string | null;
|
||||
ip: unknown;
|
||||
not_after: string | null;
|
||||
aal: 'aal1' | 'aal2' | 'aal3' | null;
|
||||
}
|
||||
|
||||
export function SecurityTab() {
|
||||
@@ -49,7 +44,7 @@ export function SecurityTab() {
|
||||
const [disconnectingProvider, setDisconnectingProvider] = useState<OAuthProvider | null>(null);
|
||||
const [hasPassword, setHasPassword] = useState(false);
|
||||
const [addingPassword, setAddingPassword] = useState(false);
|
||||
const [sessions, setSessions] = useState<UserSession[]>([]);
|
||||
const [sessions, setSessions] = useState<AuthSession[]>([]);
|
||||
const [loadingSessions, setLoadingSessions] = useState(true);
|
||||
|
||||
// Load user identities on mount
|
||||
@@ -183,33 +178,23 @@ export function SecurityTab() {
|
||||
const fetchSessions = async () => {
|
||||
if (!user) return;
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('user_sessions')
|
||||
.select('*')
|
||||
.eq('user_id', user.id)
|
||||
.order('last_activity', { ascending: false });
|
||||
const { data, error } = await supabase.rpc('get_my_sessions');
|
||||
|
||||
if (error) {
|
||||
console.error('Error fetching sessions:', error);
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Failed to load sessions',
|
||||
variant: 'destructive'
|
||||
});
|
||||
} else {
|
||||
const typedSessions: UserSession[] = (data || []).map(session => ({
|
||||
id: session.id,
|
||||
device_info: session.device_info as DeviceInfo | null,
|
||||
last_activity: session.last_activity,
|
||||
created_at: session.created_at,
|
||||
expires_at: session.expires_at,
|
||||
session_token: session.session_token
|
||||
}));
|
||||
setSessions(typedSessions);
|
||||
setSessions(data || []);
|
||||
}
|
||||
setLoadingSessions(false);
|
||||
};
|
||||
|
||||
const revokeSession = async (sessionId: string) => {
|
||||
const { error } = await supabase
|
||||
.from('user_sessions')
|
||||
.delete()
|
||||
.eq('id', sessionId);
|
||||
const { error } = await supabase.rpc('revoke_my_session', { session_id: sessionId });
|
||||
|
||||
if (error) {
|
||||
toast({
|
||||
@@ -226,13 +211,30 @@ export function SecurityTab() {
|
||||
}
|
||||
};
|
||||
|
||||
const getDeviceIcon = (deviceInfo: DeviceInfo | null) => {
|
||||
const ua = deviceInfo?.userAgent?.toLowerCase() || '';
|
||||
if (ua.includes('mobile')) return <Smartphone className="w-4 h-4" />;
|
||||
if (ua.includes('tablet')) return <Tablet className="w-4 h-4" />;
|
||||
const getDeviceIcon = (userAgent: string | null) => {
|
||||
if (!userAgent) return <Monitor className="w-4 h-4" />;
|
||||
|
||||
const ua = userAgent.toLowerCase();
|
||||
if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) {
|
||||
return <Smartphone className="w-4 h-4" />;
|
||||
}
|
||||
if (ua.includes('tablet') || ua.includes('ipad')) {
|
||||
return <Tablet className="w-4 h-4" />;
|
||||
}
|
||||
return <Monitor className="w-4 h-4" />;
|
||||
};
|
||||
|
||||
const getBrowserName = (userAgent: string | null) => {
|
||||
if (!userAgent) return 'Unknown Browser';
|
||||
|
||||
const ua = userAgent.toLowerCase();
|
||||
if (ua.includes('firefox')) return 'Firefox';
|
||||
if (ua.includes('chrome') && !ua.includes('edg')) return 'Chrome';
|
||||
if (ua.includes('safari') && !ua.includes('chrome')) return 'Safari';
|
||||
if (ua.includes('edg')) return 'Edge';
|
||||
return 'Unknown Browser';
|
||||
};
|
||||
|
||||
// Get connected accounts with identity data
|
||||
const connectedAccounts = [
|
||||
{
|
||||
@@ -415,17 +417,21 @@ export function SecurityTab() {
|
||||
{sessions.map((session) => (
|
||||
<div key={session.id} className="flex items-start justify-between p-3 border rounded-lg">
|
||||
<div className="flex gap-3">
|
||||
{getDeviceIcon(session.device_info)}
|
||||
{getDeviceIcon(session.user_agent)}
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
{session.device_info?.browser || 'Unknown Browser'}
|
||||
{getBrowserName(session.user_agent)}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Last active: {format(new Date(session.last_activity), 'PPpp')}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Expires: {format(new Date(session.expires_at), 'PPpp')}
|
||||
{session.ip && `${session.ip} • `}
|
||||
Last active: {format(new Date(session.refreshed_at || session.created_at), 'PPpp')}
|
||||
{session.aal === 'aal2' && ' • MFA'}
|
||||
</p>
|
||||
{session.not_after && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Expires: {format(new Date(session.not_after), 'PPpp')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user