npm package: @keyra/nextjs
Description: Next.js integration for Keyra authentication - Clerk alternative
Author: Keyra
Latest version: 1.1.1

# @keyra/nextjs

Next.js integration for Keyra authentication - a modern authentication platform for developers.

## Installation

```bash
npm install @keyra/nextjs
```

## Quick Start

### 1. Set Environment Variables

Create `.env.local`:

```bash
NEXT_PUBLIC_KEYRA_PUBLISHABLE_KEY=pk_test_YOUR_KEY
KEYRA_SECRET_KEY=sk_test_YOUR_KEY
```

### 2. Add Middleware

Create `middleware.ts` in your project root:

```typescript
import { keyraMiddleware } from '@keyra/nextjs/server'

export default keyraMiddleware()

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
}
```

**Protect specific routes:**

```typescript
export default keyraMiddleware({
  publicRoutes: ['/', '/pricing', '/about'],
})
```

### 3. Wrap Your App

Update `app/layout.tsx`:

```tsx
import { KeyraProvider } from '@keyra/nextjs'
import './globals.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <KeyraProvider>
          {children}
        </KeyraProvider>
      </body>
    </html>
  )
}
```

### 4. Add Authentication UI

Update `app/page.tsx`:

```tsx
import {
  SignInButton,
  SignUpButton,
  SignedIn,
  SignedOut,
  UserButton,
} from '@keyra/nextjs'

export default function Home() {
  return (
    <div>
      <header>
        <SignedOut>
          <SignInButton mode="modal">
            <button>Sign In</button>
          </SignInButton>
          <SignUpButton mode="modal">
            <button>Sign Up</button>
          </SignUpButton>
        </SignedOut>
        <SignedIn>
          <UserButton />
        </SignedIn>
      </header>

      <main>
        <SignedOut>
          <p>You are signed out</p>
        </SignedOut>
        <SignedIn>
          <p>You are signed in!</p>
        </SignedIn>
      </main>
    </div>
  )
}
```

### 5. Create Sign-In/Sign-Up Pages (Optional)

Create `app/sign-in/[[...sign-in]]/page.tsx`:

```tsx
import { SignIn } from '@keyra/nextjs'

export default function SignInPage() {
  return (
    <div style={{ display: 'flex', justifyContent: 'center', padding: '4rem' }}>
      <SignIn />
    </div>
  )
}
```

Create `app/sign-up/[[...sign-up]]/page.tsx`:

```tsx
import { SignUp } from '@keyra/nextjs'

export default function SignUpPage() {
  return (
    <div style={{ display: 'flex', justifyContent: 'center', padding: '4rem' }}>
      <SignUp />
    </div>
  )
}
```

---

## Server-Side Usage

### Get Current User (Server Component)

```tsx
import { currentUser } from '@keyra/nextjs/server'

export default async function ProfilePage() {
  const user = await currentUser()
  
  if (!user) {
    return <div>Not signed in</div>
  }
  
  return (
    <div>
      <h1>Welcome {user.firstName}!</h1>
      <p>Email: {user.email}</p>
    </div>
  )
}
```

### Get Auth State (Server Component)

```tsx
import { auth } from '@keyra/nextjs/server'
import { redirect } from 'next/navigation'

export default async function DashboardPage() {
  const { userId, sessionId, orgId } = await auth()
  
  if (!userId) {
    redirect('/sign-in')
  }
  
  return <div>User ID: {userId}</div>
}
```

### Protect Page with Role Check

```tsx
import { auth } from '@keyra/nextjs/server'

export default async function AdminPage() {
  const authObj = await auth()
  
  try {
    authObj.protect({ role: 'admin' })
  } catch {
    return <div>You need admin access</div>
  }
  
  return <div>Admin content</div>
}
```

### Protect API Routes

```typescript
// app/api/protected/route.ts
import { auth } from '@keyra/nextjs/server'
import { NextResponse } from 'next/server'

export async function GET() {
  const { userId } = await auth()
  
  if (!userId) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }
  
  return NextResponse.json({ message: 'Protected data', userId })
}
```

### With Auth Helper (Auto-Protected)

```typescript
// app/api/user/route.ts
import { withAuth } from '@keyra/nextjs/server'
import { NextResponse } from 'next/server'

export const GET = withAuth(async (req, auth) => {
  // auth.userId is guaranteed to exist
  return NextResponse.json({ userId: auth.userId })
})
```

---

## Client-Side Usage

### Use Hooks in Client Components

```tsx
'use client'

import { useUser, useAuth } from '@keyra/nextjs'

export default function Profile() {
  const { user, isLoaded, isSignedIn } = useUser()
  const { signOut } = useAuth()

  if (!isLoaded) return <div>Loading...</div>
  if (!isSignedIn) return <div>Not signed in</div>

  return (
    <div>
      <h1>Hello {user.firstName}!</h1>
      <button onClick={() => signOut()}>Sign out</button>
    </div>
  )
}
```

