npm package: @starasia/sso-api
Description: SSO integration package for Node.js backend applications
Author: Starasia
Latest version: 0.0.2

# @starasia/sso-api

NPM package untuk mempermudah integrasi SSO (Single Sign-On) internal dengan sistem backend Node.js menggunakan Express.js framework.

## Features

- 🔐 JWT verification dengan RS256 algorithm
- 🚀 Easy Express.js middleware integration
- ✅ TypeScript support dengan full type definitions
- 📦 Redis caching untuk token validation (optional)
- 🔄 Automatic public key caching dan refresh
- 🛡️ Comprehensive error handling
- 🎯 Multiple token types support (AUTH, ACCESS, REFRESH)

## Installation

```bash
npm install @starasia/sso-api
```

atau dengan yarn:

```bash
yarn add @starasia/sso-api
```

atau dengan pnpm:

```bash
pnpm add @starasia/sso-api
```

## Quick Start

```typescript
import express from 'express'
import { config, auth, AUTH_TYPE, RequestWithAccessToken } from '@starasia/sso-api'

const app = express()
app.use(express.json())

// Initialize SSO
const ssoRouter = config({
  clientId: process.env.SSO_CLIENT_ID!,
  clientSecret: process.env.SSO_CLIENT_SECRET!,
  baseUrl: process.env.SSO_BASE_URL!,
  redisUrl: process.env.REDIS_URL, // optional
})

// Mount SSO routes
app.use('/auth', ssoRouter)

// Protected route example
app.get('/api/profile', 
  auth(AUTH_TYPE.ACCESS), 
  (req, res) => {
    const { tokenPayload } = req as RequestWithAccessToken
    res.json({ user: tokenPayload })
  }
)

app.listen(3000, () => {
  console.log('Server running on port 3000')
})
```

### Authentication Flow

1. **Login**: User mengirim email dan password ke `POST /auth/login`
   - Response: `authToken` dan `redirectUri`
2. **Callback**: Kirim `authToken` ke `POST /auth/callback`
   - Response: `idToken`, `authToken`, `accessToken`, `refreshToken`
3. **Use Access Token**: Gunakan `accessToken` untuk mengakses protected endpoints
4. **Refresh**: Gunakan `refreshToken` untuk mendapatkan `accessToken` baru ketika expired

## API Reference

### Configuration

#### `config(options: SSOConfig): Router`

Inisialisasi SSO configuration dan return Express Router dengan SSO routes.

**Parameters:**

| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `clientId` | `string` | Yes | - | SSO client ID |
| `clientSecret` | `string` | Yes | - | SSO client secret |
| `baseUrl` | `string` | Yes | - | SSO server base URL |
| `issuer` | `string` | No | `baseUrl` | JWT issuer untuk validation |
| `redisUrl` | `string` | No | `undefined` | Redis connection URL (optional) |
| `publicKeyUrl` | `string` | No | `${baseUrl}/auth/public-key` | Public key endpoint URL |
| `publicKeyCacheTTL` | `number` | No | `3600` | Public key cache TTL in seconds |

**Example:**

```typescript
const ssoRouter = config({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  baseUrl: 'https://sso.example.com',
  issuer: 'https://sso.example.com',
  redisUrl: 'redis://localhost:6379',
  publicKeyCacheTTL: 7200,
})

app.use('/auth', ssoRouter)
```

### Middleware

#### `auth(type: AUTH_TYPE): RequestHandler`

Authentication middleware untuk protect routes dengan JWT verification.

**Token Types:**

- `AUTH_TYPE.AUTH` - Authentication token
- `AUTH_TYPE.ACCESS` - Access token (default)
- `AUTH_TYPE.REFRESH` - Refresh token

**Example:**

```typescript
import { auth, AUTH_TYPE, RequestWithAccessToken } from '@starasia/sso-api'

// Protect with ACCESS token (default)
app.get('/api/protected', 
  auth(AUTH_TYPE.ACCESS), 
  (req, res) => {
    const { tokenPayload } = req as RequestWithAccessToken
    res.json({ user: tokenPayload })
  }
)

// Protect with AUTH token
app.post('/api/admin', 
  auth(AUTH_TYPE.AUTH), 
  (req, res) => {
    // Only accessible with AUTH token
    res.json({ message: 'Admin access granted' })
  }
)
```

### Routes

Package ini otomatis menyediakan authentication endpoints:

#### `POST /auth/login`

Login dengan email dan password. Mengembalikan authToken dan redirectUri.

**Request Body:**
```json
{
  "email": "user@example.com",
  "password": "password123"
}
```

**Response:**
```json
{
  "success": true,
  "data": {
    "authToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "redirectUri": "https://app.example.com/callback"
  },
  "message": "Login successful"
}
```

#### `POST /auth/callback`

Handle callback dan exchange authToken untuk mendapatkan semua tokens (idToken, authToken, accessToken, refreshToken).

