AgentSkillsCN

Maestro

Maestro

SKILL.md

Maestro IDP Authentication

Add Maestro IDP authentication to React/Next.js applications.

When to Use

  • User wants to add authentication to their app
  • User mentions "Maestro", "Maestro IDP", or self-hosted auth
  • User wants open-source, self-hostable authentication
  • User needs organizations/multi-tenant support

Quick Setup

1. Install

bash
npm install @maestro-idp/react

2. Add Provider to Root Layout

tsx
// app/layout.tsx
import { MaestroProvider } from '@maestro-idp/react'
import '@maestro-idp/react/styles.css'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <MaestroProvider
          publishableKey={process.env.NEXT_PUBLIC_MAESTRO_PUBLISHABLE_KEY!}
          domain={process.env.NEXT_PUBLIC_MAESTRO_API_URL || ''}
          signInUrl="/sign-in"
          signUpUrl="/sign-up"
          afterSignInUrl="/dashboard"
        >
          {children}
        </MaestroProvider>
      </body>
    </html>
  )
}

3. Create Environment Variables

Create .env.local:

env
NEXT_PUBLIC_MAESTRO_PUBLISHABLE_KEY=pk_test_your_key
NEXT_PUBLIC_MAESTRO_API_URL=https://api.maestro.dev

4. Create Auth Pages

Sign In (app/sign-in/page.tsx):

tsx
import { SignIn, RedirectIfSignedIn } from '@maestro-idp/react'

export default function SignInPage() {
  return (
    <RedirectIfSignedIn redirectUrl="/dashboard">
      <div className="min-h-screen flex items-center justify-center bg-gray-50">
        <SignIn />
      </div>
    </RedirectIfSignedIn>
  )
}

Sign Up (app/sign-up/page.tsx):

tsx
import { SignUp, RedirectIfSignedIn } from '@maestro-idp/react'

export default function SignUpPage() {
  return (
    <RedirectIfSignedIn redirectUrl="/dashboard">
      <div className="min-h-screen flex items-center justify-center bg-gray-50">
        <SignUp />
      </div>
    </RedirectIfSignedIn>
  )
}

5. Protect Routes

tsx
// app/dashboard/page.tsx
import { Protect, UserButton } from '@maestro-idp/react'

export default function DashboardPage() {
  return (
    <Protect redirectTo="/sign-in">
      <header className="border-b p-4 flex justify-between items-center">
        <h1 className="text-xl font-semibold">Dashboard</h1>
        <UserButton />
      </header>
      <main className="p-6">
        Protected content here
      </main>
    </Protect>
  )
}

Components Reference

Authentication

ComponentUsage
<SignIn />Complete sign-in form
<SignUp />Registration form
<UserButton />Avatar dropdown with sign out

Protection

ComponentUsage
<Protect>Wrap protected content
<Protect redirectTo="/sign-in">Redirect if not authed
<Protect role="admin">Require specific role
<RedirectToSignIn />Force redirect to sign-in
<RedirectIfSignedIn />Redirect authed users away

Organizations

ComponentUsage
<OrgSwitcher />Organization dropdown
<CreateOrganization />Create org form
<InviteMember />Invite members
<MemberList />Manage members
<OrganizationProfile />Org settings

Account

ComponentUsage
<AccountSettings />Full account settings
<AccountSettings showSessions />With session management
<AccountSettings showDangerZone />With delete account

Hooks

tsx
import { useAuth, useUser, useOrganization } from '@maestro-idp/react'

// Auth actions
const { isLoaded, isSignedIn, signIn, signOut } = useAuth()

// User data
const { user } = useUser()

// Organization context
const { organization, memberships, setActiveOrganization } = useOrganization()

Common Patterns

Add to Navbar

tsx
import { UserButton, OrgSwitcher } from '@maestro-idp/react'

function Navbar() {
  return (
    <nav className="flex items-center justify-between p-4 border-b">
      <Logo />
      <div className="flex items-center gap-4">
        <OrgSwitcher />
        <UserButton />
      </div>
    </nav>
  )
}

Conditional Rendering

tsx
import { useUser } from '@maestro-idp/react'

function Header() {
  const { isLoaded, isSignedIn, user } = useUser()
  
  if (!isLoaded) return <Skeleton />
  
  if (!isSignedIn) {
    return <Link href="/sign-in">Sign In</Link>
  }
  
  return <span>Hello, {user.name || user.email}</span>
}

API Route Protection (Next.js)

tsx
// app/api/protected/route.ts
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
  const token = request.headers.get('Authorization')?.replace('Bearer ', '')
  
  if (!token) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }
  
  // Verify with Maestro API
  const res = await fetch(`${process.env.MAESTRO_API_URL}/auth/me`, {
    headers: { Authorization: `Bearer ${token}` }
  })
  
  if (!res.ok) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }
  
  const { user } = await res.json()
  return NextResponse.json({ user })
}

Styling

Always import styles:

tsx
import '@maestro-idp/react/styles.css'

Customize with CSS variables:

css
:root {
  --maestro-color-primary: #8b5cf6;
  --maestro-color-background: #ffffff;
  --maestro-border-radius: 12px;
}

Or use appearance prop:

tsx
<SignIn appearance={{ theme: 'dark', variables: { colorPrimary: '#8b5cf6' } }} />

Project Structure

After setup, your project should have:

code
app/
├── layout.tsx          # MaestroProvider here
├── page.tsx            # Landing page
├── sign-in/
│   └── page.tsx        # <SignIn />
├── sign-up/
│   └── page.tsx        # <SignUp />
├── dashboard/
│   └── page.tsx        # <Protect><Content /></Protect>
└── settings/
    └── page.tsx        # <AccountSettings />

Troubleshooting

"MaestroProvider not found" → Ensure MaestroProvider wraps your entire app in root layout.

Components unstyled → Import @maestro-idp/react/styles.css in root layout.

Auth not persisting → Check domain prop matches your API URL.

Links