SkillAgentSearch skills...

Mazeway

Clerk but you own the code because authentication should live in your project, not a node_modules folder.

Install / Use

/learn @mazeway-dev/Mazeway
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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/password
    • Google
    • GitHub (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

  1. 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
  2. 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.

  3. Update environment variables

    • Open the .env.example file
    • 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 2
      • NEXT_PUBLIC_SUPABASE_ANON_KEY: your anon/publishable key from step 2
      • SUPABASE_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

  4. 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
  5. 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):
      -- 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]
      );
      
  6. Change email templates

  7. Add redirect URLs in Supabase

    • Go here
    • Add these redirect URLs
      • http://localhost:3000/api/auth/callback
      • http://localhost:3000/api/auth/confirm
  • Ensures Supabase can actually redirect to these routes
  1. 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:

    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.

  1. Get a domain if yo
View on GitHub
GitHub Stars54
CategoryDevelopment
Updated26d ago
Forks6

Languages

TypeScript

Security Score

100/100

Audited on Mar 8, 2026

No findings