Supabase cho người mới: Auth, PostgreSQL, Storage và RLS trong một buổi chiều
Ở bài trước, chúng ta đã biết DBaaS là cơ sở dữ liệu được quản lý sẵn. Bài này lấy một cái tên cụ thể ra dùng thật: Supabase. Đây không phải bài “hello world” cho vui, mà là những phần bạn thật sự cần để một app có người dùng đăng nhập được, đọc ghi dữ liệu an toàn.
Supabase là gì?
Supabase là một nền tảng backend mã nguồn mở, thường được gọi là “giải pháp thay thế Firebase”. Điểm khác biệt lớn nhất: mọi thứ dựng trên PostgreSQL, một trong những cơ sở dữ liệu quan hệ được tin dùng nhất thế giới.
Nghĩa là bạn không bị khoá vào một công nghệ độc quyền. Bên dưới vẫn là SQL thuần, dữ liệu vẫn là các bảng có quan hệ rõ ràng, và nếu sau này muốn tự vận hành thì Supabase cho phép tự host được.
Với một người làm sản phẩm, Supabase gom ba việc nặng nhất vào một chỗ: đăng nhập, cơ sở dữ liệu, và lưu trữ tập tin.
Trụ cột 1: Auth – đăng nhập người dùng
Auth là phần lo việc đăng ký, đăng nhập và giữ danh tính người dùng. Supabase Auth dùng chuẩn JWT (JSON Web Token) để xác thực, và lưu thông tin người dùng trong một schema riêng ngay trong cơ sở dữ liệu của bạn.
Bạn có thể cho người dùng đăng nhập bằng email và mật khẩu, bằng đường link gửi qua email, hoặc bằng tài khoản Google, GitHub và nhiều nhà cung cấp khác. Điều hay là Auth không tách rời khỏi database, mà nối thẳng vào nó, để phần phân quyền phía dưới hoạt động.
Trụ cột 2: PostgreSQL – database thật
Mỗi project Supabase là một cơ sở dữ liệu PostgreSQL đầy đủ. Bạn tạo bảng, đặt khoá chính, dựng quan hệ giữa các bảng như một database bình thường.
Điểm cộng lớn: Supabase tự sinh sẵn REST API cho các bảng của bạn. Nghĩa là ngay khi tạo bảng xong, bạn đã có thể đọc ghi dữ liệu từ trình duyệt hay app mà chưa cần viết một dòng backend nào. Nhưng chính sự tiện lợi này dẫn thẳng tới phần quan trọng nhất của cả bài: RLS.
Trụ cột 3: Storage – lưu tập tin
Storage lo việc lưu ảnh, video, tài liệu, những thứ không nên nhét vào bảng dữ liệu. Bạn tổ chức tập tin theo các “bucket”, và điều quan trọng: theo mặc định, Storage không cho phép tải tập tin lên bucket nào nếu chưa có chính sách bảo mật. Cùng một cơ chế phân quyền với database, ta sẽ nói ngay dưới đây.
RLS – phần quan trọng nhất, đừng bỏ qua
RLS là viết tắt của Row Level Security, tức bảo mật ở mức từng dòng. Đây là một tính năng gốc của PostgreSQL, và là thứ quyết định dữ liệu của bạn có an toàn hay không.
Hãy hình dung thế này: mỗi lần có ai đó truy vấn một bảng, PostgreSQL tự động gắn thêm một điều kiện lọc dựa trên “người đang hỏi là ai”. Nếu dòng dữ liệu thoả điều kiện thì họ thấy, không thoả thì với họ dòng đó coi như không tồn tại. Bạn không phải viết đoạn kiểm tra quyền rải rác khắp nơi trong code, mà đặt luật ngay trong database.
Vì sao điều này sống còn với Supabase? Vì như đã nói, các bảng của bạn được mở ra ngoài qua API tự sinh. Nếu một bảng không bật RLS, thì bất kỳ ai có khoá công khai (anon key) của project đều có thể đọc, thậm chí ghi, mọi dòng trong bảng đó. Đúng vậy: chỉ một lời gọi API là dữ liệu ra ngoài.
Tin tốt là khi bạn bật RLS lên, bảng bị khoá lại theo kiểu “cấm hết trước đã”. Sau đó không ai truy cập được gì cho tới khi bạn viết chính sách (policy) cho phép cụ thể. An toàn theo mặc định, tuy lúc đầu hơi làm nhiều người mới bất ngờ vì “sao không đọc được gì cả”.
Một chính sách RLS đầu tiên: mỗi người chỉ thấy dữ liệu của mình
Giả sử bạn có bảng ghi chú và muốn mỗi người dùng chỉ đọc được ghi chú do chính họ tạo. Bước một là bật RLS cho bảng:
alter table notes enable row level security;
Bước hai là viết chính sách cho phép đọc, dùng hàm trợ giúp auth.uid() để lấy id của người đang đăng nhập:
create policy "Chi doc ghi chu cua minh"
on notes for select
using ( auth.uid() = user_id );
Đọc câu lệnh trên như tiếng Việt: “cho phép thao tác đọc trên bảng notes, với điều kiện id của người đang đăng nhập bằng đúng cột user_id của dòng đó”. Vậy là xong. Người A không bao giờ thấy được ghi chú của người B, và bạn không cần viết thêm một dòng kiểm tra nào ở tầng ứng dụng.
Một cảnh báo bảo mật phải nhớ
Supabase có hai loại khoá. Khoá công khai (anon key) dùng ở phía trình duyệt và luôn bị RLS ràng buộc. Nhưng còn một khoá đặc biệt, service role key, thì bỏ qua toàn bộ RLS. Khoá này chỉ được nằm ở phía máy chủ tin cậy của bạn, không bao giờ được lộ ra trình duyệt hay đưa vào kho code công khai. Hãy coi nó như mật khẩu quản trị cao nhất.
Định nghĩa xong
Bạn coi như nắm được bài này khi:
- Người dùng thật đăng ký và đăng nhập được vào app.
- App đọc và ghi dữ liệu vào PostgreSQL, và mỗi người chỉ thấy đúng dữ liệu của mình, nhờ RLS.
- Bạn giải thích được vì sao một bảng không bật RLS là một lỗ hổng, và vì sao service role key không bao giờ được ra tới trình duyệt.
Đến đây, app của chúng ta đã có một backend chạy được. Bài tiếp theo sẽ đóng gói phần app này lại bằng Docker, để nó chạy được ở bất cứ đâu chứ không chỉ trên máy của bạn.