### Custom Sign-In Flow

```tsx
'use client'

import { useSignIn } from '@keyra/nextjs'
import { useRouter } from 'next/navigation'
import { useState } from 'react'

export function CustomSignIn() {
  const { signIn, setActive } = useSignIn()
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const router = useRouter()
  
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    
    try {
      const result = await signIn.create({
        identifier: email,
        password,
      })
      
      if (result.status === 'complete') {
        await setActive({ session: result.createdSessionId })
        router.push('/dashboard')
      }
    } catch (error) {
      console.error('Sign-in error:', error)
    }
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit">Sign In</button>
    </form>
  )
}
```

---

## Components

All components from `@keyra/react` are re-exported:

### `<SignIn />`

Pre-built sign-in component with customizable appearance.

```tsx
<SignIn 
  redirectUrl="/dashboard"
  signUpUrl="/sign-up"
  appearance={{
    baseTheme: 'dark',
    variables: {
      colorPrimary: '#6366f1',
    },
  }}
/>
```

### `<SignUp />`

Pre-built sign-up component.

```tsx
<SignUp 
  redirectUrl="/onboarding"
  signInUrl="/sign-in"
/>
```

### `<UserButton />`

User menu dropdown with profile and sign-out.

```tsx
<UserButton 
  afterSignOutUrl="/"
  showName={true}
/>
```

### `<SignInButton />` / `<SignOutButton />`

Trigger authentication actions.

```tsx
<SignInButton mode="modal">
  <button>Sign In</button>
</SignInButton>

<SignOutButton>
  <button>Sign Out</button>
</SignOutButton>
```

### `<SignedIn>` / `<SignedOut>`

Conditional rendering based on auth state.

```tsx
<SignedIn>
  <Dashboard />
</SignedIn>

<SignedOut>
  <LandingPage />
</SignedOut>
```

### `<Protect>`

Protect content with role/permission checks.

```tsx
<Protect role="admin" fallback={<div>Unauthorized</div>}>
  <AdminPanel />
</Protect>
```

### `<OrganizationSwitcher>`

Organization selection and management.

```tsx
<OrganizationSwitcher 
  afterSelectOrganizationUrl="/dashboard"
/>
```

### `<RedirectToSignIn>` / `<RedirectToSignUp>`

Redirect helpers.

```tsx
<RedirectToSignIn returnBackUrl="/dashboard" />
```

---

## Hooks

All hooks from `@keyra/react` are re-exported:

### `useAuth()`

Get authentication state and methods.

```tsx
const { 
  isLoaded, 
  isSignedIn, 
  userId, 
  sessionId, 
  orgId,
  signOut,
  getToken,
} = useAuth()
```

### `useUser()`

Get current user information.

```tsx
const { user, isLoaded, isSignedIn } = useUser()
```

### `useSession()`

Get current session information.

```tsx
const { session, isLoaded } = useSession()
```

### `useOrganization()`

Get current organization.

```tsx
const { organization, isLoaded } = useOrganization()
```

### `useSignIn()`

Sign-in methods and state.

```tsx
const { signIn, isLoaded, setActive } = useSignIn()
```

### `useSignUp()`

Sign-up methods and state.

```tsx
const { signUp, isLoaded, setActive } = useSignUp()
```

### `useSessionList()`

Manage multiple sessions.

```tsx
const { sessions, setActive } = useSessionList()
```

### `useKeyra()`

Complete Keyra context.

```tsx
const { 
  user, 
  session, 
  organization, 
  signIn, 
  signUp, 
  signOut,
} = useKeyra()
```

---

## Middleware Configuration

### Basic Configuration

```typescript
keyraMiddleware({
  // Routes that don't require authentication
  publicRoutes: ['/', '/about', '/pricing'],
  
  // Routes to ignore completely
  ignoredRoutes: ['/api/webhooks/*'],
  
  // Custom URLs
  signInUrl: '/sign-in',
  signUpUrl: '/sign-up',
  afterSignInUrl: '/dashboard',
  afterSignUpUrl: '/onboarding',
  afterSignOutUrl: '/',
  
  // Enable debug logging
  debug: process.env.NODE_ENV === 'development',
})
```

### Pattern Matching

Use wildcards for route patterns:

```typescript
publicRoutes: [
  '/',
  '/about',
  '/blog/*',        // All blog routes
  '/api/public/*',  // Public API routes
]
```

---

## Server Utilities

### `auth()`

Get authentication state in server components and API routes.

