Documentation Index Fetch the complete documentation index at: https://mintlify.com/Jesus-Puertos/h-ayuntamiento/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Ayuntamiento de Zongolica platform uses Supabase Auth for secure user authentication and session management. The system supports multiple authentication methods to accommodate different user preferences.
OAuth Providers Google and Facebook social login for quick access
Email/Password Traditional email and password authentication
Session Management Secure JWT-based sessions with automatic refresh
Row Level Security Database-level security for user data isolation
Supabase Auth Integration
The authentication system is built on top of Supabase Auth, providing enterprise-grade security out of the box.
Client Configuration
Location: src/lib/supabase.ts
import { createClient } from '@supabase/supabase-js' ;
// Environment variables
const supabaseUrl = import . meta . env . PUBLIC_SUPABASE_URL || '' ;
const supabaseAnonKey = import . meta . env . PUBLIC_SUPABASE_ANON_KEY || '' ;
export const supabase = createClient ( supabaseUrl , supabaseAnonKey );
Never expose your Supabase service role key in client-side code. Always use the anon/public key.
Environment Variables
Configure in .env:
PUBLIC_SUPABASE_URL = https://xxxxxxxxxxxxx.supabase.co
PUBLIC_SUPABASE_ANON_KEY = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
These are prefixed with PUBLIC_ to make them available in the browser.
OAuth Providers
Google OAuth
The platform supports “Sign in with Google” for quick authentication.
Setup Process
Create Google Cloud Project
Create OAuth Credentials
Navigate to Credentials in the left sidebar
Click Create Credentials > OAuth 2.0 Client ID
Choose Web application
Add authorized redirect URI:
https://your-project.supabase.co/auth/v1/callback
Copy the Client ID and Client Secret
Configure in Supabase
Go to your Supabase project dashboard
Navigate to Authentication > Providers
Find Google and toggle it on
Paste your Client ID and Client Secret
Click Save
Implementation
export async function signInWithGoogle ( redirectPath = '/turismo?onboarding=1' ) {
const { data , error } = await supabase . auth . signInWithOAuth ({
provider: 'google' ,
options: {
redirectTo: ` ${ window . location . origin } /auth/callback?return_url= ${ encodeURIComponent ( redirectPath ) } `
}
});
return { data , error };
}
Usage in components:
import { signInWithGoogle } from '@/lib/supabase' ;
async function handleGoogleLogin () {
const { error } = await signInWithGoogle ( '/turismo' );
if ( error ) {
console . error ( 'Login failed:' , error . message );
}
}
Facebook OAuth
Similar setup process for Facebook authentication.
Setup Process
Add Facebook Login Product
In your app dashboard, click Add Product
Find Facebook Login and click Set Up
Choose Web platform
Add valid OAuth redirect URI:
https://your-project.supabase.co/auth/v1/callback
Configure in Supabase
Go to Settings > Basic in Facebook app
Copy App ID and App Secret
In Supabase: Authentication > Providers > Facebook
Enable Facebook and paste credentials
Click Save
Email/Password Authentication
Sign Up
export async function signUpWithEmail (
email : string ,
password : string ,
name ?: string
) {
const { data , error } = await supabase . auth . signUp ({
email ,
password ,
options: {
data: {
name: name || email . split ( '@' )[ 0 ]
}
}
});
return { data , error };
}
Sign In
export async function signInWithEmail ( email : string , password : string ) {
const { data , error } = await supabase . auth . signInWithPassword ({
email ,
password
});
return { data , error };
}
Usage Example
import { signInWithEmail , signUpWithEmail } from '@/lib/supabase' ;
import { useState } from 'react' ;
function LoginForm () {
const [ email , setEmail ] = useState ( '' );
const [ password , setPassword ] = useState ( '' );
const [ isSignUp , setIsSignUp ] = useState ( false );
async function handleSubmit ( e : React . FormEvent ) {
e . preventDefault ();
const authFn = isSignUp ? signUpWithEmail : signInWithEmail ;
const { error } = await authFn ( email , password );
if ( error ) {
alert ( error . message );
} else {
window . location . href = '/turismo' ;
}
}
return (
< form onSubmit = { handleSubmit } >
< input
type = "email"
value = { email }
onChange = { e => setEmail ( e . target . value )}
placeholder = "Email"
required
/>
< input
type = "password"
value = { password }
onChange = { e => setPassword ( e . target . value )}
placeholder = "Password"
required
/>
< button type = "submit" >
{ isSignUp ? 'Sign Up' : 'Sign In' }
</ button >
< button type = "button" onClick = {() => setIsSignUp (! isSignUp )} >
{ isSignUp ? 'Already have an account?' : 'Need an account?' }
</ button >
</ form >
);
}
OAuth Callback Handler
Location: src/pages/auth/callback.astro
Handles the OAuth redirect after successful authentication:
src/pages/auth/callback.astro
---
export const prerender = false ;
const { searchParams } = Astro . url ;
const returnUrl = searchParams . get ( 'return_url' ) || '/turismo' ;
// Supabase automatically handles the token exchange
// Just redirect to the intended destination
---
< script define:vars = { { returnUrl } } >
// Redirect after a brief moment to ensure session is set
setTimeout (() => {
window . location . href = returnUrl ;
}, 100 );
</ script >
< div class = "flex items-center justify-center min-h-screen" >
< div class = "text-center" >
< div class = "animate-spin rounded-full h-12 w-12 border-b-2 border-[#ff8200] mx-auto" ></ div >
< p class = "mt-4 text-gray-600" > Signing you in... </ p >
</ div >
</ div >
Session Management
Getting Current User
export async function getCurrentUser () {
const { data : { user } } = await supabase . auth . getUser ();
return user ;
}
Checking Authentication Status
In Astro pages:
---
import { supabase } from '@/lib/supabase' ;
const { data : { session } } = await supabase . auth . getSession ();
const user = session ?. user ;
if ( ! user ) {
return Astro . redirect ( '/auth/login' );
}
---
In React components:
import { useEffect , useState } from 'react' ;
import { supabase } from '@/lib/supabase' ;
function useAuth () {
const [ user , setUser ] = useState ( null );
const [ loading , setLoading ] = useState ( true );
useEffect (() => {
// Get initial session
supabase . auth . getSession (). then (({ data : { session } }) => {
setUser ( session ?. user ?? null );
setLoading ( false );
});
// Listen for auth changes
const { data : { subscription } } = supabase . auth . onAuthStateChange (
( _event , session ) => {
setUser ( session ?. user ?? null );
}
);
return () => subscription . unsubscribe ();
}, []);
return { user , loading };
}
Sign Out
export async function signOut () {
const { error } = await supabase . auth . signOut ();
return { error };
}
Usage:
import { signOut } from '@/lib/supabase' ;
async function handleSignOut () {
await signOut ();
window . location . href = '/' ;
}
User Profiles
Location: src/lib/supabase.ts
Profile Schema
export interface UserProfile {
id : string ; // Matches auth.users.id
email : string ;
full_name : string ;
avatar_url : string ;
provider : string ; // 'google', 'facebook', 'email'
onboarding_completed : boolean ;
created_at : string ;
updated_at : string ;
}
Database Table
CREATE TABLE user_profiles (
id UUID REFERENCES auth . users (id) PRIMARY KEY ,
email TEXT ,
full_name TEXT ,
avatar_url TEXT ,
provider TEXT ,
onboarding_completed BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW (),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW ()
);
Profile Management Functions
export async function getUserProfile ( userId : string ) {
const { data , error } = await supabase
. from ( 'user_profiles' )
. select ( '*' )
. eq ( 'id' , userId )
. single ();
return { data: data as UserProfile | null , error };
}
export async function upsertUserProfile (
profile : Partial < UserProfile > & { id : string }
) {
const { data , error } = await supabase
. from ( 'user_profiles' )
. upsert ([{ ... profile , updated_at: new Date (). toISOString () }])
. select ()
. single ();
return { data , error };
}
export async function markOnboardingCompleted ( userId : string ) {
const { data , error } = await supabase
. from ( 'user_profiles' )
. update ({
onboarding_completed: true ,
updated_at: new Date (). toISOString ()
})
. eq ( 'id' , userId )
. select ()
. single ();
return { data , error };
}
Protected Routes
Server-Side Protection
Protect pages at the server level:
src/pages/turismo/perfil.astro
---
import { supabase } from '@/lib/supabase' ;
import Layout from '@/layouts/Layout.astro' ;
export const prerender = false ;
// Check authentication
const { data : { session } } = await supabase . auth . getSession ();
if ( ! session ) {
return Astro . redirect ( '/auth/login?redirect=/turismo/perfil' );
}
const user = session . user ;
---
< Layout title = "Mi Perfil" >
< h1 > Welcome, { user . email } </ h1 >
<!-- Profile content -->
</ Layout >
Client-Side Protection
For React components:
import { useEffect } from 'react' ;
import { supabase } from '@/lib/supabase' ;
function ProtectedComponent () {
useEffect (() => {
supabase . auth . getSession (). then (({ data : { session } }) => {
if ( ! session ) {
window . location . href = '/auth/login' ;
}
});
}, []);
return < div > Protected content </ div > ;
}
Row Level Security (RLS)
Supabase RLS ensures users can only access their own data.
User Profiles Policies
-- Users can view their own profile
CREATE POLICY "Users can view own profile" ON user_profiles
FOR SELECT USING ( auth . uid () = id);
-- Users can update their own profile
CREATE POLICY "Users can update own profile" ON user_profiles
FOR UPDATE USING ( auth . uid () = id);
-- Users can insert their own profile
CREATE POLICY "Users can insert own profile" ON user_profiles
FOR INSERT WITH CHECK ( auth . uid () = id);
User Preferences Policies
-- Users can view their own preferences
CREATE POLICY "Users can view own preferences" ON user_preferences
FOR SELECT USING ( auth . uid () = user_id);
-- Users can insert their own preferences
CREATE POLICY "Users can insert own preferences" ON user_preferences
FOR INSERT WITH CHECK ( auth . uid () = user_id);
-- Users can update their own preferences
CREATE POLICY "Users can update own preferences" ON user_preferences
FOR UPDATE USING ( auth . uid () = user_id);
User Routes Policies
-- Anyone can view routes (for sharing)
CREATE POLICY "Anyone can view routes" ON user_routes
FOR SELECT USING (true);
-- Users can insert their own routes
CREATE POLICY "Users can insert own routes" ON user_routes
FOR INSERT WITH CHECK ( auth . uid () = user_id);
-- Users can update their own routes
CREATE POLICY "Users can update own routes" ON user_routes
FOR UPDATE USING ( auth . uid () = user_id);
Email Configuration
Enable Email Provider
Enable in Supabase
Go to Authentication > Providers
Find Email and toggle it on
Configure settings:
Confirm email : Enable for production, disable for development
Secure email change : Recommended
Double confirm email : Optional
Customize Email Templates (Optional)
Go to Authentication > Email Templates
Customize:
Confirmation email
Magic link email
Password reset email
Add your branding and custom copy
Email Verification Flow
When email confirmation is enabled:
User signs up
Supabase sends confirmation email
User clicks link in email
User is redirected to site with confirmed account
Profile is created and onboarding begins
Security Best Practices
Use HTTPS Only Always enforce SSL/TLS encryption in production
Validate Redirects Sanitize redirect URLs to prevent open redirect vulnerabilities
Rate Limiting Enable Supabase rate limiting to prevent abuse
Strong Passwords Enforce minimum password requirements
Redirect Validation
function sanitizeRedirectUrl ( url : string ) : string {
const allowedPaths = [ '/turismo' , '/perfil' , '/mi-ruta' ];
const parsed = new URL ( url , window . location . origin );
// Only allow same-origin redirects
if ( parsed . origin !== window . location . origin ) {
return '/turismo' ;
}
// Optionally restrict to specific paths
const path = parsed . pathname ;
if ( ! allowedPaths . some ( p => path . startsWith ( p ))) {
return '/turismo' ;
}
return url ;
}
Troubleshooting
OAuth redirect not working
Symptoms: User is not redirected after OAuth loginSolutions:
Verify redirect URI matches exactly in Google/Facebook console
Check that callback page exists at /auth/callback
Ensure return_url parameter is properly encoded
Check browser console for errors
Symptoms: User is logged out on page refreshSolutions:
Check that Supabase client is properly initialized
Verify cookies are enabled in browser
Ensure localStorage is available
Check for CORS issues in browser console
RLS policies blocking queries
Symptoms: Queries return empty results or permission errorsSolutions:
Verify RLS policies are correctly configured
Check that auth.uid() is available in policies
Test policies in Supabase SQL editor
Ensure user is properly authenticated before queries
Email confirmation not arriving
Symptoms: Users don’t receive confirmation emailsSolutions:
Check spam/junk folder
Verify email provider in Supabase (default is limited)
Configure custom SMTP in Supabase settings
Disable email confirmation for development
Next Steps
Tourism System Learn about the onboarding flow and user features
Government Portal Explore public-facing government pages