@ouim/simple-logto
Version:
A simpler way to use @logto/react with prebuilt UI components and hooks for fast authentication setup
438 lines (324 loc) • 10.4 kB
Markdown
# @ouim/simple-logto
A simpler way to use [@logto/react](https://github.com/logto-io/logto) with prebuilt UI components and hooks for fast authentication setup in React apps.
## Features
### Frontend Components & Hooks
- **AuthProvider**: Easy context provider for Logto authentication
- **UserCenter**: Prebuilt user dropdown/avatar for your navbar
- **CallbackPage**: Handles OAuth callback and popup flows
- **SignInPage**: Standalone sign-in page component
- **useAuth**: React hook for accessing user and auth actions
- **Custom navigation**: Integrates with React Router, Next.js, etc.
- **Popup sign-in**: Support for popup-based authentication
- **Guest mode**: Built-in guest user support with fingerprinting
### Backend Authentication
- **JWT Verification**: Manual JWT verification with JWKS caching
- **Express.js Middleware**: Ready-to-use Express middleware
- **Next.js Support**: API routes and middleware helpers
- **Scope-based Authorization**: Built-in scope validation
- **Multiple Token Sources**: Supports cookies and Authorization headers
- **TypeScript Support**: Full TypeScript definitions
### Bundler Configuration
- **Vite**: Pre-configured Vite settings
- **Webpack**: Webpack configuration helpers
- **Next.js**: Next.js bundler configuration
## Installation
```sh
npm install @ouim/simple-logto
```
## Quick Start
### Frontend Setup
### Frontend Setup
#### 1. AuthProvider
Wrap your app with `AuthProvider` and pass your Logto config:
```tsx
import { AuthProvider } from '@ouim/simple-logto'
const config = {
endpoint: 'https://your-logto-endpoint.com',
appId: 'your-app-id',
}
function App() {
return (
<AuthProvider
config={config}
callbackUrl="http://localhost:3000/callback"
enablePopupSignIn={true} // Enable popup-based sign-in
// Optionally: customNavigate for SPA routing
// customNavigate={(url, options) => { ... }}
>
<YourApp />
</AuthProvider>
)
}
```
#### 2. UserCenter Component
Drop the `UserCenter` component into your navbar for a ready-to-use user menu:
```tsx
import { UserCenter } from '@ouim/simple-logto'
function Navbar() {
return (
<nav className="flex items-center justify-between h-16 px-4 border-b">
<div className="font-bold">MyApp</div>
<UserCenter />
</nav>
)
}
```
- Shows avatar, name, and sign out when authenticated.
- Shows sign in button when not authenticated.
- Accepts optional props:
- `className`
- `signoutCallbackUrl` (defaults to `/`)
- `globalSignOut` (defaults to `true`)
- `additionalPages` - array of `{ link: string; text: string; icon?: ReactNode }`
Example adding custom pages:
```tsx
<UserCenter additionalPages={[{ link: '/settings', text: 'Go to your settings' }]} />
```
#### 3. CallbackPage
Create a route (e.g. `/callback`) and render `CallbackPage` to handle OAuth redirects:
```tsx
import { CallbackPage } from '@ouim/simple-logto'
export default function Callback() {
return <CallbackPage />
}
```
- Handles both normal and popup sign-in flows.
- Optional props:
- `onSuccess`, `onError`, `loadingComponent`, `successComponent`
#### 4. SignInPage (Optional)
For a standalone sign-in page:
```tsx
import { SignInPage } from '@ouim/simple-logto'
export default function SignIn() {
return <SignInPage />
}
```
#### 5. useAuth Hook
Access the current user and authentication actions anywhere in your app:
```tsx
import { useAuth } from '@ouim/simple-logto'
function Dashboard() {
const { user, isLoadingUser, signIn, signOut } = useAuth()
if (isLoadingUser) return <div>Loading...</div>
if (!user) return <button onClick={() => signIn()}>Sign in</button>
return (
<div>
<p>Welcome, {user.name}!</p>
<button onClick={() => signOut()}>Sign out</button>
</div>
)
}
```
#### 5. useAuth Hook
Access the current user and authentication actions anywhere in your app:
```tsx
import { useAuth } from '@ouim/simple-logto'
function Dashboard() {
const { user, isLoadingUser, signIn, signOut, refreshAuth } = useAuth()
if (isLoadingUser) return <div>Loading...</div>
if (!user) return <button onClick={() => signIn()}>Sign in</button>
return (
<div>
<p>Welcome, {user.name}!</p>
<button onClick={() => signOut()}>Sign out</button>
<button onClick={refreshAuth}>Refresh Auth</button>
</div>
)
}
```
##### Route Protection Example
```tsx
function ProtectedPage() {
const { user } = useAuth({
middleware: 'auth',
redirectTo: '/login', // Redirect if not authenticated
})
if (!user) return null // or loading indicator
return <div>Protected content</div>
}
```
##### Guest Mode Example
```tsx
function MixedContent() {
const { user } = useAuth({
middleware: 'guest', // Allow guest users
})
return <div>{user ? <p>Welcome back, {user.name}!</p> : <p>You're browsing as a guest</p>}</div>
}
```
## Backend Authentication
The library includes powerful backend authentication helpers for Node.js applications.
### Installation
Backend features are included with the main package:
```bash
npm install @ouim/simple-logto
```
### Express.js Middleware
```javascript
import { createExpressAuthMiddleware } from '@ouim/simple-logto/backend'
const authMiddleware = createExpressAuthMiddleware({
logtoUrl: 'https://your-logto-domain.com',
audience: 'your-api-resource-identifier',
cookieName: 'logto_authtoken', // optional, defaults to 'logto_authtoken'
requiredScope: 'some_scope', // optional
allowGuest: true, // optional, enables guest mode
})
// Use in your Express routes
app.get('/protected', authMiddleware, (req, res) => {
res.json({
message: 'Hello authenticated user!',
userId: req.auth.userId,
isAuthenticated: req.auth.isAuthenticated,
isGuest: req.auth.isGuest,
})
})
```
### Next.js API Routes
```javascript
// app/api/protected/route.js
import { verifyNextAuth } from '@ouim/simple-logto/backend'
export async function GET(request) {
const authResult = await verifyNextAuth(request, {
logtoUrl: 'https://your-logto-domain.com',
audience: 'your-api-resource-identifier',
cookieName: 'logto_authtoken', // optional
requiredScope: 'some_scope', // optional
allowGuest: true, // optional
})
if (!authResult.success) {
return Response.json({ error: authResult.error }, { status: 401 })
}
return Response.json({
message: 'Hello authenticated user!',
userId: authResult.auth.userId,
payload: authResult.auth.payload,
})
}
```
### Next.js Middleware
```javascript
// middleware.js
import { verifyNextAuth } from '@ouim/simple-logto/backend'
import { NextResponse } from 'next/server'
export async function middleware(request) {
if (request.nextUrl.pathname.startsWith('/api/protected')) {
const authResult = await verifyNextAuth(request, {
logtoUrl: process.env.LOGTO_URL,
audience: process.env.LOGTO_AUDIENCE,
})
if (!authResult.success) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
// Add auth info to headers for the API route
const response = NextResponse.next()
response.headers.set('x-user-id', authResult.auth.userId)
return response
}
}
export const config = {
matcher: '/api/protected/:path*',
}
```
### Generic Usage
```javascript
import { verifyAuth } from '@ouim/simple-logto/backend'
// Verify with token string
try {
const auth = await verifyAuth('your-jwt-token', {
logtoUrl: 'https://your-logto-domain.com',
audience: 'your-api-resource-identifier',
})
console.log('User ID:', auth.userId)
} catch (error) {
console.error('Auth failed:', error.message)
}
// Verify with request object
try {
const auth = await verifyAuth(requestObject, {
logtoUrl: 'https://your-logto-domain.com',
audience: 'your-api-resource-identifier',
})
} catch (error) {
console.error('Auth failed:', error.message)
}
```
### Backend Configuration Options
- `logtoUrl`: Your Logto server URL (required)
- `audience`: Your API resource identifier (required)
- `cookieName`: Custom cookie name (optional, defaults to 'logto_authtoken')
- `requiredScope`: Required scope for access (optional)
- `allowGuest`: Enable guest mode with fingerprinting (optional)
### Auth Context
When authentication is successful, you'll get an `AuthContext` object:
```typescript
interface AuthContext {
userId: string | null // User ID from token (null for guests)
isAuthenticated: boolean // true for authenticated users
isGuest: boolean // true for guest users
payload: AuthPayload | null // Full JWT payload (null for guests)
guestId?: string // Guest fingerprint ID (when allowGuest is true)
}
```
## Bundler Configuration
The library provides pre-configured bundler settings to resolve common issues with the `jose` library and other dependencies.
### Vite
```javascript
// vite.config.js
import { viteConfig } from '@ouim/simple-logto'
export default {
...viteConfig,
// your other config
}
```
### Webpack
```javascript
// webpack.config.js
import { webpackConfig } from '@ouim/simple-logto'
module.exports = {
...webpackConfig,
// your other config
}
```
### Next.js
```javascript
// next.config.js
import { nextjsConfig } from '@ouim/simple-logto'
module.exports = {
...nextjsConfig,
// your other config
}
```
### Custom Configuration
```javascript
import { getBundlerConfig } from '@ouim/simple-logto'
// Get configuration for specific bundler
const config = getBundlerConfig('vite') // 'vite' | 'webpack' | 'nextjs'
```
## Utilities
The library also exports several utility functions:
```typescript
import { cookieUtils, jwtCookieUtils } from '@ouim/simple-logto'
// Cookie utilities
cookieUtils.set('key', 'value')
cookieUtils.get('key')
cookieUtils.remove('key')
// JWT-specific cookie utilities
jwtCookieUtils.setToken('jwt-token')
jwtCookieUtils.getToken()
jwtCookieUtils.removeToken()
```
## TypeScript Support
The library is written in TypeScript and provides comprehensive type definitions:
```typescript
import type { LogtoUser, AuthOptions, AuthMiddleware, CallbackPageProps, NavigationOptions, AdditionalPage } from '@ouim/simple-logto'
// Backend types
import type { AuthContext, AuthPayload, VerifyAuthOptions } from '@ouim/simple-logto/backend'
```