```typescript
const { 
  userId, 
  sessionId, 
  orgId, 
  protect, 
  redirectToSignIn 
} = await auth()

// Protect with role check
protect({ role: 'admin' })

// Redirect if not signed in
if (!userId) {
  return redirectToSignIn('/protected-page')
}
```

### `currentUser()`

Get current user object in server components.

```typescript
const user = await currentUser()
// Returns: User | null
```

### `withAuth()`

HOC for automatically protecting API routes.

```typescript
export const GET = withAuth(async (req, auth) => {
  // auth.userId is guaranteed to exist
  return NextResponse.json({ userId: auth.userId })
})
```

### `verifyWebhook()`

Verify webhook signatures from Keyra.

```typescript
import { verifyWebhook } from '@keyra/nextjs/server'

export async function POST(req: Request) {
  const payload = await req.text()
  const headers = Object.fromEntries(req.headers.entries())
  
  const isValid = verifyWebhook(
    payload, 
    headers, 
    process.env.KEYRA_WEBHOOK_SECRET!
  )
  
  if (!isValid) {
    return new Response('Invalid signature', { status: 401 })
  }
  
  // Process webhook...
  return new Response('OK')
}
```

---

## Pages Router Support

For Next.js Pages Router:

```tsx
// pages/_app.tsx
import { KeyraProvider } from '@keyra/nextjs'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <KeyraProvider>
      <Component {...pageProps} />
    </KeyraProvider>
  )
}
```

---

## TypeScript Support

Full TypeScript support with exported types:

```typescript
import type { 
  User,
  Session,
  Organization,
  AuthObject,
  KeyraMiddlewareOptions,
  SignInProps,
  SignUpProps,
  UserButtonProps,
} from '@keyra/nextjs'
```

---

## Environment Variables

Required variables:

```bash
# Required for all features
NEXT_PUBLIC_KEYRA_PUBLISHABLE_KEY=pk_test_...
KEYRA_SECRET_KEY=sk_test_...

# Optional
NEXT_PUBLIC_KEYRA_API_URL=https://api.keyra.app
KEYRA_WEBHOOK_SECRET=whsec_...
```

---

## Customization

### Theme Customization

```tsx
const appearance = {
  baseTheme: 'dark', // 'light' | 'dark'
  variables: {
    colorPrimary: '#6366f1',
    colorBackground: '#1f2937',
    colorText: '#f9fafb',
    borderRadius: '8px',
  },
  elements: {
    formButtonPrimary: {
      backgroundColor: '#6366f1',
      '&:hover': {
        backgroundColor: '#4f46e5',
      },
    },
  },
}

<SignIn appearance={appearance} />
```

### Custom Navigation

```tsx
<KeyraProvider
  navigate={(to) => router.push(to)}
  appearance={appearance}
>
  {children}
</KeyraProvider>
```

---

## Examples

### Complete Auth Flow

```tsx
// app/layout.tsx
import { KeyraProvider } from '@keyra/nextjs'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <KeyraProvider>
          <Header />
          {children}
        </KeyraProvider>
      </body>
    </html>
  )
}

// components/Header.tsx
'use client'

import { SignedIn, SignedOut, SignInButton, UserButton } from '@keyra/nextjs'

export function Header() {
  return (
    <header>
      <SignedOut>
        <SignInButton />
      </SignedOut>
      <SignedIn>
        <UserButton />
      </SignedIn>
    </header>
  )
}

// app/dashboard/page.tsx
import { currentUser } from '@keyra/nextjs/server'
import { redirect } from 'next/navigation'

export default async function Dashboard() {
  const user = await currentUser()
  
  if (!user) {
    redirect('/sign-in')
  }
  
  return <div>Welcome {user.firstName}!</div>
}
```

---

## Troubleshooting

### "Unauthorized" errors

- Verify environment variables are set correctly
- Check that middleware is configured properly
- Ensure cookies are enabled in the browser

### Redirect loops

- Make sure sign-in/sign-up pages are in `publicRoutes`
- Check middleware configuration

### Session not persisting

- Verify cookies are enabled
- Check domain configuration in production
- Ensure HTTPS is used in production

---

## Migration from Clerk

Keyra is designed to be API-compatible with Clerk. To migrate:

1. Replace `@clerk/nextjs` with `@keyra/nextjs`
2. Update environment variables from `CLERK_*` to `KEYRA_*`
3. Replace `clerkMiddleware` with `keyraMiddleware`
4. Update imports from `@clerk/nextjs` to `@keyra/nextjs`

Most code should work without changes!

---

## License

Proprietary - Commercial software. See your Keyra subscription for usage terms.

---

## Links

- [Documentation](https://keyra.app/docs)
- [GitHub](https://github.com/yourusername/keyra)
- [Examples](https://github.com/yourusername/keyra/tree/main/packages/examples)