@ovixa/auth-client 0.1.0
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 +22 -0
- package/LICENSE +21 -0
- package/README.md +484 -0
- package/dist/chunk-UHRF6AFJ.js +544 -0
- package/dist/chunk-UHRF6AFJ.js.map +1 -0
- package/dist/chunk-Y5NJCTZO.js +143 -0
- package/dist/chunk-Y5NJCTZO.js.map +1 -0
- package/dist/index.d.ts +498 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/astro.d.ts +155 -0
- package/dist/middleware/astro.js +75 -0
- package/dist/middleware/astro.js.map +1 -0
- package/dist/middleware/express.d.ts +197 -0
- package/dist/middleware/express.js +87 -0
- package/dist/middleware/express.js.map +1 -0
- package/dist/types-Czfah64-.d.ts +57 -0
- package/package.json +78 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-01-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial public release
|
|
13
|
+
- `OvixaAuth` client class for interacting with Ovixa Auth service
|
|
14
|
+
- JWT token verification with JWKS caching
|
|
15
|
+
- Authentication methods: `login`, `signup`, `logout`
|
|
16
|
+
- Token management: `refreshToken`, `verifyToken`
|
|
17
|
+
- Password reset flow: `forgotPassword`, `resetPassword`
|
|
18
|
+
- Email verification: `verifyEmail`, `resendVerification`
|
|
19
|
+
- OAuth support: `getOAuthUrl`
|
|
20
|
+
- Astro middleware integration (`@ovixa/auth-client/astro`)
|
|
21
|
+
- Express middleware integration (`@ovixa/auth-client/express`)
|
|
22
|
+
- Full TypeScript support with exported types
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jaehyun Yeom
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
# @ovixa/auth-client
|
|
2
|
+
|
|
3
|
+
Client SDK for the Ovixa Auth service. Provides authentication, token verification, and session management for applications using Ovixa's centralized identity provider.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Email/password authentication (signup, login, password reset)
|
|
8
|
+
- OAuth integration (Google, GitHub)
|
|
9
|
+
- JWT verification with JWKS caching
|
|
10
|
+
- Automatic token refresh
|
|
11
|
+
- Framework integrations (Astro, Express)
|
|
12
|
+
- TypeScript-first with full type definitions
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @ovixa/auth-client
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @ovixa/auth-client
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { OvixaAuth } from '@ovixa/auth-client';
|
|
26
|
+
|
|
27
|
+
const auth = new OvixaAuth({
|
|
28
|
+
authUrl: 'https://auth.ovixa.io',
|
|
29
|
+
realmId: 'your-realm-id',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Sign up a new user
|
|
33
|
+
await auth.signup({
|
|
34
|
+
email: 'user@example.com',
|
|
35
|
+
password: 'SecurePassword123!',
|
|
36
|
+
redirectUri: 'https://yourapp.com/verify-callback',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Log in and get tokens
|
|
40
|
+
const tokens = await auth.login({
|
|
41
|
+
email: 'user@example.com',
|
|
42
|
+
password: 'SecurePassword123!',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Convert to AuthResult for easier handling
|
|
46
|
+
const result = await auth.toAuthResult(tokens);
|
|
47
|
+
console.log('User:', result.user.email);
|
|
48
|
+
console.log('Expires:', result.session.expiresAt);
|
|
49
|
+
|
|
50
|
+
// Verify a JWT token
|
|
51
|
+
const verified = await auth.verifyToken(tokens.access_token);
|
|
52
|
+
console.log('User ID:', verified.payload.sub);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## API Reference
|
|
56
|
+
|
|
57
|
+
### `OvixaAuth`
|
|
58
|
+
|
|
59
|
+
Main client class for interacting with the Ovixa Auth service.
|
|
60
|
+
|
|
61
|
+
#### Constructor
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
new OvixaAuth(config: AuthClientConfig)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Option | Type | Required | Description |
|
|
68
|
+
| -------------- | -------- | -------- | ------------------------------------------- |
|
|
69
|
+
| `authUrl` | `string` | Yes | Base URL of the Ovixa Auth service |
|
|
70
|
+
| `realmId` | `string` | Yes | The realm ID to authenticate against |
|
|
71
|
+
| `clientSecret` | `string` | No | Client secret (for server-side use) |
|
|
72
|
+
| `jwksCacheTtl` | `number` | No | JWKS cache duration in ms (default: 1 hour) |
|
|
73
|
+
|
|
74
|
+
### Authentication Methods
|
|
75
|
+
|
|
76
|
+
#### `signup(options)`
|
|
77
|
+
|
|
78
|
+
Create a new user account. A verification email is sent after signup.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
await auth.signup({
|
|
82
|
+
email: 'user@example.com',
|
|
83
|
+
password: 'SecurePassword123!',
|
|
84
|
+
redirectUri: 'https://yourapp.com/verify-callback', // Optional
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### `login(options)`
|
|
89
|
+
|
|
90
|
+
Authenticate with email and password.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const tokens = await auth.login({
|
|
94
|
+
email: 'user@example.com',
|
|
95
|
+
password: 'SecurePassword123!',
|
|
96
|
+
});
|
|
97
|
+
// Returns: { access_token, refresh_token, token_type, expires_in }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `logout(refreshToken)`
|
|
101
|
+
|
|
102
|
+
Revoke a refresh token to log out.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
await auth.logout(refreshToken);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Email Verification
|
|
109
|
+
|
|
110
|
+
#### `verifyEmail(options)`
|
|
111
|
+
|
|
112
|
+
Verify email using a token (returns tokens for automatic login).
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const tokens = await auth.verifyEmail({
|
|
116
|
+
token: 'verification-token-from-email',
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### `resendVerification(options)`
|
|
121
|
+
|
|
122
|
+
Resend the verification email.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
await auth.resendVerification({
|
|
126
|
+
email: 'user@example.com',
|
|
127
|
+
redirectUri: 'https://yourapp.com/verify-callback', // Optional
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Password Reset
|
|
132
|
+
|
|
133
|
+
#### `forgotPassword(options)`
|
|
134
|
+
|
|
135
|
+
Request a password reset email.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
await auth.forgotPassword({
|
|
139
|
+
email: 'user@example.com',
|
|
140
|
+
redirectUri: 'https://yourapp.com/reset-password', // Optional
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### `resetPassword(options)`
|
|
145
|
+
|
|
146
|
+
Set a new password using a reset token.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
await auth.resetPassword({
|
|
150
|
+
token: 'reset-token-from-email',
|
|
151
|
+
password: 'NewSecurePassword123!',
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Token Management
|
|
156
|
+
|
|
157
|
+
#### `verifyToken(token)`
|
|
158
|
+
|
|
159
|
+
Verify an access token and return the decoded payload.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const result = await auth.verifyToken(accessToken);
|
|
163
|
+
console.log('User ID:', result.payload.sub);
|
|
164
|
+
console.log('Email:', result.payload.email);
|
|
165
|
+
console.log('Verified:', result.payload.email_verified);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### `refreshToken(refreshToken)`
|
|
169
|
+
|
|
170
|
+
Exchange a refresh token for new tokens.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const newTokens = await auth.refreshToken(currentRefreshToken);
|
|
174
|
+
// Store the new tokens - refresh token rotation is used
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### `toAuthResult(tokenResponse)`
|
|
178
|
+
|
|
179
|
+
Convert a token response to a structured `AuthResult` with user and session data.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const tokens = await auth.login({ email, password });
|
|
183
|
+
const result = await auth.toAuthResult(tokens);
|
|
184
|
+
|
|
185
|
+
// result.user: { id, email, emailVerified }
|
|
186
|
+
// result.session: { accessToken, refreshToken, expiresAt }
|
|
187
|
+
// result.isNewUser?: boolean (for OAuth flows)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### `clearJwksCache()`
|
|
191
|
+
|
|
192
|
+
Invalidate the cached JWKS to force a refresh on next verification.
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
auth.clearJwksCache();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### OAuth
|
|
199
|
+
|
|
200
|
+
#### `getOAuthUrl(options)`
|
|
201
|
+
|
|
202
|
+
Generate an OAuth authorization URL.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const googleUrl = auth.getOAuthUrl({
|
|
206
|
+
provider: 'google', // or 'github'
|
|
207
|
+
redirectUri: 'https://yourapp.com/auth/callback',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Redirect user to start OAuth flow
|
|
211
|
+
window.location.href = googleUrl;
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
After OAuth completes, the user is redirected to your `redirectUri` with tokens as URL hash parameters:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// Handle callback in your app
|
|
218
|
+
const hash = new URLSearchParams(window.location.hash.slice(1));
|
|
219
|
+
const accessToken = hash.get('access_token');
|
|
220
|
+
const refreshToken = hash.get('refresh_token');
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Framework Integrations
|
|
224
|
+
|
|
225
|
+
### Astro Middleware
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// src/middleware.ts
|
|
229
|
+
import { createAstroAuth } from '@ovixa/auth-client/astro';
|
|
230
|
+
import { OvixaAuth } from '@ovixa/auth-client';
|
|
231
|
+
|
|
232
|
+
const auth = new OvixaAuth({
|
|
233
|
+
authUrl: import.meta.env.AUTH_URL,
|
|
234
|
+
realmId: import.meta.env.AUTH_REALM_ID,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
export const onRequest = createAstroAuth({
|
|
238
|
+
auth,
|
|
239
|
+
publicRoutes: ['/', '/login', '/signup', '/api/public/*'],
|
|
240
|
+
loginRedirect: '/login',
|
|
241
|
+
cookies: {
|
|
242
|
+
secure: import.meta.env.PROD,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Access auth context in pages:
|
|
248
|
+
|
|
249
|
+
```astro
|
|
250
|
+
---
|
|
251
|
+
// src/pages/dashboard.astro
|
|
252
|
+
const { user, isAuthenticated } = Astro.locals.auth;
|
|
253
|
+
|
|
254
|
+
if (!isAuthenticated) {
|
|
255
|
+
return Astro.redirect('/login');
|
|
256
|
+
}
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
<h1>Welcome, {user.email}</h1>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Set cookies after login:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
// src/pages/api/login.ts
|
|
266
|
+
import { setAstroAuthCookies } from '@ovixa/auth-client/astro';
|
|
267
|
+
import type { APIContext } from 'astro';
|
|
268
|
+
|
|
269
|
+
export async function POST({ request, cookies }: APIContext) {
|
|
270
|
+
const { email, password } = await request.json();
|
|
271
|
+
const tokens = await auth.login({ email, password });
|
|
272
|
+
|
|
273
|
+
setAstroAuthCookies({ cookies }, tokens);
|
|
274
|
+
|
|
275
|
+
return new Response(JSON.stringify({ success: true }));
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Clear cookies on logout:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
// src/pages/api/logout.ts
|
|
283
|
+
import { clearAstroAuthCookies } from '@ovixa/auth-client/astro';
|
|
284
|
+
|
|
285
|
+
export async function POST({ cookies, locals }: APIContext) {
|
|
286
|
+
if (locals.auth?.session?.refreshToken) {
|
|
287
|
+
await auth.logout(locals.auth.session.refreshToken);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
clearAstroAuthCookies({ cookies });
|
|
291
|
+
|
|
292
|
+
return new Response(JSON.stringify({ success: true }));
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Express Middleware
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import express from 'express';
|
|
300
|
+
import cookieParser from 'cookie-parser';
|
|
301
|
+
import { createExpressAuth, requireAuth } from '@ovixa/auth-client/express';
|
|
302
|
+
import { OvixaAuth } from '@ovixa/auth-client';
|
|
303
|
+
|
|
304
|
+
const auth = new OvixaAuth({
|
|
305
|
+
authUrl: process.env.AUTH_URL!,
|
|
306
|
+
realmId: process.env.AUTH_REALM_ID!,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const app = express();
|
|
310
|
+
app.use(cookieParser());
|
|
311
|
+
app.use(
|
|
312
|
+
createExpressAuth({
|
|
313
|
+
auth,
|
|
314
|
+
publicRoutes: ['/', '/login', '/signup'],
|
|
315
|
+
loginRedirect: '/login',
|
|
316
|
+
cookies: {
|
|
317
|
+
secure: process.env.NODE_ENV === 'production',
|
|
318
|
+
},
|
|
319
|
+
})
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Access auth context in routes
|
|
323
|
+
app.get('/api/me', (req, res) => {
|
|
324
|
+
if (!req.auth?.isAuthenticated) {
|
|
325
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
326
|
+
}
|
|
327
|
+
res.json({ user: req.auth.user });
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Use requireAuth middleware for protected routes
|
|
331
|
+
app.get('/dashboard', requireAuth({ redirect: '/login' }), (req, res) => {
|
|
332
|
+
res.render('dashboard', { user: req.auth.user });
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Set cookies after login:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import { setExpressAuthCookies } from '@ovixa/auth-client/express';
|
|
340
|
+
|
|
341
|
+
app.post('/api/login', async (req, res) => {
|
|
342
|
+
const { email, password } = req.body;
|
|
343
|
+
const tokens = await auth.login({ email, password });
|
|
344
|
+
|
|
345
|
+
setExpressAuthCookies(res, req, tokens);
|
|
346
|
+
|
|
347
|
+
res.json({ success: true });
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Custom Framework Integration
|
|
352
|
+
|
|
353
|
+
Implement the `CookieAdapter` interface to add support for other frameworks:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import type {
|
|
357
|
+
CookieAdapter,
|
|
358
|
+
SetCookieOptions,
|
|
359
|
+
DeleteCookieOptions,
|
|
360
|
+
} from '@ovixa/auth-client/astro';
|
|
361
|
+
|
|
362
|
+
class HonoCookieAdapter implements CookieAdapter {
|
|
363
|
+
constructor(private context: HonoContext) {}
|
|
364
|
+
|
|
365
|
+
getCookie(name: string): string | undefined {
|
|
366
|
+
return this.context.req.cookie(name);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
setCookie(name: string, value: string, options: SetCookieOptions): void {
|
|
370
|
+
this.context.cookie(name, value, options);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
deleteCookie(name: string, options: DeleteCookieOptions): void {
|
|
374
|
+
this.context.cookie(name, '', { ...options, maxAge: 0 });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Middleware Configuration
|
|
380
|
+
|
|
381
|
+
| Option | Type | Default | Description |
|
|
382
|
+
| --------------- | ----------- | -------- | ---------------------------------------- |
|
|
383
|
+
| `auth` | `OvixaAuth` | Required | The OvixaAuth client instance |
|
|
384
|
+
| `publicRoutes` | `string[]` | `[]` | Routes that bypass authentication |
|
|
385
|
+
| `loginRedirect` | `string` | - | URL to redirect unauthenticated requests |
|
|
386
|
+
| `autoRefresh` | `boolean` | `true` | Automatically refresh expired tokens |
|
|
387
|
+
| `cookies` | `object` | - | Cookie configuration (see below) |
|
|
388
|
+
|
|
389
|
+
### Cookie Options
|
|
390
|
+
|
|
391
|
+
| Option | Type | Default | Description |
|
|
392
|
+
| -------------------- | --------- | ----------------------- | ------------------------------- |
|
|
393
|
+
| `accessTokenCookie` | `string` | `'ovixa_access_token'` | Name of access token cookie |
|
|
394
|
+
| `refreshTokenCookie` | `string` | `'ovixa_refresh_token'` | Name of refresh token cookie |
|
|
395
|
+
| `path` | `string` | `'/'` | Cookie path |
|
|
396
|
+
| `secure` | `boolean` | `true` | Use secure cookies (HTTPS only) |
|
|
397
|
+
| `httpOnly` | `boolean` | `true` | HTTP-only cookies |
|
|
398
|
+
| `sameSite` | `string` | `'lax'` | SameSite policy |
|
|
399
|
+
| `domain` | `string` | - | Cookie domain |
|
|
400
|
+
|
|
401
|
+
## Error Handling
|
|
402
|
+
|
|
403
|
+
All methods throw `OvixaAuthError` on failure:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
import { OvixaAuth, OvixaAuthError } from '@ovixa/auth-client';
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
await auth.login({ email, password });
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (error instanceof OvixaAuthError) {
|
|
412
|
+
console.error('Code:', error.code); // e.g., 'INVALID_CREDENTIALS'
|
|
413
|
+
console.error('Message:', error.message);
|
|
414
|
+
console.error('Status:', error.statusCode); // HTTP status code
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Error Codes
|
|
420
|
+
|
|
421
|
+
| Code | Description |
|
|
422
|
+
| --------------------- | ----------------------------------- |
|
|
423
|
+
| `INVALID_CREDENTIALS` | Wrong email or password |
|
|
424
|
+
| `EMAIL_NOT_VERIFIED` | User must verify email before login |
|
|
425
|
+
| `INVALID_TOKEN` | Token is invalid or malformed |
|
|
426
|
+
| `TOKEN_EXPIRED` | Token has expired |
|
|
427
|
+
| `INVALID_SIGNATURE` | Token signature verification failed |
|
|
428
|
+
| `INVALID_ISSUER` | Token issuer doesn't match |
|
|
429
|
+
| `INVALID_AUDIENCE` | Token audience doesn't match |
|
|
430
|
+
| `RATE_LIMITED` | Too many requests |
|
|
431
|
+
| `NETWORK_ERROR` | Failed to reach auth service |
|
|
432
|
+
| `BAD_REQUEST` | Invalid request parameters |
|
|
433
|
+
| `UNAUTHORIZED` | Authentication required |
|
|
434
|
+
| `FORBIDDEN` | Access denied |
|
|
435
|
+
| `NOT_FOUND` | Resource not found |
|
|
436
|
+
| `SERVER_ERROR` | Auth service error |
|
|
437
|
+
|
|
438
|
+
## Types
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
// Token response from auth service
|
|
442
|
+
interface TokenResponse {
|
|
443
|
+
access_token: string;
|
|
444
|
+
refresh_token: string;
|
|
445
|
+
token_type: 'Bearer';
|
|
446
|
+
expires_in: number;
|
|
447
|
+
is_new_user?: boolean; // OAuth flows only
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// User information (extracted from JWT claims)
|
|
451
|
+
interface User {
|
|
452
|
+
id: string;
|
|
453
|
+
email: string;
|
|
454
|
+
emailVerified: boolean;
|
|
455
|
+
// Note: createdAt is intentionally omitted. The JWT `iat` claim is when the
|
|
456
|
+
// *token* was issued, not when the user was created. If you need the user's
|
|
457
|
+
// creation date, fetch it from the /me endpoint or include it in custom claims.
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Session data
|
|
461
|
+
interface Session {
|
|
462
|
+
accessToken: string;
|
|
463
|
+
refreshToken: string;
|
|
464
|
+
expiresAt: Date;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Combined auth result
|
|
468
|
+
interface AuthResult {
|
|
469
|
+
user: User;
|
|
470
|
+
session: Session;
|
|
471
|
+
isNewUser?: boolean;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Auth context (available in middleware)
|
|
475
|
+
interface AuthContext {
|
|
476
|
+
user: User | null;
|
|
477
|
+
session: Session | null;
|
|
478
|
+
isAuthenticated: boolean;
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## License
|
|
483
|
+
|
|
484
|
+
MIT
|