Mazeway
Clerk but you own the code because authentication should live in your project, not a node_modules folder.
Install / Use
/learn @mazeway-dev/MazewayREADME
Mazeway Auth
Introduction
Authentication should live in your project, not a node_modules folder.
Think Clerk, but you own the code.
This is a complete, production-ready auth starter for anyone, including enterprise.
Check out mazeway.dev if you want!
The philosophy
People like Shadcn UI because:
- The components are in YOUR project
- You own all the code
- Can do whatever you want with them
- They don't belong in a node_modules folder
Comparing Shadcn UI to bootstrap is like comparing Mazeway to Clerk:
Clerk:
- Locked in
- Gets expensive quick
- Can't self host
- Limited customization
- Closed-source
- Still lacks some auth (that you can't add)
Mazeway:
- Affordable thanks to Supabase
- Can be self-hosted
- Unlimited customization
- Open-source
- Actual complete auth
- Plus:
- Community-driven
- More secure
- Auth config to change common things
- Later: extensions by the community
- Acts as a foundation, not a final product. Start here, build on it.
Thanks for letting me roast my competitors. Seriously though, Clerk isn't bad at all, it just serves a different purpose than Mazeway.
- Clerk: for people who want a quick service (Bootstrap people)
- Mazeway: for people who want to own their code and pay less (Shadcn people)
Tech stack
The project uses modern tech (note that not all are required to setup at all):
- Next.js 15
- Tailwind CSS
- Shadcn UI
- Supabase
- Resend
- Upstash Redis
- Trigger.dev
I see a lot of new apps having only 5% of authentication. Including:
- Missing login page
- No "forgot password" option
- Missing crucial security (2FA, device sessions, email alerts, and more)
- Weird UI glitches with auth
- DDoS attacks for not having proper security and API rate limiting
- HUGE bills, for lack of security again
- This list is usually longer but you get the point
These are the kind of things that should be implemented by default.
That's what this project gives you: a foundation that you can build on.
But just know:
- This project doesn't guarantee 100% security
- Nothing is bulletproof
- Though it does give you a very good foundation
What's included
- Sign-in options:
Email/passwordGoogleGitHub(new)- More soon! (planned SSO, passwordless, etc)
- Complete authentication flow:
- Login/signup pages
- Password reset
- Device sessions tracking
- Two-factor authentication (2FA):
- Authenticator App
- SMS
- Backup codes
- Settings
- Basic profile management
- Change password
- Device session management
- View active sessions
- Revoke device access
- Email alerts for new logins
- Account activity tracking
- View activity history (logins, disable 2FA, etc)
- Get alerts for sensitive activity (unknown device login, etc)
- Enable and disable 2FA (including individual methods)
- Account connections
- Allows users to link/unlink social methods
- View connected methods (obv)
- Cases like changing email with OAuth-only handled
- Verification:
- 2FA methods (Authenticator, SMS)
- Backup codes (for 2FA-accounts)
- Cryptographically secure
- Supports multiple formats (words, alphanumeric, numeric)
- Password verification (no-2FA accounts with password)
- Email verification (no-2FA accounts)
- API rate limiting with Upstash Redis
- User data exports (GDPR Compliance)
- Bonus: a nice auth config in the project for devs to easily customize things (opens up more things than on this list)
This is only the beginning.
Getting started
Before we get started, understand:
- Do not at ANY point during this setup think about production
- We will do it LATER. Some examples:
- "Should I use a professional email here..."
- "I also need to buy a custom domain"
- "What about production API keys?"
- Don't think about these things at all.
- For your own sake, don't just copy things
- Actually try to reason about what we're doing
1. Install dependencies
In the terminal, run this:
npm install
2. Reset project
This is to clean up any setup for the demo so you can start fresh.
Reset it:
npm run reset-project
2. Set up Supabase
-
Create a Supabase project
- Go to Supabase
- If you don't have an account, create one
- Click "New project"
- Name it "my-app-dev" (your actual app name), choose location and generate a database password
-
Get API keys
- Once the project is fully created, go to API Settings
- Get your "Project URL", "anon" key and "service_role" key
Note that Supabase is changing "anon" and "service_role" to "publishable" and "secret". This may have changed when you're reading this.
-
Update environment variables
- Open the
.env.examplefile - Copy the contents to a new file called
.env.local - Replace the values with your own:
NEXT_PUBLIC_SUPABASE_URL: your project URL from step 2NEXT_PUBLIC_SUPABASE_ANON_KEY: your anon/publishable key from step 2SUPABASE_SERVICE_ROLE_KEY: your service role/secret key from step 2
Note: The ANON key is designed to be public! See Reddit discussion and Supabase docs
- Open the
-
Create Supabase tables
- Head over to the Supabase SQL Editor
- Run these code snippets
- It'll set up necessary functions and tables
- Pro tip: look at what you're actually copying/pasting so you know what you're working with
-
Create a bucket for profile pictures
- Go to Supabase Storage
- Click "Create bucket"
- Name it "profile-pics"
- Make it public (major platforms do this, pfps are meant to be public)
- Under "Additional configuration":
- Turn on "Restrict file upload size for bucket"
- Set it to whatever you want (if you don't know, 5MB is good)
- Set "Allowed MIME types" to "image/webp"
- Click "Create bucket"
- Add RLS policies to this bucket (yes you can really do that):
- Go to Supabase SQL Editor
- Run this query:
-- Allow users to upload their own profile pictures CREATE POLICY "Allow users to upload their own profile pictures" ON storage.objects FOR INSERT WITH CHECK ( bucket_id = 'profile-pics' AND auth.uid()::text = (storage.foldername(name))[1] ); -- Allow users to update/replace their own profile pictures CREATE POLICY "Allow users to update their own profile pictures" ON storage.objects FOR UPDATE USING ( bucket_id = 'profile-pics' AND auth.uid()::text = (storage.foldername(name))[1] ); -- Allow users to delete their own profile pictures CREATE POLICY "Allow users to delete their own profile pictures" ON storage.objects FOR DELETE USING ( bucket_id = 'profile-pics' AND auth.uid()::text = (storage.foldername(name))[1] );
-
Change email templates
- Go to Supabase Email Templates
- Copy and paste these email templates
-
Add redirect URLs in Supabase
- Go here
- Add these redirect URLs
http://localhost:3000/api/auth/callbackhttp://localhost:3000/api/auth/confirm
- Ensures Supabase can actually redirect to these routes
-
Syncing user email
"Syncing user email? What? Why?"
Alright, let me break it down:
- We created some Supabase tables earlier. Remember?
- One of those were a "users" table.
- This table includes an "email" column
- Supabase also stores an email in the auth user
- We need to make sure these are in sync
The solution? Stupid simple. Let's set it up:
- Go to Supabase SQL Editor
- Run this code:
CREATE OR REPLACE FUNCTION public.update_user_email() RETURNS TRIGGER AS $$ BEGIN -- Only update if email has changed IF NEW.email <> OLD.email THEN UPDATE public.users SET email = NEW.email WHERE id = NEW.id; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; -- Trigger to call the function on UPDATE in auth.users CREATE OR REPLACE TRIGGER on_auth_user_updated AFTER UPDATE ON auth.users FOR EACH ROW EXECUTE FUNCTION public.update_user_email();This will:
- Create a function that updates the email column to whatever is in auth
- Make a trigger on the users table to call this function when a row changes
Done!
3. Set up Resend (optional)
Supabase (as of now) gives you 2 free emails per hour but it's unreliable. Sometimes, unclear errors will pop up because of their SMTP and you might end up spending hours debugging it.
You can totally skip setting up Resend but be mindful that if auth doesn't work, setting up Resend will probably fix it.
Aside from that, the project uses Resend for:
- Email alerts
- Device verification
- Email verification
If you don't set up Resend:
- Users won't get email alerts at all
- Device verification will be disabled entirely
- Email verification won't be enabled
- All devices will be "trusted" by default
- None of this really matters for development
With that out the way, here's how to do it:
Luckily... Resend makes it really straightforward to integrate with Supabase.
You won't even need to touch the Supabase dashboard to do it.
- Get a domain if yo
