@egintegrations/auth-services 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 ADDED
@@ -0,0 +1,28 @@
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/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-01-22
9
+
10
+ ### Added
11
+ - Initial release of @egintegrations/auth-services
12
+ - TokenManager - Generic token storage with secure store
13
+ - SingletonTokenManager - Singleton pattern for simple token management
14
+ - BiometricAuth - Configurable biometric authentication (Face ID / Touch ID)
15
+ - SecureKeyStore - Generic secure key/value storage with prefix support
16
+ - Convenience functions for direct secure storage access
17
+ - Comprehensive test suite with 100% coverage (69 tests)
18
+ - Full TypeScript support with type definitions
19
+ - ESM and CommonJS module support
20
+
21
+ ### Notes
22
+ - Extracted from AutismTrainerApp authentication services
23
+ - Refactored to be configurable and reusable
24
+ - TokenManager supports custom storage keys
25
+ - BiometricAuth supports custom prompt messages and fallback labels
26
+ - SecureKeyStore supports key prefixes for namespace isolation
27
+ - Removed app-specific user management and role-based access control
28
+ - Works with Expo and bare React Native applications
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EGI Integrations
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,554 @@
1
+ # @egintegrations/auth-services
2
+
3
+ React Native authentication utilities for token management, biometric authentication, and secure key storage. Works with Expo and bare React Native.
4
+
5
+ ## Features
6
+
7
+ - **TokenManager**: Secure token storage and retrieval
8
+ - **BiometricAuth**: Face ID / Touch ID authentication
9
+ - **SecureKeyStore**: Generic secure key/value storage
10
+ - **TypeScript**: Full type safety with TypeScript support
11
+ - **Configurable**: Customizable storage keys, prompts, and behavior
12
+ - **Well-tested**: 100% test coverage (69 tests)
13
+ - **Dual Module**: ESM and CommonJS support
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @egintegrations/auth-services expo-secure-store expo-local-authentication
19
+ ```
20
+
21
+ ### Peer Dependencies
22
+
23
+ This package requires:
24
+ - `expo-secure-store` >= 12.0.0
25
+ - `expo-local-authentication` >= 13.0.0
26
+ - `react-native` >= 0.70.0
27
+
28
+ ## Quick Start
29
+
30
+ ```typescript
31
+ import { TokenManager, BiometricAuth, SecureKeyStore } from '@egintegrations/auth-services';
32
+
33
+ // Token management
34
+ const tokenManager = new TokenManager();
35
+ await tokenManager.saveToken('your-auth-token');
36
+ const token = await tokenManager.getToken();
37
+
38
+ // Biometric authentication
39
+ const bioAuth = new BiometricAuth();
40
+ if (await bioAuth.canAuthenticate()) {
41
+ await bioAuth.enable('username');
42
+ const username = await bioAuth.authenticate();
43
+ }
44
+
45
+ // Secure key storage
46
+ const keyStore = new SecureKeyStore({ keyPrefix: 'app_' });
47
+ await keyStore.set('api_key', 'secret-key');
48
+ const apiKey = await keyStore.get('api_key');
49
+ ```
50
+
51
+ ## TokenManager
52
+
53
+ Manages authentication tokens in secure storage.
54
+
55
+ ### Basic Usage
56
+
57
+ ```typescript
58
+ import { TokenManager } from '@egintegrations/auth-services';
59
+
60
+ // Create manager with default storage key
61
+ const tokenManager = new TokenManager();
62
+
63
+ // Save token
64
+ await tokenManager.saveToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
65
+
66
+ // Get token
67
+ const token = await tokenManager.getToken();
68
+
69
+ // Check if token exists
70
+ const hasToken = await tokenManager.hasToken();
71
+
72
+ // Clear token
73
+ await tokenManager.clearToken();
74
+ ```
75
+
76
+ ### Custom Storage Key
77
+
78
+ ```typescript
79
+ // Use custom storage key for multiple token types
80
+ const accessTokenManager = new TokenManager({ storageKey: 'access_token' });
81
+ const refreshTokenManager = new TokenManager({ storageKey: 'refresh_token' });
82
+
83
+ await accessTokenManager.saveToken('access-token-123');
84
+ await refreshTokenManager.saveToken('refresh-token-456');
85
+ ```
86
+
87
+ ### Singleton Pattern
88
+
89
+ For simple apps with single token:
90
+
91
+ ```typescript
92
+ import { SingletonTokenManager } from '@egintegrations/auth-services';
93
+
94
+ // All methods are static
95
+ await SingletonTokenManager.saveToken('token');
96
+ const token = await SingletonTokenManager.getToken();
97
+ const hasToken = await SingletonTokenManager.hasToken();
98
+ await SingletonTokenManager.clearToken();
99
+ ```
100
+
101
+ ## BiometricAuth
102
+
103
+ Manages biometric authentication (Face ID / Touch ID).
104
+
105
+ ### Check Availability
106
+
107
+ ```typescript
108
+ import { BiometricAuth } from '@egintegrations/auth-services';
109
+
110
+ const bioAuth = new BiometricAuth();
111
+
112
+ // Check if device supports biometric auth
113
+ const canAuth = await bioAuth.canAuthenticate();
114
+
115
+ // Get biometric type
116
+ const type = await bioAuth.getBiometricType(); // 'faceId' | 'touchId' | 'fingerprint' | 'none'
117
+ ```
118
+
119
+ ### Enable Biometric Login
120
+
121
+ ```typescript
122
+ const bioAuth = new BiometricAuth();
123
+
124
+ try {
125
+ // Prompt user to authenticate and enable biometric login
126
+ await bioAuth.enable('username');
127
+ console.log('Biometric login enabled');
128
+ } catch (error) {
129
+ console.error('Failed to enable biometric:', error.message);
130
+ }
131
+ ```
132
+
133
+ ### Authenticate with Biometrics
134
+
135
+ ```typescript
136
+ const bioAuth = new BiometricAuth();
137
+
138
+ try {
139
+ // Prompt biometric authentication
140
+ const username = await bioAuth.authenticate();
141
+ console.log('Authenticated as:', username);
142
+ } catch (error) {
143
+ console.error('Authentication failed:', error.message);
144
+ }
145
+ ```
146
+
147
+ ### Custom Prompts
148
+
149
+ ```typescript
150
+ const bioAuth = new BiometricAuth({
151
+ promptMessages: {
152
+ enable: 'Enable Face ID for MyApp',
153
+ authenticate: 'Sign in to MyApp',
154
+ },
155
+ fallbackLabel: 'Use Password',
156
+ });
157
+
158
+ await bioAuth.enable('user@example.com');
159
+ const username = await bioAuth.authenticate();
160
+ ```
161
+
162
+ ### Manage Saved Username
163
+
164
+ ```typescript
165
+ const bioAuth = new BiometricAuth();
166
+
167
+ // Save username without prompting
168
+ await bioAuth.saveUsername('newuser@example.com');
169
+
170
+ // Get saved username (only if biometric is enabled)
171
+ const savedUsername = await bioAuth.getSavedUsername();
172
+
173
+ // Clear saved username
174
+ await bioAuth.clearSavedUsername();
175
+
176
+ // Disable biometric completely
177
+ await bioAuth.disable();
178
+ ```
179
+
180
+ ### Complete Login Flow Example
181
+
182
+ ```typescript
183
+ import { BiometricAuth, TokenManager } from '@egintegrations/auth-services';
184
+
185
+ const bioAuth = new BiometricAuth();
186
+ const tokenManager = new TokenManager();
187
+
188
+ async function loginWithBiometric() {
189
+ try {
190
+ // Check if biometric is enabled
191
+ const isEnabled = await bioAuth.isEnabled();
192
+
193
+ if (!isEnabled) {
194
+ console.log('Biometric not enabled, use password login');
195
+ return;
196
+ }
197
+
198
+ // Authenticate
199
+ const username = await bioAuth.authenticate();
200
+
201
+ // Call your API to get token
202
+ const response = await fetch('https://api.example.com/auth/biometric', {
203
+ method: 'POST',
204
+ body: JSON.stringify({ username }),
205
+ });
206
+
207
+ const { token } = await response.json();
208
+
209
+ // Save token
210
+ await tokenManager.saveToken(token);
211
+
212
+ console.log('Logged in as:', username);
213
+ } catch (error) {
214
+ console.error('Biometric login failed:', error.message);
215
+ }
216
+ }
217
+ ```
218
+
219
+ ## SecureKeyStore
220
+
221
+ Generic secure key-value storage with namespace support.
222
+
223
+ ### Basic Usage
224
+
225
+ ```typescript
226
+ import { SecureKeyStore } from '@egintegrations/auth-services';
227
+
228
+ const store = new SecureKeyStore();
229
+
230
+ // Store value
231
+ await store.set('api_key', 'sk-1234567890');
232
+
233
+ // Retrieve value
234
+ const apiKey = await store.get('api_key');
235
+
236
+ // Check if key exists
237
+ const hasKey = await store.has('api_key');
238
+
239
+ // Delete key
240
+ await store.delete('api_key');
241
+ ```
242
+
243
+ ### Namespace Isolation
244
+
245
+ ```typescript
246
+ // Create separate stores for different purposes
247
+ const apiStore = new SecureKeyStore({ keyPrefix: 'api_' });
248
+ const userStore = new SecureKeyStore({ keyPrefix: 'user_' });
249
+
250
+ await apiStore.set('openai_key', 'sk-...');
251
+ await userStore.set('preference', 'dark_mode');
252
+
253
+ // Keys are isolated
254
+ const apiKeys = await apiStore.get('openai_key'); // Works
255
+ const userKeys = await userStore.get('openai_key'); // null (different namespace)
256
+ ```
257
+
258
+ ### Clear Multiple Keys
259
+
260
+ ```typescript
261
+ const store = new SecureKeyStore({ keyPrefix: 'app_' });
262
+
263
+ await store.set('key1', 'value1');
264
+ await store.set('key2', 'value2');
265
+ await store.set('key3', 'value3');
266
+
267
+ // Clear specific keys
268
+ await store.clear(['key1', 'key2']);
269
+ ```
270
+
271
+ ### Convenience Functions
272
+
273
+ For simple use cases without creating a store instance:
274
+
275
+ ```typescript
276
+ import {
277
+ getSecureKey,
278
+ setSecureKey,
279
+ deleteSecureKey,
280
+ hasSecureKey,
281
+ } from '@egintegrations/auth-services';
282
+
283
+ // Direct access to secure storage (no prefix)
284
+ await setSecureKey('my_key', 'my_value');
285
+ const value = await getSecureKey('my_key');
286
+ const exists = await hasSecureKey('my_key');
287
+ await deleteSecureKey('my_key');
288
+ ```
289
+
290
+ ## Advanced Examples
291
+
292
+ ### Complete Authentication System
293
+
294
+ ```typescript
295
+ import {
296
+ TokenManager,
297
+ BiometricAuth,
298
+ SecureKeyStore,
299
+ } from '@egintegrations/auth-services';
300
+
301
+ class AuthService {
302
+ private tokenManager = new TokenManager();
303
+ private bioAuth = new BiometricAuth({
304
+ promptMessages: {
305
+ enable: 'Enable Face ID for faster login',
306
+ authenticate: 'Authenticate to continue',
307
+ },
308
+ });
309
+ private keyStore = new SecureKeyStore({ keyPrefix: 'auth_' });
310
+
311
+ async login(username: string, password: string): Promise<void> {
312
+ // Call your API
313
+ const response = await fetch('https://api.example.com/login', {
314
+ method: 'POST',
315
+ headers: { 'Content-Type': 'application/json' },
316
+ body: JSON.stringify({ username, password }),
317
+ });
318
+
319
+ const { token } = await response.json();
320
+
321
+ // Save token
322
+ await this.tokenManager.saveToken(token);
323
+
324
+ // Offer biometric enrollment if available
325
+ const canAuth = await this.bioAuth.canAuthenticate();
326
+ if (canAuth) {
327
+ await this.bioAuth.enable(username);
328
+ }
329
+ }
330
+
331
+ async loginWithBiometric(): Promise<void> {
332
+ const username = await this.bioAuth.authenticate();
333
+
334
+ // Exchange biometric authentication for token
335
+ const response = await fetch('https://api.example.com/auth/biometric', {
336
+ method: 'POST',
337
+ headers: { 'Content-Type': 'application/json' },
338
+ body: JSON.stringify({ username }),
339
+ });
340
+
341
+ const { token } = await response.json();
342
+ await this.tokenManager.saveToken(token);
343
+ }
344
+
345
+ async logout(): Promise<void> {
346
+ await this.tokenManager.clearToken();
347
+ // Keep biometric enabled for next login
348
+ }
349
+
350
+ async getAuthToken(): Promise<string | null> {
351
+ return this.tokenManager.getToken();
352
+ }
353
+
354
+ async isAuthenticated(): Promise<boolean> {
355
+ return this.tokenManager.hasToken();
356
+ }
357
+
358
+ async saveApiKey(service: string, key: string): Promise<void> {
359
+ await this.keyStore.set(`${service}_key`, key);
360
+ }
361
+
362
+ async getApiKey(service: string): Promise<string | null> {
363
+ return this.keyStore.get(`${service}_key`);
364
+ }
365
+ }
366
+
367
+ // Usage
368
+ const auth = new AuthService();
369
+
370
+ // Normal login
371
+ await auth.login('user@example.com', 'password123');
372
+
373
+ // Biometric login
374
+ if (await auth.bioAuth.canAuthenticate()) {
375
+ await auth.loginWithBiometric();
376
+ }
377
+
378
+ // Get auth token for API calls
379
+ const token = await auth.getAuthToken();
380
+ ```
381
+
382
+ ### API Integration with TokenManager
383
+
384
+ ```typescript
385
+ import { TokenManager } from '@egintegrations/auth-services';
386
+ import { ApiClient } from '@egintegrations/api-client';
387
+
388
+ const tokenManager = new TokenManager();
389
+
390
+ // Create API client
391
+ const apiClient = new ApiClient({
392
+ baseUrl: 'https://api.example.com',
393
+ });
394
+
395
+ // Add token to all requests
396
+ apiClient.addRequestInterceptor(async (config) => {
397
+ const token = await tokenManager.getToken();
398
+ if (token) {
399
+ config.headers = config.headers || {};
400
+ config.headers.Authorization = `Bearer ${token}`;
401
+ }
402
+ return config;
403
+ });
404
+
405
+ // Handle 401 errors
406
+ apiClient.addErrorInterceptor(async (error) => {
407
+ if (error.response?.status === 401) {
408
+ await tokenManager.clearToken();
409
+ // Redirect to login
410
+ }
411
+ throw error;
412
+ });
413
+ ```
414
+
415
+ ## API Reference
416
+
417
+ ### TokenManager
418
+
419
+ #### Constructor
420
+ ```typescript
421
+ new TokenManager(config?: TokenManagerConfig)
422
+ ```
423
+
424
+ **Config:**
425
+ - `storageKey?: string` - Storage key (default: 'auth_token')
426
+
427
+ #### Methods
428
+ - `getToken(): Promise<string | null>` - Get stored token
429
+ - `saveToken(token: string): Promise<void>` - Save token
430
+ - `clearToken(): Promise<void>` - Delete token
431
+ - `hasToken(): Promise<boolean>` - Check if token exists
432
+
433
+ ### SingletonTokenManager
434
+
435
+ Static methods matching TokenManager interface.
436
+
437
+ ### BiometricAuth
438
+
439
+ #### Constructor
440
+ ```typescript
441
+ new BiometricAuth(config?: BiometricAuthConfig)
442
+ ```
443
+
444
+ **Config:**
445
+ - `savedUsernameKey?: string` - Username storage key (default: 'biometric_username')
446
+ - `enabledKey?: string` - Enabled flag key (default: 'biometric_enabled')
447
+ - `promptMessages?: { enable?: string, authenticate?: string }` - Custom prompts
448
+ - `fallbackLabel?: string` - Fallback button label (default: 'Use password')
449
+
450
+ #### Methods
451
+ - `canAuthenticate(): Promise<boolean>` - Check device support
452
+ - `isEnabled(): Promise<boolean>` - Check if biometric enabled
453
+ - `enable(username: string): Promise<void>` - Enable biometric login
454
+ - `disable(): Promise<void>` - Disable biometric login
455
+ - `getBiometricType(): Promise<BiometricType>` - Get biometric type
456
+ - `authenticate(): Promise<string>` - Authenticate and get username
457
+ - `saveUsername(username: string): Promise<void>` - Save username
458
+ - `clearSavedUsername(): Promise<void>` - Clear username
459
+ - `getSavedUsername(): Promise<string | null>` - Get saved username
460
+
461
+ ### SecureKeyStore
462
+
463
+ #### Constructor
464
+ ```typescript
465
+ new SecureKeyStore(config?: SecureKeyStoreConfig)
466
+ ```
467
+
468
+ **Config:**
469
+ - `keyPrefix?: string` - Key prefix for namespace isolation (default: 'secure_key_')
470
+
471
+ #### Methods
472
+ - `get(key: string): Promise<string | null>` - Get value
473
+ - `set(key: string, value: string): Promise<void>` - Set value
474
+ - `delete(key: string): Promise<void>` - Delete key
475
+ - `has(key: string): Promise<boolean>` - Check if key exists
476
+ - `clear(keys?: string[]): Promise<void>` - Clear multiple keys
477
+
478
+ ## Error Handling
479
+
480
+ All methods can throw errors. Always use try-catch:
481
+
482
+ ```typescript
483
+ try {
484
+ await tokenManager.saveToken(token);
485
+ } catch (error) {
486
+ console.error('Failed to save token:', error);
487
+ }
488
+
489
+ try {
490
+ const username = await bioAuth.authenticate();
491
+ } catch (error) {
492
+ console.error('Biometric auth failed:', error.message);
493
+ // Handle failure (show password login)
494
+ }
495
+ ```
496
+
497
+ ## Development
498
+
499
+ ```bash
500
+ # Install dependencies
501
+ npm install
502
+
503
+ # Build
504
+ npm run build
505
+
506
+ # Run tests
507
+ npm test
508
+
509
+ # Run tests with coverage
510
+ npm test -- --coverage
511
+
512
+ # Type check
513
+ npm run typecheck
514
+
515
+ # Lint
516
+ npm run lint
517
+ ```
518
+
519
+ ## Contributing
520
+
521
+ Contributions are welcome! Please ensure:
522
+
523
+ 1. All tests pass (`npm test`)
524
+ 2. Code coverage remains 100%
525
+ 3. TypeScript types are properly defined
526
+ 4. Code follows the existing style (run `npm run lint`)
527
+
528
+ ## License
529
+
530
+ MIT © EGI Integrations
531
+
532
+ ## Extracted From
533
+
534
+ This package was extracted from AutismTrainerApp's authentication services and refactored to be reusable across React Native applications.
535
+
536
+ **What was removed:**
537
+ - App-specific user management (roles, permissions, progress tracking)
538
+ - Local user database and AsyncStorage user management
539
+ - Supervised user relationships
540
+ - Application-specific authentication flows
541
+
542
+ **What was kept and improved:**
543
+ - Token management (now configurable)
544
+ - Biometric authentication (now with custom prompts)
545
+ - Secure key storage (now with namespace support)
546
+
547
+ ## Related Packages
548
+
549
+ - [@egintegrations/api-client](https://www.npmjs.com/package/@egintegrations/api-client) - HTTP client for API integrations
550
+ - [@egintegrations/core-utils](https://www.npmjs.com/package/@egintegrations/core-utils) - Utility functions
551
+
552
+ ## Support
553
+
554
+ For issues and questions, please open an issue on [GitHub](https://github.com/EGIntegrations/egi-comp-library/issues).
@@ -0,0 +1,68 @@
1
+ interface TokenManagerConfig {
2
+ storageKey?: string;
3
+ }
4
+ declare class TokenManager {
5
+ private storageKey;
6
+ constructor(config?: TokenManagerConfig);
7
+ getToken(): Promise<string | null>;
8
+ saveToken(token: string): Promise<void>;
9
+ clearToken(): Promise<void>;
10
+ hasToken(): Promise<boolean>;
11
+ }
12
+ declare class SingletonTokenManager {
13
+ private static instance;
14
+ private static defaultKey;
15
+ private constructor();
16
+ static getInstance(storageKey?: string): TokenManager;
17
+ static getToken(): Promise<string | null>;
18
+ static saveToken(token: string): Promise<void>;
19
+ static clearToken(): Promise<void>;
20
+ static hasToken(): Promise<boolean>;
21
+ }
22
+
23
+ interface BiometricAuthConfig {
24
+ savedUsernameKey?: string;
25
+ enabledKey?: string;
26
+ promptMessages?: {
27
+ enable?: string;
28
+ authenticate?: string;
29
+ };
30
+ fallbackLabel?: string;
31
+ }
32
+ type BiometricType = 'faceId' | 'touchId' | 'fingerprint' | 'none';
33
+ declare class BiometricAuth {
34
+ private savedUsernameKey;
35
+ private enabledKey;
36
+ private promptMessages;
37
+ private fallbackLabel;
38
+ constructor(config?: BiometricAuthConfig);
39
+ canAuthenticate(): Promise<boolean>;
40
+ isEnabled(): Promise<boolean>;
41
+ enable(username: string): Promise<void>;
42
+ disable(): Promise<void>;
43
+ getBiometricType(): Promise<BiometricType>;
44
+ authenticate(): Promise<string>;
45
+ saveUsername(username: string): Promise<void>;
46
+ clearSavedUsername(): Promise<void>;
47
+ getSavedUsername(): Promise<string | null>;
48
+ }
49
+
50
+ interface SecureKeyStoreConfig {
51
+ keyPrefix?: string;
52
+ }
53
+ declare class SecureKeyStore {
54
+ private keyPrefix;
55
+ constructor(config?: SecureKeyStoreConfig);
56
+ private getFullKey;
57
+ get(key: string): Promise<string | null>;
58
+ set(key: string, value: string): Promise<void>;
59
+ delete(key: string): Promise<void>;
60
+ has(key: string): Promise<boolean>;
61
+ clear(keys?: string[]): Promise<void>;
62
+ }
63
+ declare function getSecureKey(key: string): Promise<string | null>;
64
+ declare function setSecureKey(key: string, value: string): Promise<void>;
65
+ declare function deleteSecureKey(key: string): Promise<void>;
66
+ declare function hasSecureKey(key: string): Promise<boolean>;
67
+
68
+ export { BiometricAuth, type BiometricAuthConfig, type BiometricType, SecureKeyStore, type SecureKeyStoreConfig, SingletonTokenManager, TokenManager, type TokenManagerConfig, deleteSecureKey, getSecureKey, hasSecureKey, setSecureKey };