Invoking Supabase Auth in Cloudflare Page Functions
Securely Manage Authentication Without Exposing Supabase APIs to Clients
Key Takeaways
- Enhanced Security: Server-side handling of authentication protects sensitive API keys.
- Efficient Session Management: Utilize HTTP-only cookies and session tokens for secure user sessions.
- Scalable Integration: Cloudflare Workers provide a scalable environment for seamless Supabase interactions.
Introduction
Integrating Supabase Authentication within Cloudflare Page Functions allows developers to manage user authentication server-side, enhancing security by preventing direct exposure of Supabase APIs to clients. This comprehensive guide outlines the steps to achieve this integration, ensuring a secure and efficient authentication flow.
Setting Up Supabase with Cloudflare Workers
1. Initialize Supabase Project
Begin by setting up a Supabase project. Obtain your Supabase URL and Service Role Key, which are essential for secure server-side operations. Ensure these credentials are kept confidential and never exposed to the client.
2. Configure Cloudflare Pages with Workers
Create a Cloudflare Pages project if you haven't already. Within this project, you can define server-side functions using Cloudflare Workers. These Workers will act as intermediaries between your client application and Supabase, handling all authentication-related requests securely.
Securely Storing Supabase Keys
1. Use Environment Variables
Store your Supabase credentials as environment variables within the Cloudflare Pages dashboard. This practice ensures that sensitive information remains secure and is not hard-coded into your application.
# Example of setting environment variables
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
2. Accessing Environment Variables in Code
In your Cloudflare Worker functions, access these environment variables to initialize the Supabase client securely.
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
Deno.env.get('SUPABASE_URL'),
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
);
Passing User Session Token
1. Client-Side Token Retrieval
After authenticating the user on the client side, retrieve the user's session token. This token will be used to authenticate requests to your Cloudflare Worker.
// Client-side code to get the session token
const { data: { session } } = await supabase.auth.getSession();
const accessToken = session.access_token;
// Make a request to the Cloudflare Worker with the token
fetch('/api/auth-function', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
2. Sending Token in Request Headers
Include the retrieved session token in the request headers when making API calls to your Cloudflare Worker. This ensures that each request is authenticated and authorized.
Initializing Supabase Client with Auth Context
1. Retrieve Authorization Header
Within your Cloudflare Worker, extract the `Authorization` header from incoming requests to obtain the user's session token.
// Cloudflare Worker code to extract the token
export async function onRequest(context) {
const authHeader = context.request.headers.get('Authorization');
if (!authHeader) {
return new Response('Unauthorized', { status: 401 });
}
const token = authHeader.replace('Bearer ', '');
// Initialize Supabase client with the token
const supabase = createClient(
Deno.env.get('SUPABASE_URL'),
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY'),
{
global: {
headers: { Authorization: authHeader },
},
}
);
// Verify the user
const { data: { user }, error } = await supabase.auth.getUser(token);
if (error || !user) {
return new Response('Unauthorized', { status: 401 });
}
// Proceed with authenticated operations
return new Response(JSON.stringify({ message: 'Authenticated!', user }), {
headers: { 'Content-Type': 'application/json' },
status: 200,
});
}
2. Using Auth Context for Database Operations
With the Supabase client initialized using the user's session token, you can perform database operations that respect the user's permissions and roles, enforced by Row Level Security (RLS) policies.
// Example of fetching user-specific data
const { data: userData, error } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id);
if (error) {
return new Response(JSON.stringify({ error: error.message }), { status: 500 });
}
return new Response(JSON.stringify(userData), { status: 200 });
Handling Authentication Requests
1. Sign-Up Functionality
Create a Cloudflare Worker function to handle user registrations securely. This function will interact with Supabase to create new user accounts.
// Cloudflare Worker sign-up handler
export async function onRequestPost(context) {
const { email, password } = await context.request.json();
const { data, error } = await supabase.auth.signUp({
email,
password,
});
if (error) {
return new Response(JSON.stringify({ error: error.message }), { status: 400 });
}
return new Response(JSON.stringify({ message: 'User created successfully', data }), { status: 200 });
}
2. Sign-In Functionality
Similarly, implement a sign-in function that authenticates users and returns session tokens.
// Cloudflare Worker sign-in handler
export async function onRequestPost(context) {
const { email, password } = await context.request.json();
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
return new Response(JSON.stringify({ error: error.message }), { status: 400 });
}
// Optionally set HTTP-only cookies here for session management
return new Response(JSON.stringify({ message: 'Login successful', data }), { status: 200 });
}
3. Token Verification
Implement a function to verify the authenticity of session tokens sent from the client.
// Cloudflare Worker token verification
export async function onRequestPost(context) {
const { token } = await context.request.json();
const { data, error } = await supabase.auth.getUser(token);
if (error || !data.user) {
return new Response(JSON.stringify({ error: 'Invalid token' }), { status: 401 });
}
return new Response(JSON.stringify({ message: 'Token is valid', user: data.user }), { status: 200 });
}
Secure Communication Between Client and Worker
1. Use HTTPS Protocol
Ensure all communications between the client and Cloudflare Workers are conducted over HTTPS to protect data in transit.
2. Implement Rate Limiting and Validation
Protect your Workers from abuse by implementing rate limiting and validating all incoming requests. This can prevent malicious activities such as brute-force attacks.
// Example of rate limiting in a Cloudflare Worker
const RATE_LIMIT = 100; // requests
const WINDOW_SIZE = 60 * 1000; // 1 minute
let requestCounts = {};
export async function onRequest(context) {
const ip = context.request.headers.get('CF-Connecting-IP');
const now = Date.now();
if (!requestCounts[ip]) {
requestCounts[ip] = [];
}
// Remove outdated requests
requestCounts[ip] = requestCounts[ip].filter(timestamp => now - timestamp < WINDOW_SIZE);
if (requestCounts[ip].length >= RATE_LIMIT) {
return new Response('Too Many Requests', { status: 429 });
}
requestCounts[ip].push(now);
// Proceed with handling the request
// ...
}
Managing Sessions and Tokens
1. Use HTTP-Only Cookies
Store session tokens in HTTP-only cookies to prevent client-side scripts from accessing them, mitigating the risk of XSS attacks.
// Setting an HTTP-only cookie in Cloudflare Worker
return new Response('Login successful', {
status: 200,
headers: {
'Set-Cookie': `access_token=${token}; HttpOnly; Secure; Path=/; Max-Age=3600`,
'Content-Type': 'application/json'
},
});
2. Session Expiry and Refresh
Implement session expiry and token refresh mechanisms to ensure that user sessions remain secure over time.
// Example of session expiry check
if (Date.now() > sessionExpiry) {
return new Response('Session expired', { status: 401 });
}
// Function to refresh token
async function refreshToken(oldToken) {
// Logic to refresh the token using Supabase's API
}
Additional Security Measures
1. Regularly Rotate Supabase Keys
Periodically rotate your Supabase Service Role Keys to minimize the risk of compromised credentials.
2. Implement Logging and Monitoring
Use Cloudflare's logging and monitoring tools to keep track of authentication requests and detect any suspicious activities.
// Example of logging in Cloudflare Worker
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
console.log(`Received request from ${request.headers.get('CF-Connecting-IP')}`);
// Handle the request
}
Testing and Deployment
1. Deploy to Cloudflare Pages
Deploy your Cloudflare Pages project, ensuring that all environment variables are correctly set and that your Workers are properly configured.
2. Test Authentication Flow
Thoroughly test the authentication flow by signing up, signing in, and accessing protected routes to ensure that everything functions as expected.
# Example of testing with cURL
curl -X POST https://your-cloudflare-worker.com/api/auth-function \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com", "password":"securepassword"}'
Benefits of Server-Side Authentication
Managing authentication on the server side offers numerous advantages:
-
Enhanced Security: Sensitive API keys are never exposed to the client, reducing the risk of unauthorized access.
-
Customizable Logic: Server-side functions allow for the integration of custom authentication workflows, logging, and additional security checks.
-
Scalability: Cloudflare Workers provide a scalable environment capable of handling high volumes of authentication requests with low latency.
Conclusion
Integrating Supabase Authentication with Cloudflare Page Functions offers a robust and secure method for managing user authentication without exposing Supabase APIs to the client. By following the steps outlined in this guide, developers can ensure that their applications maintain high security standards while providing seamless authentication experiences for users.
References
Last updated January 14, 2025