@hamak/auth 0.5.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/README.md +366 -0
- package/dist/api/api/auth-service.d.ts +111 -0
- package/dist/api/api/auth-service.d.ts.map +1 -0
- package/dist/api/api/auth-service.js +5 -0
- package/dist/api/api/index.d.ts +2 -0
- package/dist/api/api/index.d.ts.map +1 -0
- package/dist/api/api/index.js +1 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +12 -0
- package/dist/api/tokens/index.d.ts +2 -0
- package/dist/api/tokens/index.d.ts.map +1 -0
- package/dist/api/tokens/index.js +1 -0
- package/dist/api/tokens/service-tokens.d.ts +26 -0
- package/dist/api/tokens/service-tokens.d.ts.map +1 -0
- package/dist/api/tokens/service-tokens.js +25 -0
- package/dist/api/types/auth-result.d.ts +69 -0
- package/dist/api/types/auth-result.d.ts.map +1 -0
- package/dist/api/types/auth-result.js +5 -0
- package/dist/api/types/config.d.ts +130 -0
- package/dist/api/types/config.d.ts.map +1 -0
- package/dist/api/types/config.js +5 -0
- package/dist/api/types/credentials.d.ts +52 -0
- package/dist/api/types/credentials.d.ts.map +1 -0
- package/dist/api/types/credentials.js +5 -0
- package/dist/api/types/index.d.ts +5 -0
- package/dist/api/types/index.d.ts.map +1 -0
- package/dist/api/types/index.js +4 -0
- package/dist/api/types/user.d.ts +39 -0
- package/dist/api/types/user.d.ts.map +1 -0
- package/dist/api/types/user.js +5 -0
- package/dist/impl/index.d.ts +15 -0
- package/dist/impl/index.d.ts.map +1 -0
- package/dist/impl/index.js +21 -0
- package/dist/impl/plugin/auth-plugin-factory.d.ts +20 -0
- package/dist/impl/plugin/auth-plugin-factory.d.ts.map +1 -0
- package/dist/impl/plugin/auth-plugin-factory.js +226 -0
- package/dist/impl/plugin/index.d.ts +2 -0
- package/dist/impl/plugin/index.d.ts.map +1 -0
- package/dist/impl/plugin/index.js +1 -0
- package/dist/impl/services/AuthService.d.ts +44 -0
- package/dist/impl/services/AuthService.d.ts.map +1 -0
- package/dist/impl/services/AuthService.js +277 -0
- package/dist/impl/services/index.d.ts +2 -0
- package/dist/impl/services/index.d.ts.map +1 -0
- package/dist/impl/services/index.js +1 -0
- package/dist/impl/storage/LocalTokenStorage.d.ts +32 -0
- package/dist/impl/storage/LocalTokenStorage.d.ts.map +1 -0
- package/dist/impl/storage/LocalTokenStorage.js +148 -0
- package/dist/impl/storage/MemoryTokenStorage.d.ts +34 -0
- package/dist/impl/storage/MemoryTokenStorage.d.ts.map +1 -0
- package/dist/impl/storage/MemoryTokenStorage.js +91 -0
- package/dist/impl/storage/SessionTokenStorage.d.ts +33 -0
- package/dist/impl/storage/SessionTokenStorage.d.ts.map +1 -0
- package/dist/impl/storage/SessionTokenStorage.js +147 -0
- package/dist/impl/storage/index.d.ts +10 -0
- package/dist/impl/storage/index.d.ts.map +1 -0
- package/dist/impl/storage/index.js +26 -0
- package/dist/impl/store/auth-reducer.d.ts +135 -0
- package/dist/impl/store/auth-reducer.d.ts.map +1 -0
- package/dist/impl/store/auth-reducer.js +179 -0
- package/dist/impl/store/index.d.ts +2 -0
- package/dist/impl/store/index.d.ts.map +1 -0
- package/dist/impl/store/index.js +1 -0
- package/dist/impl/strategies/KeycloakStrategy.d.ts +42 -0
- package/dist/impl/strategies/KeycloakStrategy.d.ts.map +1 -0
- package/dist/impl/strategies/KeycloakStrategy.js +237 -0
- package/dist/impl/strategies/OAuth2Strategy.d.ts +30 -0
- package/dist/impl/strategies/OAuth2Strategy.d.ts.map +1 -0
- package/dist/impl/strategies/OAuth2Strategy.js +232 -0
- package/dist/impl/strategies/PasswordStrategy.d.ts +25 -0
- package/dist/impl/strategies/PasswordStrategy.d.ts.map +1 -0
- package/dist/impl/strategies/PasswordStrategy.js +159 -0
- package/dist/impl/strategies/StrategyRegistry.d.ts +24 -0
- package/dist/impl/strategies/StrategyRegistry.d.ts.map +1 -0
- package/dist/impl/strategies/StrategyRegistry.js +70 -0
- package/dist/impl/strategies/index.d.ts +5 -0
- package/dist/impl/strategies/index.d.ts.map +1 -0
- package/dist/impl/strategies/index.js +4 -0
- package/dist/impl/utils/index.d.ts +3 -0
- package/dist/impl/utils/index.d.ts.map +1 -0
- package/dist/impl/utils/index.js +2 -0
- package/dist/impl/utils/jwt.d.ts +81 -0
- package/dist/impl/utils/jwt.d.ts.map +1 -0
- package/dist/impl/utils/jwt.js +103 -0
- package/dist/impl/utils/pkce.d.ts +44 -0
- package/dist/impl/utils/pkce.d.ts.map +1 -0
- package/dist/impl/utils/pkce.js +93 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/spi/guards/AuthGuard.d.ts +108 -0
- package/dist/spi/guards/AuthGuard.d.ts.map +1 -0
- package/dist/spi/guards/AuthGuard.js +5 -0
- package/dist/spi/guards/index.d.ts +2 -0
- package/dist/spi/guards/index.d.ts.map +1 -0
- package/dist/spi/guards/index.js +1 -0
- package/dist/spi/index.d.ts +12 -0
- package/dist/spi/index.d.ts.map +1 -0
- package/dist/spi/index.js +15 -0
- package/dist/spi/storage/ITokenStorage.d.ts +107 -0
- package/dist/spi/storage/ITokenStorage.d.ts.map +1 -0
- package/dist/spi/storage/ITokenStorage.js +5 -0
- package/dist/spi/storage/index.d.ts +2 -0
- package/dist/spi/storage/index.d.ts.map +1 -0
- package/dist/spi/storage/index.js +1 -0
- package/dist/spi/strategies/IAuthStrategy.d.ts +114 -0
- package/dist/spi/strategies/IAuthStrategy.d.ts.map +1 -0
- package/dist/spi/strategies/IAuthStrategy.js +16 -0
- package/dist/spi/strategies/IStrategyRegistry.d.ts +64 -0
- package/dist/spi/strategies/IStrategyRegistry.d.ts.map +1 -0
- package/dist/spi/strategies/IStrategyRegistry.js +5 -0
- package/dist/spi/strategies/index.d.ts +3 -0
- package/dist/spi/strategies/index.d.ts.map +1 -0
- package/dist/spi/strategies/index.js +2 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# @hamak/auth
|
|
2
|
+
|
|
3
|
+
A complete authentication system for the Hamak App Framework, supporting password-based login, OAuth2, and Keycloak authentication.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Multiple Authentication Strategies** - Password, OAuth2, Keycloak
|
|
8
|
+
- 🔄 **Automatic Token Refresh** - Proactive refresh before expiry
|
|
9
|
+
- 💾 **Flexible Token Storage** - localStorage, sessionStorage, or memory
|
|
10
|
+
- 🛡️ **PKCE Support** - Secure OAuth2 for public clients
|
|
11
|
+
- ⚛️ **React Integration** - Providers, hooks, and guard components
|
|
12
|
+
- 🏪 **Redux Integration** - Auth state in your store
|
|
13
|
+
- 🔌 **Plugin Architecture** - Integrates with microkernel plugin system
|
|
14
|
+
|
|
15
|
+
## Packages
|
|
16
|
+
|
|
17
|
+
| Package | Description |
|
|
18
|
+
|---------|-------------|
|
|
19
|
+
| `@hamak/auth-api` | Interfaces, types, and DI tokens |
|
|
20
|
+
| `@hamak/auth-spi` | Extension points for custom strategies |
|
|
21
|
+
| `@hamak/auth-impl` | Core implementations and plugin factory |
|
|
22
|
+
| `@hamak/auth-templates` | React providers, hooks, and components |
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @hamak/auth-api @hamak/auth-spi @hamak/auth-impl @hamak/auth-templates
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Configure the Auth Plugin
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createAuthPlugin } from '@hamak/auth-impl';
|
|
36
|
+
|
|
37
|
+
// Password-based authentication
|
|
38
|
+
const authPlugin = createAuthPlugin({
|
|
39
|
+
strategy: 'password',
|
|
40
|
+
password: {
|
|
41
|
+
loginEndpoint: '/api/auth/login',
|
|
42
|
+
refreshEndpoint: '/api/auth/refresh',
|
|
43
|
+
logoutEndpoint: '/api/auth/logout',
|
|
44
|
+
userInfoEndpoint: '/api/auth/me'
|
|
45
|
+
},
|
|
46
|
+
tokenStorage: 'localStorage',
|
|
47
|
+
autoRefresh: {
|
|
48
|
+
enabled: true,
|
|
49
|
+
threshold: 60000 // Refresh 1 min before expiry
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Or OAuth2
|
|
54
|
+
const oauthPlugin = createAuthPlugin({
|
|
55
|
+
strategy: 'oauth2',
|
|
56
|
+
oauth2: {
|
|
57
|
+
clientId: 'my-app',
|
|
58
|
+
authorizationUrl: 'https://auth.example.com/authorize',
|
|
59
|
+
tokenUrl: 'https://auth.example.com/token',
|
|
60
|
+
userInfoUrl: 'https://auth.example.com/userinfo',
|
|
61
|
+
redirectUri: 'http://localhost:3000/callback',
|
|
62
|
+
scope: ['openid', 'profile', 'email']
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Or Keycloak
|
|
67
|
+
const keycloakPlugin = createAuthPlugin({
|
|
68
|
+
strategy: 'keycloak',
|
|
69
|
+
keycloak: {
|
|
70
|
+
realm: 'my-realm',
|
|
71
|
+
serverUrl: 'https://keycloak.example.com',
|
|
72
|
+
clientId: 'my-app',
|
|
73
|
+
redirectUri: 'http://localhost:3000/callback',
|
|
74
|
+
enableDirectGrant: true // Allow password login
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Register the Plugin
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Host } from '@hamak/microkernel-impl';
|
|
83
|
+
|
|
84
|
+
const host = new Host();
|
|
85
|
+
host.registerPlugin('auth', manifest, authPlugin);
|
|
86
|
+
await host.bootstrapAllAtRoot();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. Setup React Provider
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { AuthProvider } from '@hamak/auth-templates';
|
|
93
|
+
import { AUTH_SERVICE_TOKEN } from '@hamak/auth-api';
|
|
94
|
+
|
|
95
|
+
function App() {
|
|
96
|
+
const [authService, setAuthService] = useState(null);
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
host.bootstrapAllAtRoot().then(() => {
|
|
100
|
+
const ctx = host.rootActivationCtx;
|
|
101
|
+
setAuthService(ctx.resolve(AUTH_SERVICE_TOKEN));
|
|
102
|
+
});
|
|
103
|
+
}, []);
|
|
104
|
+
|
|
105
|
+
if (!authService) return <Loading />;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<AuthProvider authService={authService}>
|
|
109
|
+
<YourApp />
|
|
110
|
+
</AuthProvider>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 4. Use Auth in Components
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { useAuth, useLogin, useLogout, RequireAuth, RequireRole } from '@hamak/auth-templates';
|
|
119
|
+
|
|
120
|
+
// Login form
|
|
121
|
+
function LoginPage() {
|
|
122
|
+
const { loginWithPassword, loading, error } = useLogin();
|
|
123
|
+
|
|
124
|
+
const handleSubmit = async (e) => {
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
const result = await loginWithPassword(username, password);
|
|
127
|
+
if (result.success) {
|
|
128
|
+
navigate('/dashboard');
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<form onSubmit={handleSubmit}>
|
|
134
|
+
{error && <Alert>{error.message}</Alert>}
|
|
135
|
+
<input name="username" />
|
|
136
|
+
<input name="password" type="password" />
|
|
137
|
+
<button disabled={loading}>Login</button>
|
|
138
|
+
</form>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Protected content
|
|
143
|
+
function Dashboard() {
|
|
144
|
+
const { user } = useAuth();
|
|
145
|
+
const { logout } = useLogout();
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<RequireAuth fallback={<Loading />}>
|
|
149
|
+
<h1>Welcome, {user?.name}</h1>
|
|
150
|
+
|
|
151
|
+
<RequireRole role="admin">
|
|
152
|
+
<AdminPanel />
|
|
153
|
+
</RequireRole>
|
|
154
|
+
|
|
155
|
+
<button onClick={logout}>Logout</button>
|
|
156
|
+
</RequireAuth>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Available Hooks
|
|
162
|
+
|
|
163
|
+
| Hook | Description |
|
|
164
|
+
|------|-------------|
|
|
165
|
+
| `useAuth()` | Full auth context with state and operations |
|
|
166
|
+
| `useAuthState()` | Auth state only (isAuthenticated, user, loading) |
|
|
167
|
+
| `useUser()` | Current user or null |
|
|
168
|
+
| `useLogin()` | Login operations with loading/error state |
|
|
169
|
+
| `useLogout()` | Logout operation with loading state |
|
|
170
|
+
| `useRoles()` | Role checking utilities |
|
|
171
|
+
| `usePermissions()` | Permission checking utilities |
|
|
172
|
+
| `useOAuthCallback()` | Handle OAuth redirect callbacks |
|
|
173
|
+
|
|
174
|
+
## Guard Components
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
// Require authentication
|
|
178
|
+
<RequireAuth fallback={<Loading />}>
|
|
179
|
+
<ProtectedContent />
|
|
180
|
+
</RequireAuth>
|
|
181
|
+
|
|
182
|
+
// Require specific role
|
|
183
|
+
<RequireRole role="admin" unauthorizedContent={<AccessDenied />}>
|
|
184
|
+
<AdminPanel />
|
|
185
|
+
</RequireRole>
|
|
186
|
+
|
|
187
|
+
// Require any of multiple roles
|
|
188
|
+
<RequireRole roles={['admin', 'moderator']}>
|
|
189
|
+
<ModeratorTools />
|
|
190
|
+
</RequireRole>
|
|
191
|
+
|
|
192
|
+
// Require permission
|
|
193
|
+
<RequirePermission permission="document:write">
|
|
194
|
+
<EditButton />
|
|
195
|
+
</RequirePermission>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Higher-Order Components
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { withAuth, withRole, withPermission } from '@hamak/auth-templates';
|
|
202
|
+
|
|
203
|
+
// Protect a component
|
|
204
|
+
const ProtectedDashboard = withAuth(Dashboard, {
|
|
205
|
+
fallback: <Loading />,
|
|
206
|
+
onUnauthenticated: () => navigate('/login')
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Require role
|
|
210
|
+
const AdminPanel = withRole(Panel, {
|
|
211
|
+
role: 'admin',
|
|
212
|
+
unauthorizedComponent: AccessDenied
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Plugin Commands
|
|
217
|
+
|
|
218
|
+
The auth plugin registers these commands:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
ctx.commands.run('auth.login', credentials);
|
|
222
|
+
ctx.commands.run('auth.logout');
|
|
223
|
+
ctx.commands.run('auth.refresh');
|
|
224
|
+
ctx.commands.run('auth.isAuthenticated');
|
|
225
|
+
ctx.commands.run('auth.getCurrentUser');
|
|
226
|
+
ctx.commands.run('auth.hasRole', 'admin');
|
|
227
|
+
ctx.commands.run('auth.hasPermission', 'document:write');
|
|
228
|
+
ctx.commands.run('auth.initiateOAuth', 'keycloak');
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Plugin Events
|
|
232
|
+
|
|
233
|
+
Subscribe to auth events via hooks:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
ctx.hooks.on('auth:login-success', ({ user }) => {
|
|
237
|
+
console.log('User logged in:', user);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
ctx.hooks.on('auth:logout', () => {
|
|
241
|
+
console.log('User logged out');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
ctx.hooks.on('auth:session-expired', () => {
|
|
245
|
+
console.log('Session expired');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
ctx.hooks.on('auth:token-refresh', ({ user }) => {
|
|
249
|
+
console.log('Token refreshed');
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Configuration Options
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
interface AuthPluginConfig {
|
|
257
|
+
// Primary strategy
|
|
258
|
+
strategy: 'password' | 'oauth2' | 'keycloak';
|
|
259
|
+
|
|
260
|
+
// Password strategy config
|
|
261
|
+
password?: {
|
|
262
|
+
loginEndpoint: string;
|
|
263
|
+
refreshEndpoint: string;
|
|
264
|
+
logoutEndpoint: string;
|
|
265
|
+
userInfoEndpoint?: string;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// OAuth2 config
|
|
269
|
+
oauth2?: {
|
|
270
|
+
clientId: string;
|
|
271
|
+
authorizationUrl: string;
|
|
272
|
+
tokenUrl: string;
|
|
273
|
+
userInfoUrl?: string;
|
|
274
|
+
redirectUri: string;
|
|
275
|
+
scope: string[];
|
|
276
|
+
usePkce?: boolean; // default: true
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Keycloak config
|
|
280
|
+
keycloak?: {
|
|
281
|
+
realm: string;
|
|
282
|
+
serverUrl: string;
|
|
283
|
+
clientId: string;
|
|
284
|
+
redirectUri: string;
|
|
285
|
+
scope?: string[];
|
|
286
|
+
enableDirectGrant?: boolean;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Storage
|
|
290
|
+
tokenStorage?: 'localStorage' | 'sessionStorage' | 'memory';
|
|
291
|
+
storageKeyPrefix?: string;
|
|
292
|
+
|
|
293
|
+
// Auto refresh
|
|
294
|
+
autoRefresh?: {
|
|
295
|
+
enabled: boolean;
|
|
296
|
+
threshold?: number; // ms before expiry
|
|
297
|
+
checkInterval?: number; // ms between checks
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// Routes
|
|
301
|
+
routes?: {
|
|
302
|
+
afterLogin?: string;
|
|
303
|
+
afterLogout?: string;
|
|
304
|
+
login?: string;
|
|
305
|
+
unauthorized?: string;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
debug?: boolean;
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Custom Strategy
|
|
313
|
+
|
|
314
|
+
Implement your own authentication strategy:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
import type { IAuthStrategy } from '@hamak/auth-spi';
|
|
318
|
+
|
|
319
|
+
class CustomStrategy implements IAuthStrategy {
|
|
320
|
+
readonly type = 'custom';
|
|
321
|
+
readonly name = 'my-custom-auth';
|
|
322
|
+
|
|
323
|
+
async authenticate(credentials) {
|
|
324
|
+
// Your authentication logic
|
|
325
|
+
return { success: true, user, accessToken, refreshToken };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async refreshToken(refreshToken) {
|
|
329
|
+
// Your refresh logic
|
|
330
|
+
return { success: true, accessToken };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async logout(accessToken) {
|
|
334
|
+
// Your logout logic
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Security Considerations
|
|
340
|
+
|
|
341
|
+
- **PKCE**: Enabled by default for OAuth2 flows
|
|
342
|
+
- **State Parameter**: CSRF protection for OAuth callbacks
|
|
343
|
+
- **Token Storage**: Use `memory` for highest security (tokens lost on refresh)
|
|
344
|
+
- **Auto Refresh**: Prevents session expiry during active use
|
|
345
|
+
- **JWT Validation**: Server-side validation required; client-side is for UX only
|
|
346
|
+
|
|
347
|
+
## Demo
|
|
348
|
+
|
|
349
|
+
Run the demo app to see auth in action:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
npm run demo:dev
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Navigate to the **Auth Demo** tab to test:
|
|
356
|
+
- Login with `demo`/`demo123` (user + editor roles)
|
|
357
|
+
- Login with `admin`/`admin123` (all roles)
|
|
358
|
+
- Login with `guest`/`guest123` (guest role only)
|
|
359
|
+
|
|
360
|
+
## API Reference
|
|
361
|
+
|
|
362
|
+
See [DESIGN.md](./DESIGN.md) for detailed architecture documentation.
|
|
363
|
+
|
|
364
|
+
## License
|
|
365
|
+
|
|
366
|
+
MIT
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Service Interface
|
|
3
|
+
* Core authentication service contract
|
|
4
|
+
*/
|
|
5
|
+
import type { User } from '../types/user';
|
|
6
|
+
import type { LoginCredentials, OAuthCallbackParams } from '../types/credentials';
|
|
7
|
+
import type { AuthResult, AuthStateCallback } from '../types/auth-result';
|
|
8
|
+
/**
|
|
9
|
+
* Main authentication service interface
|
|
10
|
+
*
|
|
11
|
+
* Provides a unified API for all authentication operations regardless
|
|
12
|
+
* of the underlying strategy (password, OAuth2, Keycloak, etc.)
|
|
13
|
+
*/
|
|
14
|
+
export interface IAuthService {
|
|
15
|
+
/**
|
|
16
|
+
* Check if the user is currently authenticated
|
|
17
|
+
* @returns true if authenticated with a valid token
|
|
18
|
+
*/
|
|
19
|
+
isAuthenticated(): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Get the currently authenticated user
|
|
22
|
+
* @returns The user object or null if not authenticated
|
|
23
|
+
*/
|
|
24
|
+
getCurrentUser(): User | null;
|
|
25
|
+
/**
|
|
26
|
+
* Get the current access token
|
|
27
|
+
* @returns The access token or null if not authenticated
|
|
28
|
+
*/
|
|
29
|
+
getAccessToken(): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Get the user's permissions
|
|
32
|
+
* @returns Array of permission strings
|
|
33
|
+
*/
|
|
34
|
+
getPermissions(): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Get the user's roles
|
|
37
|
+
* @returns Array of role strings
|
|
38
|
+
*/
|
|
39
|
+
getRoles(): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Check if the user has a specific permission
|
|
42
|
+
* @param permission The permission to check
|
|
43
|
+
*/
|
|
44
|
+
hasPermission(permission: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check if the user has a specific role
|
|
47
|
+
* @param role The role to check
|
|
48
|
+
*/
|
|
49
|
+
hasRole(role: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Check if the user has any of the specified roles
|
|
52
|
+
* @param roles The roles to check
|
|
53
|
+
*/
|
|
54
|
+
hasAnyRole(roles: string[]): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Check if the user has all of the specified roles
|
|
57
|
+
* @param roles The roles to check
|
|
58
|
+
*/
|
|
59
|
+
hasAllRoles(roles: string[]): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Authenticate with credentials
|
|
62
|
+
* @param credentials The login credentials
|
|
63
|
+
* @returns Authentication result
|
|
64
|
+
*/
|
|
65
|
+
login(credentials: LoginCredentials): Promise<AuthResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Log out the current user
|
|
68
|
+
* Clears tokens and session data
|
|
69
|
+
*/
|
|
70
|
+
logout(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Refresh the current access token
|
|
73
|
+
* @returns New authentication result with fresh tokens
|
|
74
|
+
*/
|
|
75
|
+
refreshToken(): Promise<AuthResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Initiate OAuth login flow
|
|
78
|
+
* Redirects the user to the OAuth provider's authorization page
|
|
79
|
+
* @param provider Optional provider name for multi-provider setups
|
|
80
|
+
*/
|
|
81
|
+
initiateOAuth(provider?: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Handle OAuth callback after authorization
|
|
84
|
+
* @param params The callback parameters from the OAuth provider
|
|
85
|
+
* @returns Authentication result
|
|
86
|
+
*/
|
|
87
|
+
handleOAuthCallback(params: OAuthCallbackParams): Promise<AuthResult>;
|
|
88
|
+
/**
|
|
89
|
+
* Get the OAuth authorization URL without redirecting
|
|
90
|
+
* @param provider Optional provider name
|
|
91
|
+
* @returns The authorization URL
|
|
92
|
+
*/
|
|
93
|
+
getAuthorizationUrl(provider?: string): string | null;
|
|
94
|
+
/**
|
|
95
|
+
* Subscribe to authentication state changes
|
|
96
|
+
* @param callback Function called when auth state changes
|
|
97
|
+
* @returns Unsubscribe function
|
|
98
|
+
*/
|
|
99
|
+
onAuthStateChange(callback: AuthStateCallback): () => void;
|
|
100
|
+
/**
|
|
101
|
+
* Initialize the auth service
|
|
102
|
+
* Restores session from storage if available
|
|
103
|
+
*/
|
|
104
|
+
initialize(): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Destroy the auth service
|
|
107
|
+
* Cleans up resources and event listeners
|
|
108
|
+
*/
|
|
109
|
+
destroy(): void;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=auth-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../../src/api/api/auth-service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAK3B;;;OAGG;IACH,eAAe,IAAI,OAAO,CAAC;IAE3B;;;OAGG;IACH,cAAc,IAAI,IAAI,GAAG,IAAI,CAAC;IAE9B;;;OAGG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI,CAAC;IAEhC;;;OAGG;IACH,cAAc,IAAI,MAAM,EAAE,CAAC;IAE3B;;;OAGG;IACH,QAAQ,IAAI,MAAM,EAAE,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAE3C;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAE/B;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAErC;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAMtC;;;;OAIG;IACH,KAAK,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE1D;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAExB;;;OAGG;IACH,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAMpC;;;;OAIG;IACH,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvC;;;;OAIG;IACH,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEtE;;;;OAIG;IACH,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAMtD;;;;OAIG;IACH,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAAC;IAM3D;;;OAGG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/api/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './auth-service';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hamak/auth/api
|
|
3
|
+
*
|
|
4
|
+
* Pure interfaces and types for the pluggable authentication system.
|
|
5
|
+
* This layer contains no implementation - only contracts.
|
|
6
|
+
*/
|
|
7
|
+
export * from './tokens';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from './api';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,UAAU,CAAC;AAGzB,cAAc,SAAS,CAAC;AAGxB,cAAc,OAAO,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hamak/auth/api
|
|
3
|
+
*
|
|
4
|
+
* Pure interfaces and types for the pluggable authentication system.
|
|
5
|
+
* This layer contains no implementation - only contracts.
|
|
6
|
+
*/
|
|
7
|
+
// Service tokens
|
|
8
|
+
export * from './tokens';
|
|
9
|
+
// Type definitions
|
|
10
|
+
export * from './types';
|
|
11
|
+
// API interfaces
|
|
12
|
+
export * from './api';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/tokens/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './service-tokens';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Service Tokens
|
|
3
|
+
* Symbol-based tokens for dependency injection
|
|
4
|
+
* Using Symbol.for() for global registry to ensure consistency across packages
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Token for the main authentication service
|
|
8
|
+
*/
|
|
9
|
+
export declare const AUTH_SERVICE_TOKEN: unique symbol;
|
|
10
|
+
/**
|
|
11
|
+
* Token for the authentication strategy registry
|
|
12
|
+
*/
|
|
13
|
+
export declare const AUTH_STRATEGY_REGISTRY_TOKEN: unique symbol;
|
|
14
|
+
/**
|
|
15
|
+
* Token for the token storage service
|
|
16
|
+
*/
|
|
17
|
+
export declare const TOKEN_STORAGE_TOKEN: unique symbol;
|
|
18
|
+
/**
|
|
19
|
+
* Token for the auth configuration
|
|
20
|
+
*/
|
|
21
|
+
export declare const AUTH_CONFIG_TOKEN: unique symbol;
|
|
22
|
+
/**
|
|
23
|
+
* Token for the auth state selector
|
|
24
|
+
*/
|
|
25
|
+
export declare const AUTH_STATE_TOKEN: unique symbol;
|
|
26
|
+
//# sourceMappingURL=service-tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-tokens.d.ts","sourceRoot":"","sources":["../../../src/api/tokens/service-tokens.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,eAAO,MAAM,kBAAkB,eAAwC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,4BAA4B,eAA6C,CAAC;AAEvF;;GAEG;AACH,eAAO,MAAM,mBAAmB,eAAyC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,iBAAiB,eAAmC,CAAC;AAElE;;GAEG;AACH,eAAO,MAAM,gBAAgB,eAAkC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Service Tokens
|
|
3
|
+
* Symbol-based tokens for dependency injection
|
|
4
|
+
* Using Symbol.for() for global registry to ensure consistency across packages
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Token for the main authentication service
|
|
8
|
+
*/
|
|
9
|
+
export const AUTH_SERVICE_TOKEN = Symbol.for('@hamak/auth:AuthService');
|
|
10
|
+
/**
|
|
11
|
+
* Token for the authentication strategy registry
|
|
12
|
+
*/
|
|
13
|
+
export const AUTH_STRATEGY_REGISTRY_TOKEN = Symbol.for('@hamak/auth:StrategyRegistry');
|
|
14
|
+
/**
|
|
15
|
+
* Token for the token storage service
|
|
16
|
+
*/
|
|
17
|
+
export const TOKEN_STORAGE_TOKEN = Symbol.for('@hamak/auth:TokenStorage');
|
|
18
|
+
/**
|
|
19
|
+
* Token for the auth configuration
|
|
20
|
+
*/
|
|
21
|
+
export const AUTH_CONFIG_TOKEN = Symbol.for('@hamak/auth:Config');
|
|
22
|
+
/**
|
|
23
|
+
* Token for the auth state selector
|
|
24
|
+
*/
|
|
25
|
+
export const AUTH_STATE_TOKEN = Symbol.for('@hamak/auth:State');
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Result Types
|
|
3
|
+
* Types for authentication operation results
|
|
4
|
+
*/
|
|
5
|
+
import type { User } from './user';
|
|
6
|
+
/**
|
|
7
|
+
* Error codes for authentication failures
|
|
8
|
+
*/
|
|
9
|
+
export type AuthErrorCode = 'invalid_credentials' | 'invalid_token' | 'token_expired' | 'refresh_failed' | 'no_refresh_token' | 'invalid_state' | 'network_error' | 'server_error' | 'unauthorized' | 'forbidden' | 'user_not_found' | 'account_locked' | 'account_disabled' | 'mfa_required' | 'unknown_error';
|
|
10
|
+
/**
|
|
11
|
+
* Authentication error details
|
|
12
|
+
*/
|
|
13
|
+
export interface AuthError {
|
|
14
|
+
/** Error code for programmatic handling */
|
|
15
|
+
code: AuthErrorCode;
|
|
16
|
+
/** Human-readable error message */
|
|
17
|
+
message?: string;
|
|
18
|
+
/** Additional error details */
|
|
19
|
+
details?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Token information
|
|
23
|
+
*/
|
|
24
|
+
export interface TokenInfo {
|
|
25
|
+
/** The access token */
|
|
26
|
+
accessToken: string;
|
|
27
|
+
/** The refresh token (if provided) */
|
|
28
|
+
refreshToken?: string;
|
|
29
|
+
/** Token type (usually 'Bearer') */
|
|
30
|
+
tokenType?: string;
|
|
31
|
+
/** When the access token expires (timestamp in ms) */
|
|
32
|
+
expiresAt?: number;
|
|
33
|
+
/** Token scopes */
|
|
34
|
+
scope?: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Result of an authentication operation
|
|
38
|
+
*/
|
|
39
|
+
export interface AuthResult {
|
|
40
|
+
/** Whether the operation succeeded */
|
|
41
|
+
success: boolean;
|
|
42
|
+
/** The authenticated user (on success) */
|
|
43
|
+
user?: User;
|
|
44
|
+
/** Access token (on success) */
|
|
45
|
+
accessToken?: string;
|
|
46
|
+
/** Refresh token (on success) */
|
|
47
|
+
refreshToken?: string;
|
|
48
|
+
/** Token type */
|
|
49
|
+
tokenType?: string;
|
|
50
|
+
/** When the access token expires (timestamp in ms) */
|
|
51
|
+
expiresAt?: number;
|
|
52
|
+
/** Token scopes */
|
|
53
|
+
scope?: string[];
|
|
54
|
+
/** Error details (on failure) */
|
|
55
|
+
error?: AuthError;
|
|
56
|
+
/** Whether MFA is required to complete login */
|
|
57
|
+
mfaRequired?: boolean;
|
|
58
|
+
/** MFA challenge token if MFA is required */
|
|
59
|
+
mfaToken?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Auth state change event types
|
|
63
|
+
*/
|
|
64
|
+
export type AuthStateChangeType = 'login' | 'logout' | 'token_refresh' | 'session_expired' | 'user_updated';
|
|
65
|
+
/**
|
|
66
|
+
* Callback for auth state changes
|
|
67
|
+
*/
|
|
68
|
+
export type AuthStateCallback = (isAuthenticated: boolean, user: User | null, changeType?: AuthStateChangeType) => void;
|
|
69
|
+
//# sourceMappingURL=auth-result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-result.d.ts","sourceRoot":"","sources":["../../../src/api/types/auth-result.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,qBAAqB,GACrB,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,eAAe,GACf,cAAc,GACd,cAAc,GACd,WAAW,GACX,gBAAgB,GAChB,gBAAgB,GAChB,kBAAkB,GAClB,cAAc,GACd,eAAe,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,IAAI,EAAE,aAAa,CAAC;IACpB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,uBAAuB;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,iCAAiC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,OAAO,GACP,QAAQ,GACR,eAAe,GACf,iBAAiB,GACjB,cAAc,CAAC;AAEnB;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,eAAe,EAAE,OAAO,EACxB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,UAAU,CAAC,EAAE,mBAAmB,KAC7B,IAAI,CAAC"}
|