**Request Body:**
```json
{
  "authToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

**Response:**
```json
{
  "success": true,
  "data": {
    "idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "authToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expiresIn": 3600
  },
  "message": "Callback processed successfully"
}
```

#### `POST /auth/refresh`

Refresh access token menggunakan refresh token.

**Request Body:**
```json
{
  "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

**Response:**
```json
{
  "success": true,
  "data": {
    "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expiresIn": 3600
  }
}
```

#### `POST /auth/logout`

Logout dan revoke tokens (requires ACCESS token).

**Headers:**
```
Authorization: Bearer <access_token>
```

**Response:**
```json
{
  "success": true,
  "data": null,
  "message": "Logout successful"
}
```

#### `GET /auth/userinfo`

Get current user information (requires ACCESS token).

**Headers:**
```
Authorization: Bearer <access_token>
```

**Response:**
```json
{
  "success": true,
  "data": {
    "sub": "user-id-123",
    "email": "user@example.com",
    "name": "John Doe"
  }
}
```

#### `GET /auth/verify`

Verify token validity (requires ACCESS token).

**Headers:**
```
Authorization: Bearer <access_token>
```

**Response:**
```json
{
  "success": true,
  "data": {
    "valid": true,
    "payload": {
      "sub": "user-id-123",
      "email": "user@example.com",
      "type": "ACCESS",
      "iat": 1234567890,
      "exp": 1234571490
    }
  }
}
```

#### `GET /auth/health`

Health check endpoint.

**Response:**
```json
{
  "success": true,
  "data": {
    "status": "healthy",
    "timestamp": "2025-01-01T00:00:00.000Z"
  }
}
```

## TypeScript Support

Package ini fully typed dengan TypeScript:

```typescript
import { 
  config, 
  auth, 
  AUTH_TYPE,
  SSOConfig,
  RequestWithAccessToken,
  PayloadAccessToken 
} from '@starasia/sso-api'

// Configuration types
const ssoConfig: SSOConfig = {
  clientId: 'client-id',
  clientSecret: 'client-secret',
  baseUrl: 'https://sso.example.com',
}

// Extended Request types
app.get('/profile', auth(AUTH_TYPE.ACCESS), (req, res) => {
  const { tokenPayload } = req as RequestWithAccessToken
  // tokenPayload is fully typed as PayloadAccessToken
  console.log(tokenPayload.sub, tokenPayload.email)
})
```

## Error Handling

Package ini menggunakan standardized error responses:

```json
{
  "success": false,
  "error": {
    "message": "Invalid or expired token",
    "statusCode": 401
  }
}
```

**Error Codes:**

| Status Code | Error Message | Description |
|-------------|---------------|-------------|
| 400 | Invalid SSO configuration | Configuration tidak valid |
| 400 | Authorization token is required | Token tidak ditemukan |
| 400 | Invalid token format | Format token salah |
| 401 | Invalid or expired token | Token invalid atau expired |
| 401 | Token has expired | Token sudah expired |
| 403 | Wrong token type | Token type tidak sesuai endpoint |
| 403 | Token has been revoked | Token sudah di-revoke |
| 500 | Failed to connect to SSO server | Koneksi ke SSO server gagal |
| 500 | Failed to fetch public key | Gagal fetch public key |

## Advanced Usage

### Custom Error Handler

```typescript
import { errorHandler, notFoundHandler } from '@starasia/sso-api'

// Use custom error handler
app.use(errorHandler)
app.use(notFoundHandler)
```

### Using Utilities

```typescript
import { 
  generateChallengeCode, 
  successResponse, 
  errorResponse 
} from '@starasia/sso-api'

// Generate challenge code for OAuth flows
const challenge = generateChallengeCode(32)

// Use response helpers
app.get('/custom', (req, res) => {
  return successResponse(res, { data: 'value' }, 'Success message')
})
```

### Multiple Token Type Protection

```typescript
import { auth, AUTH_TYPE } from '@starasia/sso-api'

// Different protection levels
app.get('/public', (req, res) => {
  res.json({ message: 'Public endpoint' })
})

app.get('/user', auth(AUTH_TYPE.ACCESS), (req, res) => {
  res.json({ message: 'User endpoint' })
})

app.get('/admin', auth(AUTH_TYPE.AUTH), (req, res) => {
  res.json({ message: 'Admin endpoint' })
})
```

## Environment Variables

Example `.env` file:

```env
SSO_CLIENT_ID=your-client-id
SSO_CLIENT_SECRET=your-client-secret
SSO_BASE_URL=https://sso.example.com
REDIS_URL=redis://localhost:6379
```

## Requirements

- Node.js >= 18.0.0
- Express.js >= 4.18.0

## Dependencies

- `axios` - HTTP client untuk SSO server communication
- `jsonwebtoken` - JWT verification dengan RS256
- `ioredis` - Redis client untuk token caching (optional)

## Contributing

Contributions are welcome! Please follow these guidelines:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

### Development Setup

```bash
# Clone repository
git clone https://github.com/starasia/sso-api.git
cd sso-api

# Install dependencies
npm install

# Run development mode
npm run dev

# Run tests
npm test

# Build package
npm run build

# Lint code
npm run lint

# Format code
npm run format
```

## License

MIT © Starasia

## Examples

Lihat folder `examples/` untuk contoh penggunaan lengkap:

- `examples/basic-usage.ts` - Contoh implementasi basic dengan Express.js
- `examples/authentication-flow.md` - Dokumentasi lengkap authentication flow dengan curl examples dan frontend integration

## Support

For issues and questions, please open an issue on [GitHub](https://github.com/starasia/sso-api/issues).