@authaz/next 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/CLAUDE.md +118 -0
- package/README.md +209 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +30 -0
- package/docs/ENVIRONMENT-CONFIG.md +171 -0
- package/docs/README-AXIOS-INSTANCE.md +276 -0
- package/docs/REFACTORING.md +163 -0
- package/docs/STATUS-CODES.md +141 -0
- package/jest.config.js +25 -0
- package/package.json +52 -0
- package/src/index.tsx +34 -0
- package/tsconfig.json +12 -0
- package/tsdown.config.ts +21 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Todas as mudanças notáveis deste projeto serão documentadas neste arquivo.
|
|
4
|
+
|
|
5
|
+
## [1.1.0] - 2024-01-XX
|
|
6
|
+
|
|
7
|
+
### ✨ Adicionado
|
|
8
|
+
- **Nova API de Configuração**: Introduzida configuração baseada em objeto para melhor organização
|
|
9
|
+
- **Validação de Configuração**: Validação automática de campos obrigatórios
|
|
10
|
+
- **Status Específicos**: Cada fluxo agora tem status específicos para diferentes cenários
|
|
11
|
+
- **Arquivo de Configuração**: Novo módulo `config.ts` para centralizar configurações
|
|
12
|
+
|
|
13
|
+
### 🔧 Melhorado
|
|
14
|
+
- **Nomenclatura Padronizada**:
|
|
15
|
+
- `SignupConfigureMfa` → `signupConfigureMfa`
|
|
16
|
+
- `SignupVerifyMfa` → `signupVerifyMfa`
|
|
17
|
+
- `acessTokenMfa` → `accessTokenMfa`
|
|
18
|
+
- **Estrutura de Respostas**: Status específicos para cada tipo de operação com cenários detalhados
|
|
19
|
+
- **Tipos de Usuário**: Unificação dos tipos de usuário com interface base `User`
|
|
20
|
+
- **URLs Dinâmicas**: Remoção de URLs hardcoded, agora configuráveis
|
|
21
|
+
|
|
22
|
+
### 🏗️ Refatorado
|
|
23
|
+
- **Construtor da ApiService**: Suporte para objeto de configuração + compatibilidade legacy
|
|
24
|
+
- **Tipos de Token**: Melhor organização e reutilização de tipos de token
|
|
25
|
+
- **Tipos de Erro**: Padronização de tipos de erro comuns
|
|
26
|
+
|
|
27
|
+
### 📚 Documentação
|
|
28
|
+
- **README Atualizado**: Exemplos de configuração moderna e legacy
|
|
29
|
+
- **Tipos Documentados**: Melhor documentação dos tipos disponíveis
|
|
30
|
+
- **Exemplos de Uso**: Adicionados exemplos com nova API de configuração
|
|
31
|
+
|
|
32
|
+
### ⚡ Compatibilidade
|
|
33
|
+
- **Backward Compatible**: Mantida compatibilidade com API legacy
|
|
34
|
+
- **Migração Suave**: Possibilidade de migrar gradualmente para nova API
|
|
35
|
+
|
|
36
|
+
## Exemplo de Migração
|
|
37
|
+
|
|
38
|
+
### Antes (Legacy)
|
|
39
|
+
```typescript
|
|
40
|
+
const client = new ApiService(
|
|
41
|
+
'https://api.authaz.com',
|
|
42
|
+
'client-id',
|
|
43
|
+
'client-secret',
|
|
44
|
+
'auth-pool-id',
|
|
45
|
+
'org-id',
|
|
46
|
+
'redirect-uri'
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Depois (Moderno)
|
|
51
|
+
```typescript
|
|
52
|
+
const client = new ApiService({
|
|
53
|
+
baseUrl: 'https://api.authaz.com',
|
|
54
|
+
clientId: 'client-id',
|
|
55
|
+
clientSecret: 'client-secret',
|
|
56
|
+
authPoolId: 'auth-pool-id',
|
|
57
|
+
organizationId: 'org-id',
|
|
58
|
+
redirectUri: 'redirect-uri'
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Breaking Changes
|
|
63
|
+
|
|
64
|
+
Nenhuma breaking change nesta versão. Todas as mudanças mantêm compatibilidade com versões anteriores.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is the **Authaz SDK for JavaScript/TypeScript**, providing authentication and user management for the Authaz platform. The SDK supports Universal Login flow with OAuth2/OIDC, PKCE, and secure session management.
|
|
8
|
+
|
|
9
|
+
## Development Commands
|
|
10
|
+
|
|
11
|
+
### Building
|
|
12
|
+
```bash
|
|
13
|
+
npm run build # Build using tsdown (outputs to dist/)
|
|
14
|
+
npm run prepare # Build script (runs on npm install)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Testing
|
|
18
|
+
```bash
|
|
19
|
+
npm test # Run Jest tests
|
|
20
|
+
npm run test:watch # Run tests in watch mode
|
|
21
|
+
npm run test:coverage # Run tests with coverage report (80% threshold)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Linting
|
|
25
|
+
```bash
|
|
26
|
+
npm run lint # Check code with ESLint
|
|
27
|
+
npm run lint:fix # Fix linting issues automatically
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Releasing
|
|
31
|
+
```bash
|
|
32
|
+
npm run release # Build and create patch release
|
|
33
|
+
npm run release:patch # Bump patch version and push with tags
|
|
34
|
+
npm run release:minor # Bump minor version and push with tags
|
|
35
|
+
npm run release:major # Bump major version and push with tags
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Architecture Overview
|
|
39
|
+
|
|
40
|
+
### Core Service Structure
|
|
41
|
+
|
|
42
|
+
The SDK follows a **layered service architecture**:
|
|
43
|
+
|
|
44
|
+
1. **ApiService** (`src/api.ts`) - Main entry point that orchestrates child services
|
|
45
|
+
- Initializes configuration via `createConfig()`
|
|
46
|
+
- Delegates to specialized services: `UserApiService`, `UniversalLoginApiService`
|
|
47
|
+
- Provides high-level methods: `getMe()`, `getUniversalLoginUrl()`, `getUniversalLoginAccessToken()`
|
|
48
|
+
|
|
49
|
+
2. **BaseApiService** (`src/shared/base-api.ts`) - Abstract base class for all API services
|
|
50
|
+
- Manages authentication tokens (API token, CSRF token, Session token)
|
|
51
|
+
- Initializes on construction: fetches CSRF token → gets API token (client credentials grant)
|
|
52
|
+
- Updates Axios instances with tokens via `updateAxiosTokens()`
|
|
53
|
+
- Provides two Axios instances: `apiAxiosInstance` (for API calls) and `universalLoginAxiosInstance` (for OAuth flow)
|
|
54
|
+
|
|
55
|
+
3. **Specialized Services** (extend BaseApiService)
|
|
56
|
+
- `UserApiService` (`src/general/user/api.ts`) - User profile operations
|
|
57
|
+
- `UniversalLoginApiService` (`src/flows/universal-login/api.ts`) - OAuth2/OIDC Universal Login flow
|
|
58
|
+
|
|
59
|
+
### HTTP Client Layer
|
|
60
|
+
|
|
61
|
+
**AuthazAxiosInstance** (`src/shared/axios-instance.ts`) - Wraps Axios with automatic token injection
|
|
62
|
+
- Request interceptor adds: `X-Client-Authorization`, `X-CSRF-Token`, `X-Session-Token` headers
|
|
63
|
+
- Tokens are updated via `updateTokens()` method
|
|
64
|
+
- All HTTP methods (get, post, put, delete, patch) return typed responses
|
|
65
|
+
|
|
66
|
+
### Configuration System
|
|
67
|
+
|
|
68
|
+
**Config** (`src/config.ts`) - Environment-aware configuration
|
|
69
|
+
- Detects production vs development via `NODE_ENV`
|
|
70
|
+
- Production defaults: `api.authaz.com`, `authaz.com`
|
|
71
|
+
- Development defaults: `localhost:5138`
|
|
72
|
+
- Validates required fields: `clientId`, `clientSecret`, `organizationId`, `redirectUri`, `tenantId`
|
|
73
|
+
|
|
74
|
+
### Type System
|
|
75
|
+
|
|
76
|
+
The SDK uses a **dual-type approach**:
|
|
77
|
+
|
|
78
|
+
1. **Raw HTTP Types** (`src/http-types.ts`) - Direct server responses (e.g., `RawApiTokenResponse`, `RawUserProfileResponse`)
|
|
79
|
+
2. **Processed Types** (`src/types.ts` + flow-specific types) - SDK-transformed responses with status codes
|
|
80
|
+
|
|
81
|
+
Exported via namespace: `import type * as HttpTypes from "@authaz/sdk"`
|
|
82
|
+
|
|
83
|
+
### Universal Login Flow
|
|
84
|
+
|
|
85
|
+
OAuth2/OIDC with PKCE implementation (`src/flows/universal-login/api.ts`):
|
|
86
|
+
1. `redirectToLogin()` - Generates PKCE challenge, state parameter, and authorization URL
|
|
87
|
+
2. `getUniversalLoginAccessToken()` - Exchanges authorization code for access/refresh tokens
|
|
88
|
+
3. Uses Web Crypto API for SHA-256 hashing and secure random generation
|
|
89
|
+
|
|
90
|
+
## Key Implementation Details
|
|
91
|
+
|
|
92
|
+
### Token Management
|
|
93
|
+
- All services extend `BaseApiService` which handles 3-token system: API token (client credentials), CSRF token, Session token
|
|
94
|
+
- Tokens are automatically injected into requests via Axios interceptors
|
|
95
|
+
- Base service constructor chains: get CSRF → get API token → update Axios instances
|
|
96
|
+
|
|
97
|
+
### Build Configuration
|
|
98
|
+
- Uses `tsdown` for bundling (not tsc)
|
|
99
|
+
- Output: ESM format only, with TypeScript declarations
|
|
100
|
+
- External dependencies: Next.js, React, routing libraries (not bundled)
|
|
101
|
+
- Tree-shaking enabled for optimal bundle size
|
|
102
|
+
|
|
103
|
+
### Testing Setup
|
|
104
|
+
- Jest with ts-jest preset
|
|
105
|
+
- 80% coverage threshold required (branches, functions, lines, statements)
|
|
106
|
+
- Test files: `**/__tests__/**/*.ts` or `**/*.{spec,test}.ts`
|
|
107
|
+
|
|
108
|
+
### Peer Dependencies
|
|
109
|
+
- Next.js >=13.0.0
|
|
110
|
+
- nookies >=2.5.2 (for cookie management in Next.js)
|
|
111
|
+
|
|
112
|
+
## Important Notes
|
|
113
|
+
|
|
114
|
+
- The SDK is designed for Next.js applications (see peer dependencies)
|
|
115
|
+
- Environment detection relies on `process.env.NODE_ENV`
|
|
116
|
+
- All API calls use `withCredentials: true` for cookie handling
|
|
117
|
+
- PKCE implementation uses `S256` method (SHA-256)
|
|
118
|
+
- Session management utilities are exported but implementation is in `src/utils/session.ts`
|
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# @authaz/next
|
|
2
|
+
|
|
3
|
+
Next.js integration for Authaz authentication. Provides server-side session management with cookie-based authentication for Next.js applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @authaz/next @authaz/sdk
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @authaz/next @authaz/sdk
|
|
11
|
+
# or
|
|
12
|
+
yarn add @authaz/next @authaz/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Peer Dependencies
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install next@">=15" react@">=17" @authaz/sdk@workspace:*
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **Server-Side Session Management** - Secure cookie-based authentication
|
|
24
|
+
- **Next.js 15+ Support** - Built for the latest Next.js features
|
|
25
|
+
- **Type-Safe** - Full TypeScript support
|
|
26
|
+
- **Cookie Integration** - Uses Next.js cookies API for secure token storage
|
|
27
|
+
- **Debug Mode** - Optional debug logging for development
|
|
28
|
+
- **Zero Configuration** - Works with sensible defaults
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Configure Authaz SDK
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// lib/authaz.ts
|
|
36
|
+
import { authazNext } from "@authaz/next";
|
|
37
|
+
|
|
38
|
+
export const authaz = authazNext(
|
|
39
|
+
{
|
|
40
|
+
clientId: process.env.AUTHAZ_CLIENT_ID!,
|
|
41
|
+
clientSecret: process.env.AUTHAZ_CLIENT_SECRET!,
|
|
42
|
+
tenantId: process.env.AUTHAZ_TENANT_ID!,
|
|
43
|
+
organizationId: process.env.AUTHAZ_ORG_ID!,
|
|
44
|
+
redirectUri: process.env.AUTHAZ_REDIRECT_URI!,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
debug: process.env.NODE_ENV === "development",
|
|
48
|
+
cookies: {
|
|
49
|
+
accessToken: "accessToken", // optional
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Use in Server Components
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
// app/dashboard/page.tsx
|
|
59
|
+
import { authaz } from "@/lib/authaz";
|
|
60
|
+
import { redirect } from "next/navigation";
|
|
61
|
+
|
|
62
|
+
export default async function DashboardPage() {
|
|
63
|
+
const user = await authaz.getUserSession();
|
|
64
|
+
|
|
65
|
+
if (!user) {
|
|
66
|
+
redirect("/login");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div>
|
|
71
|
+
<h1>Welcome, {user.name}</h1>
|
|
72
|
+
<p>Email: {user.email}</p>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 3. Use in API Routes
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// app/api/user/route.ts
|
|
82
|
+
import { authaz } from "@/lib/authaz";
|
|
83
|
+
import { NextResponse } from "next/server";
|
|
84
|
+
|
|
85
|
+
export async function GET() {
|
|
86
|
+
const user = await authaz.getUserSession();
|
|
87
|
+
|
|
88
|
+
if (!user) {
|
|
89
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return NextResponse.json({ user });
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API Reference
|
|
97
|
+
|
|
98
|
+
### `authazNext(config, options?)`
|
|
99
|
+
|
|
100
|
+
Creates an Authaz instance configured for Next.js.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// SDK Configuration (required)
|
|
104
|
+
config: {
|
|
105
|
+
clientId: string;
|
|
106
|
+
clientSecret: string;
|
|
107
|
+
tenantId: string;
|
|
108
|
+
organizationId: string;
|
|
109
|
+
redirectUri?: string;
|
|
110
|
+
authPoolId?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// SDK Options (optional)
|
|
114
|
+
options?: {
|
|
115
|
+
debug?: boolean;
|
|
116
|
+
cookies?: {
|
|
117
|
+
accessToken?: string; // Default: "accessToken"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
{
|
|
126
|
+
getUserSession: () => Promise<UserProfile | null>
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `getUserSession()`
|
|
131
|
+
|
|
132
|
+
Retrieves the authenticated user's session from cookies.
|
|
133
|
+
|
|
134
|
+
Returns `UserProfile | null`:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
interface UserProfile {
|
|
138
|
+
id: string;
|
|
139
|
+
email: string;
|
|
140
|
+
name: string;
|
|
141
|
+
role?: string;
|
|
142
|
+
createdAt?: string;
|
|
143
|
+
updatedAt?: string;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Environment Variables
|
|
148
|
+
|
|
149
|
+
Create a `.env.local` file:
|
|
150
|
+
|
|
151
|
+
```env
|
|
152
|
+
AUTHAZ_CLIENT_ID=your_client_id
|
|
153
|
+
AUTHAZ_CLIENT_SECRET=your_client_secret
|
|
154
|
+
AUTHAZ_TENANT_ID=your_tenant_id
|
|
155
|
+
AUTHAZ_ORG_ID=your_organization_id
|
|
156
|
+
AUTHAZ_REDIRECT_URI=http://localhost:3000/callback
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Middleware Protection
|
|
160
|
+
|
|
161
|
+
Protect multiple routes with Next.js middleware:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// middleware.ts
|
|
165
|
+
import { NextResponse } from "next/server";
|
|
166
|
+
import type { NextRequest } from "next/server";
|
|
167
|
+
import { authaz } from "@/lib/authaz";
|
|
168
|
+
|
|
169
|
+
export async function middleware(request: NextRequest) {
|
|
170
|
+
const user = await authaz.getUserSession();
|
|
171
|
+
|
|
172
|
+
if (request.nextUrl.pathname.startsWith("/dashboard") && !user) {
|
|
173
|
+
return NextResponse.redirect(new URL("/login", request.url));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return NextResponse.next();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const config = {
|
|
180
|
+
matcher: ["/dashboard/:path*"],
|
|
181
|
+
};
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Security Best Practices
|
|
185
|
+
|
|
186
|
+
1. **Use HTTP-Only Cookies** - Always set `httpOnly: true`
|
|
187
|
+
2. **Enable Secure Flag in Production** - Set `secure: true` when `NODE_ENV === "production"`
|
|
188
|
+
3. **Set SameSite** - Use `sameSite: "lax"` or `"strict"`
|
|
189
|
+
4. **Never Expose Client Secret** - Only use this package in server-side code
|
|
190
|
+
5. **Implement Token Refresh** - Refresh tokens before expiration
|
|
191
|
+
|
|
192
|
+
## TypeScript Support
|
|
193
|
+
|
|
194
|
+
Full TypeScript support with type inference:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import type { UserProfile } from "@authaz/sdk";
|
|
198
|
+
|
|
199
|
+
const user: UserProfile | null = await authaz.getUserSession();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
|
205
|
+
|
|
206
|
+
## Support
|
|
207
|
+
|
|
208
|
+
- GitHub: https://github.com/Authaz/authaz-sdk-js
|
|
209
|
+
- Documentation: https://www.authaz.io/docs
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as _authaz_sdk0 from "@authaz/sdk";
|
|
2
|
+
import { UserProfile, authaz } from "@authaz/sdk";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
type SdkConfig = {
|
|
6
|
+
debug?: boolean;
|
|
7
|
+
cookies?: {
|
|
8
|
+
accessToken?: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
type AuthazArgs = Parameters<typeof authaz>[0];
|
|
12
|
+
declare const authazNext: (config: AuthazArgs, args?: SdkConfig) => {
|
|
13
|
+
getUserSession: () => Promise<UserProfile | null>;
|
|
14
|
+
sdk: _authaz_sdk0.ApiService;
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { authazNext };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { authaz, validateToken } from "@authaz/sdk";
|
|
2
|
+
import { cookies } from "next/headers";
|
|
3
|
+
|
|
4
|
+
//#region src/index.tsx
|
|
5
|
+
const authazNext = (config, args) => {
|
|
6
|
+
const sdk = authaz(config);
|
|
7
|
+
const isDebug = args?.debug || false;
|
|
8
|
+
const getUserSession = (args$1) => {
|
|
9
|
+
const accessTokenCookie = args$1?.cookies?.accessToken || "accessToken";
|
|
10
|
+
return async () => {
|
|
11
|
+
try {
|
|
12
|
+
const accessToken = (await cookies()).get(accessTokenCookie)?.value;
|
|
13
|
+
if (!accessToken || !validateToken(accessToken)) return null;
|
|
14
|
+
const response = await sdk.getMe(accessToken);
|
|
15
|
+
if (response.status !== "success") return null;
|
|
16
|
+
return response.user;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (isDebug) console.error("[authaz-sdk] Error on get user session", error);
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
getUserSession: getUserSession(args),
|
|
25
|
+
sdk
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { authazNext };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Environment Configuration - Authaz SDK
|
|
2
|
+
|
|
3
|
+
The SDK now automatically detects the environment (development or production) and applies the appropriate configurations.
|
|
4
|
+
|
|
5
|
+
## 🔧 How It Works
|
|
6
|
+
|
|
7
|
+
The `DEFAULT_CONFIG` now reads from environment variables first, with fallback based on `NODE_ENV`:
|
|
8
|
+
|
|
9
|
+
### 📋 Environment Variables (Priority)
|
|
10
|
+
- `AUTHAZ_API_URL` - API URL
|
|
11
|
+
- `AUTHAZ_DOMAIN` - Authaz Domain
|
|
12
|
+
- `AUTHAZ_UNIVERSAL_LOGIN_URL` - Universal Login URL
|
|
13
|
+
|
|
14
|
+
### 🏠 Development (Fallback)
|
|
15
|
+
- **When**: `NODE_ENV !== 'production'` and environment variables are not defined
|
|
16
|
+
- **URLs**:
|
|
17
|
+
- `baseUrl`: `http://localhost:5138/api`
|
|
18
|
+
- `universalLoginUrl`: `http://localhost:5138`
|
|
19
|
+
- `authazDomain`: `http://localhost:5138`
|
|
20
|
+
|
|
21
|
+
### 🚀 Production (Fallback)
|
|
22
|
+
- **When**: `NODE_ENV === 'production'` and environment variables are not defined
|
|
23
|
+
- **URLs**:
|
|
24
|
+
- `baseUrl`: `https://api.authaz.com`
|
|
25
|
+
- `universalLoginUrl`: `https://authaz.com`
|
|
26
|
+
- `authazDomain`: `https://authaz.com`
|
|
27
|
+
|
|
28
|
+
## 💻 Practical Usage
|
|
29
|
+
|
|
30
|
+
### Automatic Configuration (Recommended)
|
|
31
|
+
```typescript
|
|
32
|
+
import { ApiService } from 'authaz-sdk-js'
|
|
33
|
+
|
|
34
|
+
// URLs are automatically detected based on environment
|
|
35
|
+
const authazClient = new ApiService({
|
|
36
|
+
clientId: 'your-client-id',
|
|
37
|
+
clientSecret: 'your-client-secret',
|
|
38
|
+
authPoolId: 'your-auth-pool-id',
|
|
39
|
+
organizationId: 'your-organization-id',
|
|
40
|
+
redirectUri: 'your-redirect-uri'
|
|
41
|
+
// baseUrl, authazDomain and universalLoginUrl are set automatically
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Manual Override (Optional)
|
|
46
|
+
```typescript
|
|
47
|
+
// You can still override URLs if needed
|
|
48
|
+
const authazClient = new ApiService({
|
|
49
|
+
clientId: 'your-client-id',
|
|
50
|
+
clientSecret: 'your-client-secret',
|
|
51
|
+
authPoolId: 'your-auth-pool-id',
|
|
52
|
+
organizationId: 'your-organization-id',
|
|
53
|
+
redirectUri: 'your-redirect-uri',
|
|
54
|
+
baseUrl: 'https://api-custom.authaz.com', // Manual override
|
|
55
|
+
authazDomain: 'https://custom.authaz.com'
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 🌍 Environment Detection
|
|
60
|
+
|
|
61
|
+
### Node.js (Backend)
|
|
62
|
+
```javascript
|
|
63
|
+
// Development
|
|
64
|
+
process.env.NODE_ENV = 'development' // or undefined
|
|
65
|
+
|
|
66
|
+
// Production
|
|
67
|
+
process.env.NODE_ENV = 'production'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Browser (Frontend)
|
|
71
|
+
```javascript
|
|
72
|
+
// Development
|
|
73
|
+
window.location.hostname = 'localhost' // or '127.0.0.1'
|
|
74
|
+
|
|
75
|
+
// Production
|
|
76
|
+
window.location.hostname = 'myapp.com' // any domain that is not localhost
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 🔍 Debug
|
|
80
|
+
|
|
81
|
+
To verify which environment was detected, you can check in the console:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { DEFAULT_CONFIG } from 'authaz-sdk-js'
|
|
85
|
+
|
|
86
|
+
console.log('Detected configuration:', DEFAULT_CONFIG)
|
|
87
|
+
// Development: { baseUrl: 'http://localhost:5138/api', ... }
|
|
88
|
+
// Production: { baseUrl: 'https://api.authaz.com', ... }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 📝 Complete Example
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// authaz.config.ts
|
|
95
|
+
import { ApiService } from 'authaz-sdk-js'
|
|
96
|
+
|
|
97
|
+
export const createAuthazClient = () => {
|
|
98
|
+
return new ApiService({
|
|
99
|
+
clientId: process.env.NEXT_PUBLIC_CLIENT_ID!,
|
|
100
|
+
clientSecret: process.env.CLIENT_SECRET!,
|
|
101
|
+
authPoolId: process.env.NEXT_PUBLIC_AUTH_POOL_ID!,
|
|
102
|
+
organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!,
|
|
103
|
+
redirectUri: process.env.NEXT_PUBLIC_REDIRECT_URI!
|
|
104
|
+
// URLs are automatically set based on environment
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// In development: will use localhost:5138
|
|
109
|
+
// In production: will use api.authaz.com
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## ⚙️ Environment Variables
|
|
113
|
+
|
|
114
|
+
### Development (.env.local)
|
|
115
|
+
```env
|
|
116
|
+
NODE_ENV=development
|
|
117
|
+
|
|
118
|
+
# Authaz URLs (optional - uses localhost if not defined)
|
|
119
|
+
AUTHAZ_API_URL=http://localhost:5138/api
|
|
120
|
+
AUTHAZ_DOMAIN=http://localhost:5138
|
|
121
|
+
AUTHAZ_UNIVERSAL_LOGIN_URL=http://localhost:5138
|
|
122
|
+
|
|
123
|
+
# Credentials
|
|
124
|
+
NEXT_PUBLIC_CLIENT_ID=dev-client-id
|
|
125
|
+
CLIENT_SECRET=dev-client-secret
|
|
126
|
+
NEXT_PUBLIC_AUTH_POOL_ID=dev-auth-pool
|
|
127
|
+
NEXT_PUBLIC_ORGANIZATION_ID=dev-org-id
|
|
128
|
+
NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/auth/callback
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Production (.env.production)
|
|
132
|
+
```env
|
|
133
|
+
NODE_ENV=production
|
|
134
|
+
|
|
135
|
+
# Authaz URLs (optional - uses api.authaz.com if not defined)
|
|
136
|
+
AUTHAZ_API_URL=https://api.authaz.com
|
|
137
|
+
AUTHAZ_DOMAIN=https://authaz.com
|
|
138
|
+
AUTHAZ_UNIVERSAL_LOGIN_URL=https://authaz.com
|
|
139
|
+
|
|
140
|
+
# Credentials
|
|
141
|
+
NEXT_PUBLIC_CLIENT_ID=prod-client-id
|
|
142
|
+
CLIENT_SECRET=prod-client-secret
|
|
143
|
+
NEXT_PUBLIC_AUTH_POOL_ID=prod-auth-pool
|
|
144
|
+
NEXT_PUBLIC_ORGANIZATION_ID=prod-org-id
|
|
145
|
+
NEXT_PUBLIC_REDIRECT_URI=https://myapp.com/auth/callback
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Staging/Custom (.env.staging)
|
|
149
|
+
```env
|
|
150
|
+
NODE_ENV=staging
|
|
151
|
+
|
|
152
|
+
# Custom URLs
|
|
153
|
+
AUTHAZ_API_URL=https://api-staging.authaz.com
|
|
154
|
+
AUTHAZ_DOMAIN=https://staging.authaz.com
|
|
155
|
+
AUTHAZ_UNIVERSAL_LOGIN_URL=https://staging.authaz.com
|
|
156
|
+
|
|
157
|
+
# Credentials
|
|
158
|
+
NEXT_PUBLIC_CLIENT_ID=staging-client-id
|
|
159
|
+
CLIENT_SECRET=staging-client-secret
|
|
160
|
+
NEXT_PUBLIC_AUTH_POOL_ID=staging-auth-pool
|
|
161
|
+
NEXT_PUBLIC_ORGANIZATION_ID=staging-org-id
|
|
162
|
+
NEXT_PUBLIC_REDIRECT_URI=https://staging.myapp.com/auth/callback
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## ✅ Advantages
|
|
166
|
+
|
|
167
|
+
1. **Automatic**: No need to manually configure URLs
|
|
168
|
+
2. **Safe**: Prevents using development URLs in production
|
|
169
|
+
3. **Simple**: Just configure credentials, URLs are automatic
|
|
170
|
+
4. **Flexible**: Still allows manual override when needed
|
|
171
|
+
5. **Compatible**: Works in both Node.js and Browser
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Authaz SDK - Axios Instance
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The SDK now uses a custom Axios instance that automatically adds the necessary authentication tokens (`ApiToken` and `CsrfToken`) to the headers of all requests.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
### AuthazAxiosInstance
|
|
10
|
+
|
|
11
|
+
The `AuthazAxiosInstance` class manages an Axios instance with configured interceptors:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { AuthazAxiosInstance } from './shared/axios-instance';
|
|
15
|
+
|
|
16
|
+
// Create an instance with base URL
|
|
17
|
+
const axiosInstance = new AuthazAxiosInstance('https://api.authaz.com');
|
|
18
|
+
|
|
19
|
+
// Update tokens
|
|
20
|
+
axiosInstance.updateTokens(apiToken, csrfToken);
|
|
21
|
+
|
|
22
|
+
// Make requests that automatically include tokens
|
|
23
|
+
const data = await axiosInstance.get('/users');
|
|
24
|
+
const response = await axiosInstance.post('/auth/login', { email, password });
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Automatic Interceptors
|
|
28
|
+
|
|
29
|
+
The Axios instance includes interceptors that:
|
|
30
|
+
|
|
31
|
+
1. **Request Interceptor**: Automatically adds headers:
|
|
32
|
+
- `X-Client-Authorization: Bearer {apiToken}`
|
|
33
|
+
- `X-CSRF-Token: {csrfToken}`
|
|
34
|
+
|
|
35
|
+
2. **Response Interceptor**: Handles errors consistently
|
|
36
|
+
|
|
37
|
+
### Integration with BaseApiService
|
|
38
|
+
|
|
39
|
+
The `BaseApiService` class now includes an instance of `AuthazAxiosInstance`:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
export abstract class BaseApiService {
|
|
43
|
+
protected axiosInstance: AuthazAxiosInstance;
|
|
44
|
+
|
|
45
|
+
constructor(...) {
|
|
46
|
+
this.axiosInstance = new AuthazAxiosInstance(baseUrl);
|
|
47
|
+
// ...
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Method to update tokens in the axios instance
|
|
51
|
+
protected updateAxiosTokens(): void {
|
|
52
|
+
this.axiosInstance.updateTokens(this.ApiToken, this.CsrfToken);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Benefits
|
|
58
|
+
|
|
59
|
+
1. **Automation**: No longer necessary to manually add authentication headers
|
|
60
|
+
2. **Consistency**: All services inherit the same authentication configuration
|
|
61
|
+
3. **Maintainability**: Changes to authentication logic are centralized
|
|
62
|
+
4. **Type Safety**: Full TypeScript support with generic types
|
|
63
|
+
|
|
64
|
+
## Usage in Services
|
|
65
|
+
|
|
66
|
+
### Before (with fetch)
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
const response = await fetch(`${this.baseUrl}/auth/login`, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
'X-Client-Authorization': `Bearer ${apiToken}`,
|
|
74
|
+
'X-CSRF-Token': csrfToken,
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify({ email, password }),
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### After (with axios instance)
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const data = await this.axiosInstance.post<LoginResponse>(
|
|
84
|
+
'/auth/login',
|
|
85
|
+
{ email, password }
|
|
86
|
+
);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Available Methods
|
|
90
|
+
|
|
91
|
+
The Axios instance offers convenient methods:
|
|
92
|
+
|
|
93
|
+
- `get<T>(url, config?)`: GET requests
|
|
94
|
+
- `post<T>(url, data?, config?)`: POST requests
|
|
95
|
+
- `put<T>(url, data?, config?)`: PUT requests
|
|
96
|
+
- `delete<T>(url, config?)`: DELETE requests
|
|
97
|
+
- `patch<T>(url, data?, config?)`: PATCH requests
|
|
98
|
+
|
|
99
|
+
## Token Updates
|
|
100
|
+
|
|
101
|
+
Tokens are automatically updated when:
|
|
102
|
+
|
|
103
|
+
1. CSRF token is obtained via `getCsrfToken()`
|
|
104
|
+
2. API token is obtained via `getApiToken()`
|
|
105
|
+
3. Both are updated in the axios instance via `updateAxiosTokens()`
|
|
106
|
+
|
|
107
|
+
## Error Handling
|
|
108
|
+
|
|
109
|
+
The Axios instance includes consistent error handling:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
try {
|
|
113
|
+
const data = await this.axiosInstance.post('/auth/login', credentials);
|
|
114
|
+
return { status: 'success', data };
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const axiosError = error as { response?: { status: number; data?: unknown } };
|
|
117
|
+
|
|
118
|
+
if (axiosError.response?.status === 401) {
|
|
119
|
+
return { status: 'error', message: 'Unauthorized' };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { status: 'error', message: 'Internal error' };
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Migration
|
|
127
|
+
|
|
128
|
+
To migrate existing services:
|
|
129
|
+
|
|
130
|
+
1. Remove manual `fetch` calls
|
|
131
|
+
2. Replace with calls to `this.axiosInstance`
|
|
132
|
+
3. Remove manual addition of authentication headers
|
|
133
|
+
4. Update error handling to use Axios structure
|
|
134
|
+
|
|
135
|
+
## Complete Example
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
export class MyApiService extends BaseApiService {
|
|
139
|
+
public async getUserData(userId: string): Promise<UserData> {
|
|
140
|
+
try {
|
|
141
|
+
// Ensure tokens are up to date
|
|
142
|
+
await this.getApiToken();
|
|
143
|
+
|
|
144
|
+
// Make request - headers are added automatically
|
|
145
|
+
const data = await this.axiosInstance.get<UserData>(`/users/${userId}`);
|
|
146
|
+
|
|
147
|
+
return data;
|
|
148
|
+
} catch (error: unknown) {
|
|
149
|
+
const axiosError = error as { response?: { status: number } };
|
|
150
|
+
|
|
151
|
+
if (axiosError.response?.status === 404) {
|
|
152
|
+
throw new Error('User not found');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
throw new Error('Error fetching user data');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
# AuthazAxiosInstance - Per-Request Cookies
|
|
162
|
+
|
|
163
|
+
This document explains how to use request-specific cookies in `AuthazAxiosInstance`.
|
|
164
|
+
|
|
165
|
+
## Configuration
|
|
166
|
+
|
|
167
|
+
The `AuthazAxiosInstance` now supports request-specific cookies through the `cookies` property in the configuration.
|
|
168
|
+
|
|
169
|
+
## Interface
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
interface AuthazRequestConfig extends AxiosRequestConfig {
|
|
173
|
+
cookies?: Record<string, string>;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Usage Examples
|
|
178
|
+
|
|
179
|
+
### 1. GET Request with cookies
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const axiosInstance = new AuthazAxiosInstance('https://api.example.com');
|
|
183
|
+
|
|
184
|
+
// GET request with specific cookies
|
|
185
|
+
const response = await axiosInstance.get('/users', {
|
|
186
|
+
cookies: {
|
|
187
|
+
'sessionId': 'abc123',
|
|
188
|
+
'userId': 'user456'
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 2. POST Request with cookies
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// POST request with cookies
|
|
197
|
+
const response = await axiosInstance.post('/login', {
|
|
198
|
+
email: 'user@example.com',
|
|
199
|
+
password: 'password123'
|
|
200
|
+
}, {
|
|
201
|
+
cookies: {
|
|
202
|
+
'sessionId': 'new-session-123',
|
|
203
|
+
'theme': 'dark'
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 3. PUT Request with cookies
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// PUT request with cookies
|
|
212
|
+
const response = await axiosInstance.put('/users/123', {
|
|
213
|
+
name: 'John Doe',
|
|
214
|
+
email: 'john@example.com'
|
|
215
|
+
}, {
|
|
216
|
+
cookies: {
|
|
217
|
+
'sessionId': 'abc123',
|
|
218
|
+
'lastUpdate': Date.now().toString()
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 4. DELETE Request with cookies
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// DELETE request with cookies
|
|
227
|
+
const response = await axiosInstance.delete('/users/123', {
|
|
228
|
+
cookies: {
|
|
229
|
+
'sessionId': 'abc123',
|
|
230
|
+
'action': 'delete'
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 5. PATCH Request with cookies
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// PATCH request with cookies
|
|
239
|
+
const response = await axiosInstance.patch('/users/123', {
|
|
240
|
+
name: 'Jane Doe'
|
|
241
|
+
}, {
|
|
242
|
+
cookies: {
|
|
243
|
+
'sessionId': 'abc123',
|
|
244
|
+
'updateType': 'name'
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Advantages
|
|
250
|
+
|
|
251
|
+
1. **Flexibility**: Each request can have its own cookies
|
|
252
|
+
2. **Isolation**: Cookies from one request don't affect others
|
|
253
|
+
3. **Control**: Allows granular control over cookies per operation
|
|
254
|
+
4. **Security**: Prevents cookie leakage between different contexts
|
|
255
|
+
|
|
256
|
+
## Global Tokens
|
|
257
|
+
|
|
258
|
+
API and CSRF tokens continue to be configured globally:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Configure global tokens
|
|
262
|
+
axiosInstance.updateTokens('api-token-123', 'csrf-token-456');
|
|
263
|
+
|
|
264
|
+
// Or individually
|
|
265
|
+
axiosInstance.updateApiToken('api-token-123');
|
|
266
|
+
axiosInstance.updateCsrfToken('csrf-token-456');
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Interceptor
|
|
270
|
+
|
|
271
|
+
The interceptor automatically:
|
|
272
|
+
1. Adds API token if configured
|
|
273
|
+
2. Adds CSRF token if configured
|
|
274
|
+
3. Adds request-specific cookies if configured
|
|
275
|
+
|
|
276
|
+
All headers are added automatically in each request.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Authaz SDK Refactoring
|
|
2
|
+
|
|
3
|
+
## 📁 New Structure
|
|
4
|
+
|
|
5
|
+
The refactoring organized the SDK by **functional flows**, separating responsibilities and improving maintainability:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
├── flows/ # Specific functionality flows
|
|
10
|
+
│ ├── auth/ # Authentication and MFA
|
|
11
|
+
│ │ ├── api.ts # Methods: login, loginVerifyMfa
|
|
12
|
+
│ │ └── types.ts # LoginResponse, MfaVerifyResponse
|
|
13
|
+
│ ├── password-reset/ # Password recovery
|
|
14
|
+
│ │ ├── api.ts # Methods: requestPasswordReset, verifyPasswordResetCode, etc.
|
|
15
|
+
│ │ └── types.ts # PasswordResetRequestResponse, etc.
|
|
16
|
+
│ ├── signup/ # User registration
|
|
17
|
+
│ │ ├── api.ts # Methods: requestSignup, confirmSignup, etc.
|
|
18
|
+
│ │ └── types.ts # SignupRequestResponse, etc.
|
|
19
|
+
│ └── user/ # User data
|
|
20
|
+
│ ├── api.ts # Methods: getMe
|
|
21
|
+
│ └── types.ts # UserResponse, UserProfile
|
|
22
|
+
├── shared/ # Shared logic and types
|
|
23
|
+
│ ├── base-api.ts # Base class with common authentication
|
|
24
|
+
│ └── types.ts # Base types (tokens, session, etc.)
|
|
25
|
+
├── utils/ # Utilities
|
|
26
|
+
│ ├── auth.ts # Validation functions and helpers
|
|
27
|
+
│ └── session.ts # Session management
|
|
28
|
+
├── api.ts # Main aggregator class
|
|
29
|
+
├── types.ts # Re-exports of all types
|
|
30
|
+
├── http-types.ts # Raw HTTP types (maintained)
|
|
31
|
+
└── index.ts # Main exports
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 🔄 Compatibility
|
|
35
|
+
|
|
36
|
+
The refactoring **maintains full compatibility** with existing code:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Will continue to work exactly the same
|
|
40
|
+
import { ApiService } from 'authaz-sdk-js'
|
|
41
|
+
|
|
42
|
+
const client = new ApiService(clientId, clientSecret, authPoolId, organizationId)
|
|
43
|
+
|
|
44
|
+
// All methods work the same
|
|
45
|
+
const loginResult = await client.login(email, password)
|
|
46
|
+
const userResult = await client.getMe(token)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 🎯 Refactoring Benefits
|
|
50
|
+
|
|
51
|
+
### 1. **Separation of Concerns**
|
|
52
|
+
- Each flow has its own class and types
|
|
53
|
+
- More organized and easier to maintain code
|
|
54
|
+
- Less chance of conflicts between functionalities
|
|
55
|
+
|
|
56
|
+
### 2. **Code Reusability**
|
|
57
|
+
- `BaseApiService` class eliminates duplication
|
|
58
|
+
- Centralized authentication logic
|
|
59
|
+
- Shared configuration across all services
|
|
60
|
+
|
|
61
|
+
### 3. **Better Developer Experience**
|
|
62
|
+
- More specific types per flow
|
|
63
|
+
- More precise IntelliSense
|
|
64
|
+
- Easier debugging and maintenance
|
|
65
|
+
|
|
66
|
+
### 4. **Flexibility**
|
|
67
|
+
- Ability to use individual services
|
|
68
|
+
- Easier unit testing
|
|
69
|
+
- Allows future extensions
|
|
70
|
+
|
|
71
|
+
## 🚀 Advanced Usage (Optional)
|
|
72
|
+
|
|
73
|
+
For specific cases, you can use individual services:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import {
|
|
77
|
+
AuthApiService,
|
|
78
|
+
PasswordResetApiService,
|
|
79
|
+
SignupApiService,
|
|
80
|
+
UserApiService
|
|
81
|
+
} from 'authaz-sdk-js'
|
|
82
|
+
|
|
83
|
+
// Use only the authentication service
|
|
84
|
+
const authService = new AuthApiService(baseUrl, clientId, clientSecret, authPoolId, organizationId)
|
|
85
|
+
const loginResult = await authService.login(email, password)
|
|
86
|
+
|
|
87
|
+
// Or only password recovery
|
|
88
|
+
const passwordService = new PasswordResetApiService(baseUrl, clientId, clientSecret, authPoolId, organizationId)
|
|
89
|
+
const resetResult = await passwordService.requestPasswordReset(email)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 📝 Usage Examples
|
|
93
|
+
|
|
94
|
+
### Complete Authentication
|
|
95
|
+
```typescript
|
|
96
|
+
import { ApiService } from 'authaz-sdk-js'
|
|
97
|
+
|
|
98
|
+
const client = new ApiService(/* configs */)
|
|
99
|
+
|
|
100
|
+
// Normal login
|
|
101
|
+
const loginResult = await client.login('user@example.com', 'password')
|
|
102
|
+
|
|
103
|
+
if (loginResult.status === 'mfa_required') {
|
|
104
|
+
// Verify MFA
|
|
105
|
+
const mfaResult = await client.loginVerifyMfa(
|
|
106
|
+
'user@example.com',
|
|
107
|
+
'123456',
|
|
108
|
+
loginResult.challengeToken.value
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if (mfaResult.status === 'success') {
|
|
112
|
+
// User authenticated
|
|
113
|
+
console.log('Access token:', mfaResult.accessToken.value)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Password Recovery
|
|
119
|
+
```typescript
|
|
120
|
+
// Request reset
|
|
121
|
+
const requestResult = await client.requestPasswordReset('user@example.com')
|
|
122
|
+
|
|
123
|
+
// Verify email code
|
|
124
|
+
const verifyResult = await client.verifyPasswordResetCode('user@example.com', '123456')
|
|
125
|
+
|
|
126
|
+
if (verifyResult.status === 'challenge') {
|
|
127
|
+
// Needs MFA
|
|
128
|
+
const mfaResult = await client.forgotPasswordVerifyMfa(
|
|
129
|
+
'user@example.com',
|
|
130
|
+
'654321',
|
|
131
|
+
verifyResult.challengeToken.value
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Confirm new password
|
|
136
|
+
const confirmResult = await client.confirmPasswordReset(
|
|
137
|
+
token,
|
|
138
|
+
'user@example.com',
|
|
139
|
+
'newPassword123'
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 🔧 Migration (If Needed)
|
|
144
|
+
|
|
145
|
+
If you were importing specific internal files, update:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// ❌ Before (if you were doing this)
|
|
149
|
+
import { validateToken } from 'authaz-sdk-js/src/auth'
|
|
150
|
+
import { getSession } from 'authaz-sdk-js/src/session'
|
|
151
|
+
|
|
152
|
+
// ✅ Now
|
|
153
|
+
import { validateToken } from 'authaz-sdk-js'
|
|
154
|
+
import { getSession } from 'authaz-sdk-js'
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 📊 Impact
|
|
158
|
+
|
|
159
|
+
- **Code removed**: ~400 duplicated lines
|
|
160
|
+
- **Organized files**: 6 → 14 well-structured files
|
|
161
|
+
- **Maintainability**: ↗️ Much better
|
|
162
|
+
- **Performance**: Same (no impact)
|
|
163
|
+
- **Compatibility**: ✅ 100% maintained
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Status Codes - Authaz SDK
|
|
2
|
+
|
|
3
|
+
This document describes all specific statuses for each operation flow in the Authaz SDK.
|
|
4
|
+
|
|
5
|
+
## 🔐 Authentication (Auth)
|
|
6
|
+
|
|
7
|
+
### LoginResponse
|
|
8
|
+
- `success` - Login successful
|
|
9
|
+
- `error` - Generic login error
|
|
10
|
+
- `mfa_required` - MFA is required to complete login
|
|
11
|
+
- `too_many_attempts` - Too many login attempts
|
|
12
|
+
|
|
13
|
+
### MfaVerifyResponse
|
|
14
|
+
- `success` - MFA verified successfully
|
|
15
|
+
- `error` - Generic MFA verification error
|
|
16
|
+
- `invalid_code` - Invalid MFA code
|
|
17
|
+
- `expired` - MFA token expired
|
|
18
|
+
|
|
19
|
+
## 📝 Signup
|
|
20
|
+
|
|
21
|
+
### SignupRequestResponse
|
|
22
|
+
- `success` - Signup request sent successfully
|
|
23
|
+
- `error` - Generic request error
|
|
24
|
+
- `user_already_exists` - User already exists in the system
|
|
25
|
+
- `invalid_email` - Invalid email
|
|
26
|
+
- `password_not_strong_enough` - Password doesn't meet criteria
|
|
27
|
+
|
|
28
|
+
### SignupConfirmResponse
|
|
29
|
+
- `success` - Signup confirmed successfully
|
|
30
|
+
- `requires_mfa` - MFA is required to complete signup
|
|
31
|
+
- `error` - Generic confirmation error
|
|
32
|
+
- `invalid_code` - Invalid confirmation code
|
|
33
|
+
- `expired` - Confirmation code expired
|
|
34
|
+
- `user_exists` - User already exists
|
|
35
|
+
|
|
36
|
+
### SignupConfigureMfaResponse
|
|
37
|
+
- `success` - MFA configured successfully
|
|
38
|
+
- `error` - Generic configuration error
|
|
39
|
+
- `unauthorized` - Invalid access token
|
|
40
|
+
- `mfa_already_configured` - MFA already configured
|
|
41
|
+
|
|
42
|
+
### SignupVerifyMfaResponse
|
|
43
|
+
- `success` - MFA verified successfully during signup
|
|
44
|
+
- `error` - Generic verification error
|
|
45
|
+
- `invalid_code` - Invalid MFA code
|
|
46
|
+
- `expired` - MFA token expired
|
|
47
|
+
- `unauthorized` - Invalid access token
|
|
48
|
+
- `user_exists` - User already exists
|
|
49
|
+
|
|
50
|
+
## 🔑 Password Reset
|
|
51
|
+
|
|
52
|
+
### PasswordResetRequestResponse
|
|
53
|
+
- `success` - Reset request sent successfully
|
|
54
|
+
- `error` - Generic request error
|
|
55
|
+
- `user_not_found` - User not found
|
|
56
|
+
- `too_many_requests` - Too many reset requests
|
|
57
|
+
- `email_not_verified` - Email not verified
|
|
58
|
+
|
|
59
|
+
### PasswordResetVerifyEmailTokenResponse
|
|
60
|
+
- `success` - Code verified successfully
|
|
61
|
+
- `error` - Generic verification error
|
|
62
|
+
- `invalid_code` - Invalid code
|
|
63
|
+
- `challenge` - MFA challenge required
|
|
64
|
+
- `expired` - Code expired
|
|
65
|
+
- `code_already_used` - Code already used
|
|
66
|
+
|
|
67
|
+
### PasswordResetVerifyMfaResponse
|
|
68
|
+
- `success` - MFA verified successfully during reset
|
|
69
|
+
- `error` - Generic MFA verification error
|
|
70
|
+
- `invalid_code` - Invalid MFA code
|
|
71
|
+
- `expired` - MFA token expired
|
|
72
|
+
- `challenge_token_expired` - Challenge token expired
|
|
73
|
+
- `too_many_attempts` - Too many verification attempts
|
|
74
|
+
|
|
75
|
+
### PasswordResetConfirmResponse
|
|
76
|
+
- `success` - Password reset successfully
|
|
77
|
+
- `password_not_strong_enough` - New password doesn't meet criteria
|
|
78
|
+
- `error` - Generic confirmation error
|
|
79
|
+
- `token_expired` - Reset token expired
|
|
80
|
+
- `token_invalid` - Reset token invalid
|
|
81
|
+
- `password_recently_used` - Password was recently used
|
|
82
|
+
|
|
83
|
+
## 👤 User
|
|
84
|
+
|
|
85
|
+
### UserResponse
|
|
86
|
+
- `success` - User data retrieved successfully
|
|
87
|
+
- `error` - Generic data retrieval error
|
|
88
|
+
- `unauthorized` - Invalid access token
|
|
89
|
+
- `token_expired` - Access token expired
|
|
90
|
+
- `user_not_found` - User not found
|
|
91
|
+
|
|
92
|
+
## 🔒 Password Policy
|
|
93
|
+
|
|
94
|
+
### PasswordPolicyResponse
|
|
95
|
+
- `success` - Policy retrieved successfully
|
|
96
|
+
- `error` - Generic policy retrieval error
|
|
97
|
+
- `policy_not_found` - Policy not found
|
|
98
|
+
- `unauthorized` - Unauthorized access
|
|
99
|
+
|
|
100
|
+
## 🔄 Refresh Token
|
|
101
|
+
|
|
102
|
+
### RefreshTokenResponse
|
|
103
|
+
- `success` - Token refreshed successfully
|
|
104
|
+
- `error` - Generic refresh error
|
|
105
|
+
- `invalid_refresh_token` - Invalid refresh token
|
|
106
|
+
- `refresh_token_expired` - Refresh token expired
|
|
107
|
+
- `unauthorized` - Unauthorized access
|
|
108
|
+
|
|
109
|
+
## 💡 Using Status Codes
|
|
110
|
+
|
|
111
|
+
Each status allows specific handling in the frontend:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const loginResult = await authazClient.login(email, password)
|
|
115
|
+
|
|
116
|
+
switch (loginResult.status) {
|
|
117
|
+
case 'success':
|
|
118
|
+
// Redirect to dashboard
|
|
119
|
+
break
|
|
120
|
+
case 'mfa_required':
|
|
121
|
+
// Show MFA screen
|
|
122
|
+
showMfaScreen(loginResult.challengeToken)
|
|
123
|
+
break
|
|
124
|
+
case 'too_many_attempts':
|
|
125
|
+
// Show temporary block message
|
|
126
|
+
showBlockedMessage()
|
|
127
|
+
break
|
|
128
|
+
case 'error':
|
|
129
|
+
// Show generic error
|
|
130
|
+
showError(loginResult.message)
|
|
131
|
+
break
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 🔍 Advantages of Specific Status Codes
|
|
136
|
+
|
|
137
|
+
1. **Precise Handling**: Each scenario can be handled specifically
|
|
138
|
+
2. **Improved UX**: More precise messages and actions for users
|
|
139
|
+
3. **Easier Debugging**: Quick identification of specific issues
|
|
140
|
+
4. **Flexibility**: Adding new statuses without breaking compatibility
|
|
141
|
+
5. **Strong Typing**: TypeScript ensures all cases are handled
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
preset: 'ts-jest',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
roots: ['<rootDir>/src'],
|
|
5
|
+
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
|
|
6
|
+
transform: {
|
|
7
|
+
'^.+\\.ts$': 'ts-jest',
|
|
8
|
+
},
|
|
9
|
+
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
|
|
10
|
+
collectCoverageFrom: [
|
|
11
|
+
'src/**/*.ts',
|
|
12
|
+
'!src/**/*.d.ts',
|
|
13
|
+
'!src/**/__tests__/**',
|
|
14
|
+
],
|
|
15
|
+
coverageDirectory: 'coverage',
|
|
16
|
+
coverageReporters: ['text', 'lcov'],
|
|
17
|
+
coverageThreshold: {
|
|
18
|
+
global: {
|
|
19
|
+
branches: 80,
|
|
20
|
+
functions: 80,
|
|
21
|
+
lines: 80,
|
|
22
|
+
statements: 80,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "@authaz",
|
|
3
|
+
"name": "@authaz/next",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "NextJS authaz SDK",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"module": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
"./*": "./dist/*",
|
|
13
|
+
"./src/*": "./src/*",
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"development": "./src/index.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/Authaz/authaz-sdk-js.git"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsdown",
|
|
27
|
+
"prepare": "npm run build",
|
|
28
|
+
"release:patch": "pnpm version patch && git push origin main --follow-tags",
|
|
29
|
+
"release:minor": "pnpm version minor && git push origin main --follow-tags",
|
|
30
|
+
"release:major": "pnpm version major && git push origin main --follow-tags",
|
|
31
|
+
"release": "pnpm build && pnpm run release:patch"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"auth",
|
|
35
|
+
"authentication",
|
|
36
|
+
"authaz",
|
|
37
|
+
"sdk",
|
|
38
|
+
"react",
|
|
39
|
+
"nextjs",
|
|
40
|
+
"next"
|
|
41
|
+
],
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": ">=17",
|
|
44
|
+
"next": ">=15",
|
|
45
|
+
"@authaz/sdk": "workspace:*"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "24.10.0",
|
|
49
|
+
"tsdown": "0.15.12",
|
|
50
|
+
"typescript": "5.9.3"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { authaz, UserProfile, validateToken } from "@authaz/sdk";
|
|
2
|
+
import { cookies } from "next/headers";
|
|
3
|
+
|
|
4
|
+
type SdkConfig = { debug?: boolean; cookies?: { accessToken?: string } };
|
|
5
|
+
|
|
6
|
+
type AuthazArgs = Parameters<typeof authaz>[0];
|
|
7
|
+
|
|
8
|
+
export const authazNext = (config: AuthazArgs, args?: SdkConfig) => {
|
|
9
|
+
const sdk = authaz(config);
|
|
10
|
+
const isDebug = args?.debug || false;
|
|
11
|
+
|
|
12
|
+
const getUserSession = (args?: SdkConfig) => {
|
|
13
|
+
const accessTokenCookie = args?.cookies?.accessToken || "accessToken";
|
|
14
|
+
return async (): Promise<UserProfile | null> => {
|
|
15
|
+
try {
|
|
16
|
+
const cookieStore = await cookies();
|
|
17
|
+
const accessToken = cookieStore.get(accessTokenCookie)?.value;
|
|
18
|
+
if (!accessToken || !validateToken(accessToken)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const response = await sdk.getMe(accessToken);
|
|
22
|
+
if (response.status !== "success") {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return response.user;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (isDebug) console.error("[authaz-sdk] Error on get user session", error);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return { getUserSession: getUserSession(args), sdk };
|
|
34
|
+
};
|
package/tsconfig.json
ADDED
package/tsdown.config.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig } from "tsdown";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ["./src/index.tsx"],
|
|
5
|
+
clean: true,
|
|
6
|
+
format: ["esm"],
|
|
7
|
+
dts: true,
|
|
8
|
+
outDir: "dist",
|
|
9
|
+
treeshake: true,
|
|
10
|
+
tsconfig: "tsconfig.json",
|
|
11
|
+
external: [
|
|
12
|
+
"next",
|
|
13
|
+
"react",
|
|
14
|
+
"nookies",
|
|
15
|
+
"react-dom",
|
|
16
|
+
"@remix-run/react",
|
|
17
|
+
"react-router-dom",
|
|
18
|
+
"react-router",
|
|
19
|
+
"@tanstack/react-router",
|
|
20
|
+
],
|
|
21
|
+
});
|