282 lines
7.3 KiB
Markdown
282 lines
7.3 KiB
Markdown
# SecNex Auth Login
|
|
|
|
A Next.js 15 application for authentication with SecNex. The application uses Server Actions for login/logout and Route Handlers for session checks. Authentication tokens are managed via HTTP-only cookies.
|
|
|
|
## Features
|
|
|
|
- **Login Form** - Username/password authentication with form validation
|
|
- **OAuth Authorization Flow** - `/authorize` endpoint for third-party app authorization
|
|
- **Session Management** - Token validation and automatic cleanup
|
|
- **Social Login Buttons** - GitHub and Google OAuth buttons (UI only)
|
|
- **Responsive UI** - Built with shadcn/ui components
|
|
- **Toast Notifications** - User feedback using Sonner
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
auth-login/
|
|
├── app/
|
|
│ ├── api/
|
|
│ │ ├── logout/
|
|
│ │ │ └── route.tsx # Route Handler for logout
|
|
│ │ └── session/
|
|
│ │ └── route.ts # Route Handler for session check
|
|
│ ├── authorize/
|
|
│ │ └── page.tsx # OAuth authorization page
|
|
│ ├── layout.tsx # Root layout with fonts
|
|
│ └── page.tsx # Home page (Login / Success)
|
|
├── components/
|
|
│ ├── core/
|
|
│ │ ├── login-form.tsx # Login form components (Client Components)
|
|
│ │ ├── authorize.tsx # OAuth authorization component
|
|
│ │ ├── social-login-button.tsx
|
|
│ │ └── loading.tsx # Loading spinner component
|
|
│ ├── server/
|
|
│ │ └── login.tsx # Server Actions for login
|
|
│ └── ui/ # UI components (shadcn/ui)
|
|
├── lib/
|
|
│ └── utils.ts # Utility functions
|
|
└── permissions.json # OAuth scope permissions
|
|
```
|
|
|
|
## Technical Details
|
|
|
|
### Route Handlers vs Server Actions
|
|
|
|
In Next.js 15, cookies can only be modified in the following contexts:
|
|
- **Route Handlers** (API routes under `app/api/`)
|
|
- **Server Actions** with `"use server"` directive
|
|
|
|
**Key Difference:**
|
|
- Route Handlers can ALWAYS modify cookies
|
|
- Server Actions CANNOT modify cookies when called from a Server Component
|
|
|
|
### Architecture Decisions
|
|
|
|
| Operation | Type | Reason |
|
|
|-----------|------|--------|
|
|
| Login | Server Action | Called from Client Components |
|
|
| Session Check | Route Handler | Called from Server Component, needs cookie deletion ability |
|
|
| Logout | Route Handler | Called from Client Component via fetch |
|
|
|
|
### Cookie Deletion in Route Handler
|
|
|
|
**Problem:** The original code attempted to delete cookies in a Server Action called from a Server Component, resulting in:
|
|
|
|
```
|
|
Error: Cookies can only be modified in a Server Action or Route Handler.
|
|
```
|
|
|
|
**Solution:** The session check was moved to a Route Handler (`app/api/session/route.ts`). Route Handlers always have access to cookie modification, regardless of the caller.
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- Node.js 18+
|
|
- pnpm
|
|
|
|
### Environment Variables
|
|
|
|
Create a `.env.local` file in the root directory:
|
|
|
|
```bash
|
|
SECNEX_API_HOST=http://localhost:3001
|
|
SECNEX_API_KEY=your_api_key_here
|
|
```
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
pnpm install
|
|
```
|
|
|
|
### Development Server
|
|
|
|
```bash
|
|
pnpm dev
|
|
```
|
|
|
|
The application will be available at [http://localhost:3000](http://localhost:3000).
|
|
|
|
### Build
|
|
|
|
```bash
|
|
pnpm build
|
|
```
|
|
|
|
### Production
|
|
|
|
```bash
|
|
pnpm start
|
|
```
|
|
|
|
## How It Works
|
|
|
|
### Login Flow
|
|
|
|
1. User enters username and password
|
|
2. `login()` Server Action is called
|
|
3. Request to SecNex API (`/login`)
|
|
4. On success: Token is stored in HTTP-only cookie
|
|
5. Page is refreshed
|
|
|
|
### Session Check (via Route Handler)
|
|
|
|
1. Server Component checks if token exists
|
|
2. GET request to `/api/session`
|
|
3. Route Handler validates token with SecNex API (`/session/info`)
|
|
4. On invalid token: Cookie is deleted directly
|
|
5. Result is returned
|
|
|
|
### Logout Flow
|
|
|
|
1. `GET /api/logout` is called
|
|
2. Request to SecNex API (`/logout`)
|
|
3. Cookie is deleted
|
|
|
|
### OAuth Authorization Flow
|
|
|
|
1. User is redirected to `/authorize` with query parameters:
|
|
- `client_id` - The client application identifier
|
|
- `redirect_uri` - The URI to redirect after authorization
|
|
- `response_type` - The response type (default: "code")
|
|
- `scope` - Requested permissions (default: "profile email")
|
|
2. User must be logged in (otherwise redirected to `/`)
|
|
3. Authorization page shows:
|
|
- Application name and URL
|
|
- User information
|
|
- Requested permissions (from `permissions.json`)
|
|
4. User can authorize or deny the request
|
|
|
|
## API Endpoints
|
|
|
|
### Server Actions
|
|
|
|
#### `login(username, password)`
|
|
|
|
Performs login and stores the token in a cookie.
|
|
|
|
```typescript
|
|
const response = await login("user", "password");
|
|
// { success: boolean, message: string, token?: string }
|
|
```
|
|
|
|
### Route Handlers
|
|
|
|
#### `GET /api/session`
|
|
|
|
Validates the token and returns session information. Automatically deletes the cookie on invalid token.
|
|
|
|
```typescript
|
|
const response = await fetch("/api/session");
|
|
const data = await response.json();
|
|
// { success: boolean, message: string, sessionInfo?: { username, email, role } }
|
|
```
|
|
|
|
#### `GET /api/logout`
|
|
|
|
Logs out the user and deletes the token.
|
|
|
|
```typescript
|
|
const response = await fetch("/api/logout");
|
|
const data = await response.json();
|
|
// { success: boolean, message: string }
|
|
```
|
|
|
|
## Components
|
|
|
|
### `LoginContainer`
|
|
|
|
Displays the login form.
|
|
|
|
```tsx
|
|
<LoginContainer applicationName="SecNex" applicationLogo="/logo.png" />
|
|
```
|
|
|
|
### `LoginSuccessContainer`
|
|
|
|
Displays the success page after login.
|
|
|
|
```tsx
|
|
<LoginSuccessContainer
|
|
applicationName="SecNex"
|
|
applicationLogo="/logo.png"
|
|
/>
|
|
```
|
|
|
|
### `AuthorizeContainer`
|
|
|
|
Displays the OAuth authorization page.
|
|
|
|
```tsx
|
|
<AuthorizeContainer
|
|
applicationName="My App"
|
|
applicationUrl="https://example.com"
|
|
applicationLogo="/app-logo.png"
|
|
client_id="abc123"
|
|
redirect_uri="https://example.com/callback"
|
|
response_type="code"
|
|
scope="profile email"
|
|
/>
|
|
```
|
|
|
|
## Permissions
|
|
|
|
The `permissions.json` file defines OAuth scope permissions:
|
|
|
|
```json
|
|
{
|
|
"profile": {
|
|
"name": "Profile Information",
|
|
"description": "View your name and profile picture"
|
|
},
|
|
"email": {
|
|
"name": "Email Address",
|
|
"description": "View your email address"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Important Notes
|
|
|
|
- Server Actions use `"use server"` directive
|
|
- Route Handlers allow cookie modification from Server Components
|
|
- Cookies are HTTP-only for additional security
|
|
- The SecNex API must be running at the configured `SECNEX_API_HOST`
|
|
- API credentials should be stored in environment variables (not hardcoded)
|
|
|
|
## Tech Stack
|
|
|
|
- **Next.js 15** - React framework
|
|
- **React 19** - UI library
|
|
- **TypeScript** - Type safety
|
|
- **Tailwind CSS** - Styling
|
|
- **shadcn/ui** - UI components
|
|
- **Radix UI** - Headless UI primitives
|
|
- **React Hook Form** - Form management
|
|
- **Zod** - Schema validation
|
|
- **Sonner** - Toast notifications
|
|
- **Tabler Icons** - Icon library
|
|
|
|
## Deployment
|
|
|
|
### Vercel
|
|
|
|
The easiest way to deploy is using the Vercel platform:
|
|
|
|
[https://vercel.com/new](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme)
|
|
|
|
For more information, see the [Next.js Deployment Documentation](https://nextjs.org/docs/app/building-your-application/deploying).
|
|
|
|
### Environment Variables
|
|
|
|
Make sure to set the following environment variables in your deployment:
|
|
|
|
- `SECNEX_API_HOST` - Your SecNex API host URL
|
|
- `SECNEX_API_KEY` - Your SecNex API key
|
|
|
|
## License
|
|
|
|
MIT
|