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.
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.
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.
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
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')
);
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}`
}
});
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.
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,
});
}
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 });
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 });
}
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 });
}
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 });
}
Ensure all communications between the client and Cloudflare Workers are conducted over HTTPS to protect data in transit.
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
// ...
}
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'
},
});
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
}
Periodically rotate your Supabase Service Role Keys to minimize the risk of compromised credentials.
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
}
Deploy your Cloudflare Pages project, ensuring that all environment variables are correctly set and that your Workers are properly configured.
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"}'
Managing authentication on the server side offers numerous advantages:
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.