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
| Component | Usage |
|---|---|
<SignIn /> | Complete sign-in form |
<SignUp /> | Registration form |
<UserButton /> | Avatar dropdown with sign out |
Protection
| Component | Usage |
|---|---|
<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
| Component | Usage |
|---|---|
<OrgSwitcher /> | Organization dropdown |
<CreateOrganization /> | Create org form |
<InviteMember /> | Invite members |
<MemberList /> | Manage members |
<OrganizationProfile /> | Org settings |
Account
| Component | Usage |
|---|---|
<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
- •Documentation: https://docs.maestro.dev
- •GitHub: https://github.com/maestro-idp/maestro
- •NPM: https://npmjs.com/package/@maestro-idp/react