Anonymous Sign-Ins
Create and use anonymous users to authenticate with Supabase
Enable Anonymous Sign-Ins to build apps which provide users an authenticated experience without requiring users to enter an email address, password, use an OAuth provider or provide any other PII (Personally Identifiable Information). Later, when ready, the user can link an authentication method to their account.
Anonymous user vs the anon key
Calling signInAnonymously()
creates an anonymous user. It's just like a permanent user, except the user can't access their account if they sign out, clear browsing data, or use another device.
Like permanent users, the authenticated
Postgres role will be used when using the Data APIs to access your project. JWTs for these users will have an is_anonymous
claim which you can use to distinguish in RLS policies.
This is different from the anon
API key which does not create a user and can be used to implement public access to your database as it uses the anonymous
Postgres role.
Anonymous sign-ins can be used to build:
- E-commerce applications, such as shopping carts before check-out
- Full-feature demos without collecting personal information
- Temporary or throw-away accounts
Review your existing RLS policies before enabling anonymous sign-ins. Anonymous users use the authenticated
role. To distinguish between anonymous users and permanent users, your policies need to check the is_anonymous
field of the user's JWT.
See the Access control section for more details.
Self hosting and local development
For self-hosting, you can update your project configuration using the files and environment variables provided. See the local development docs for more details.
Sign in anonymously
Convert an anonymous user to a permanent user
Converting an anonymous user to a permanent user requires linking an identity to the user. This requires you to enable manual linking in your Supabase project.
Link an Email / Phone identity
Link an OAuth identity
Access control
An anonymous user assumes the authenticated
role just like a permanent user. You can use row-level security (RLS) policies to differentiate between an anonymous user and a permanent user by checking for the is_anonymous
claim in the JWT returned by auth.jwt()
:
_10create policy "Only permanent users can post to the news feed"_10on news_feed as restrictive for insert_10to authenticated_10with check ((select (auth.jwt()->>'is_anonymous')::boolean) is false );_10_10create policy "Anonymous and permanent users can view the news feed"_10on news_feed for select_10to authenticated_10using ( true );
Use restrictive policies
RLS policies are permissive by default, which means that they are combined using an "OR" operator when multiple policies are applied. It is important to construct restrictive policies to ensure that the checks for an anonymous user are always enforced when combined with other policies.
Resolving identity conflicts
Depending on your application requirements, data conflicts can arise when an anonymous user is converted to a permanent user. For example, in the context of an e-commerce application, an anonymous user would be allowed to add items to the shopping cart without signing up / signing in. When they decide to sign-in to an existing account, you will need to decide how you want to resolve data conflicts in the shopping cart:
- Overwrite the items in the cart with those in the existing account
- Overwrite the items in the cart with those from the anonymous user
- Merge the items in the cart together
Abuse prevention and rate limits
Since anonymous users are stored in your database, bad actors can abuse the endpoint to increase your database size drastically. It is strongly recommended to enable invisible Captcha or Cloudflare Turnstile to prevent abuse for anonymous sign-ins. An IP-based rate limit is enforced at 30 requests per hour which can be modified in your dashboard. You can refer to the full list of rate limits here.
Automatic cleanup
Automatic cleanup of anonymous users is currently not available. Instead, you can delete anonymous users from your project by running the following SQL:
_10-- deletes anonymous users created more than 30 days ago_10delete from auth.users_10where is_anonymous is true and created_at < now() - interval '30 days';