Migrate User from Auth.js to Supabae
Migrate User from Auth.js to Supabae
Creating a Secure User Creation Function in PostgreSQL
In this article, we’ll explore how to create a secure user creation function in PostgreSQL that interacts with Supabase’s auth schema. We’ll discuss the implementation, usage, and important security considerations.
The Function
Here’s our create_user_with_metadata
function, modified to be a security definer:
CREATE OR REPLACE FUNCTION public.create_user_with_metadata(
email text,
password text,
user_meta_data jsonb
)
RETURNS uuid
SECURITY DEFINER
SET search_path = public
LANGUAGE plpgsql
AS $$
DECLARE
user_id uuid;
encrypted_pw text;
app_metadata jsonb;
confirmation timestamp;
firstname text := user_meta_data->>'firstName';
lastname text := user_meta_data->>'lastName';
BEGIN
-- set variables
user_id := gen_random_uuid();
app_metadata := '{
"provider": "email",
"providers": [
"email"
]
}'::jsonb;
IF TRUE THEN -- Replace with actual logic if needed
confirmation := now();
ELSE
confirmation := null;
END IF;
-- Insert into auth.users
INSERT INTO auth.users
(instance_id, id, aud, role, email, encrypted_password, email_confirmed_at, recovery_sent_at, last_sign_in_at,
confirmation_sent_at, raw_app_meta_data, raw_user_meta_data, created_at, updated_at, confirmation_token,
email_change, email_change_token_new, recovery_token)
VALUES
('00000000-0000-0000-0000-000000000000', user_id, 'authenticated', 'authenticated', email, password,
confirmation, confirmation, confirmation, confirmation, app_metadata, user_meta_data, now(), now(), '', '', '', '');
-- Insert into auth.identities
INSERT INTO auth.identities
(provider_id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at)
VALUES
(gen_random_uuid(), user_id, format('{"sub":"%s","email":"%s"}', user_id::text, email)::jsonb, 'email',
now(), now(), now());
-- Insert into public.profiles
-- INSERT INTO public.profiles (id, "firstName", "lastName")
-- VALUES (user_id, firstname, lastname);
RETURN user_id;
END;
$$;
Usage Example
Here’s how you can use the function:
SELECT * FROM public.create_user_with_metadata(
'[email protected]',
'$2a$10$k5mT4h7brMq0rW59cYrhPOYaPh/VFhtZoJ1coHz.nWEtZ39HSDwb2',
'{"firstName": "Test", "lastName": "User", "phone": "123456719", "email": "[email protected]"}'::jsonb
);
This will create a new user with the provided email, password (which should be pre-hashed), and metadata.
Security Considerations
-
SECURITY DEFINER: The
SECURITY DEFINER
clause means the function will execute with the privileges of the user who created it, not the user who calls it. This is necessary to allow the function to insert into theauth.users
andauth.identities
tables. -
SET search_path: We’ve added
SET search_path = public
to ensure the function uses the intended schema, preventing potential hijacking. -
Access Control: Ensure that only trusted roles have EXECUTE privileges on this function. You can restrict access like this:
REVOKE ALL ON FUNCTION public.create_user_with_metadata(text, text, jsonb) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.create_user_with_metadata(text, text, jsonb) TO authenticated_role;
-
Input Validation: The function currently doesn’t validate inputs. Consider adding checks for email format, password strength, and metadata structure.
-
Password Hashing: The function assumes the password is pre-hashed. Ensure your application properly hashes passwords before calling this function.
-
Audit Logging: Consider adding audit logging to track when and by whom this function is called.
-
Deactivation: If you need to temporarily disable user creation, you can revoke execute privileges from all roles:
REVOKE ALL ON FUNCTION public.create_user_with_metadata(text, text, jsonb) FROM PUBLIC;
Remember, exposing database functions, especially those with elevated privileges, always carries some risk. Regularly review your security settings and consider using application-level user management if possible.
By following these guidelines, you can create a more secure user creation process in your PostgreSQL database. Always stay vigilant and keep your security measures up to date!