@cranberry-money/shared-services 1.0.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/README.md +288 -0
- package/dist/adapters/MobileApiClient.d.ts +68 -0
- package/dist/adapters/MobileApiClient.d.ts.map +1 -0
- package/dist/adapters/MobileApiClient.js +240 -0
- package/dist/adapters/MobileTokenStorage.d.ts +43 -0
- package/dist/adapters/MobileTokenStorage.d.ts.map +1 -0
- package/dist/adapters/MobileTokenStorage.js +128 -0
- package/dist/adapters/WebApiClient.d.ts +28 -0
- package/dist/adapters/WebApiClient.d.ts.map +1 -0
- package/dist/adapters/WebApiClient.js +119 -0
- package/dist/adapters/WebTokenStorage.d.ts +38 -0
- package/dist/adapters/WebTokenStorage.d.ts.map +1 -0
- package/dist/adapters/WebTokenStorage.js +86 -0
- package/dist/auth/AuthManager.d.ts +81 -0
- package/dist/auth/AuthManager.d.ts.map +1 -0
- package/dist/auth/AuthManager.js +223 -0
- package/dist/auth/createAuthManager.d.ts +63 -0
- package/dist/auth/createAuthManager.d.ts.map +1 -0
- package/dist/auth/createAuthManager.js +103 -0
- package/dist/auth/useAuthManager.d.ts +66 -0
- package/dist/auth/useAuthManager.d.ts.map +1 -0
- package/dist/auth/useAuthManager.js +133 -0
- package/dist/core/BaseApiClient.d.ts +82 -0
- package/dist/core/BaseApiClient.d.ts.map +1 -0
- package/dist/core/BaseApiClient.js +89 -0
- package/dist/core/TokenStorage.d.ts +45 -0
- package/dist/core/TokenStorage.d.ts.map +1 -0
- package/dist/core/TokenStorage.js +23 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/query/QueryClient.d.ts +82 -0
- package/dist/query/QueryClient.d.ts.map +1 -0
- package/dist/query/QueryClient.js +136 -0
- package/dist/query/useAuth.d.ts +64 -0
- package/dist/query/useAuth.d.ts.map +1 -0
- package/dist/query/useAuth.js +144 -0
- package/dist/query/usePortfolios.d.ts +79 -0
- package/dist/query/usePortfolios.d.ts.map +1 -0
- package/dist/query/usePortfolios.js +172 -0
- package/dist/services/AuthService.d.ts +75 -0
- package/dist/services/AuthService.d.ts.map +1 -0
- package/dist/services/AuthService.js +83 -0
- package/dist/services/BaseService.d.ts +48 -0
- package/dist/services/BaseService.d.ts.map +1 -0
- package/dist/services/BaseService.js +51 -0
- package/dist/services/PortfolioService.d.ts +100 -0
- package/dist/services/PortfolioService.d.ts.map +1 -0
- package/dist/services/PortfolioService.js +68 -0
- package/package.json +56 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mobile-specific token storage using secure storage
|
|
3
|
+
*
|
|
4
|
+
* This implementation uses platform-secure storage mechanisms
|
|
5
|
+
* like iOS Keychain and Android Keystore via React Native libraries.
|
|
6
|
+
*
|
|
7
|
+
* For Expo: Uses Expo SecureStore
|
|
8
|
+
* For bare React Native: Would use @react-native-async-storage/async-storage
|
|
9
|
+
* with encryption or react-native-keychain
|
|
10
|
+
*/
|
|
11
|
+
import { BaseTokenStorage } from '../core/TokenStorage';
|
|
12
|
+
// Keys for secure storage
|
|
13
|
+
const STORAGE_KEYS = {
|
|
14
|
+
ACCESS_TOKEN: 'myportfolio_access_token',
|
|
15
|
+
REFRESH_TOKEN: 'myportfolio_refresh_token',
|
|
16
|
+
};
|
|
17
|
+
export class MobileTokenStorage extends BaseTokenStorage {
|
|
18
|
+
constructor(secureStore) {
|
|
19
|
+
super();
|
|
20
|
+
this.secureStore = secureStore;
|
|
21
|
+
}
|
|
22
|
+
async storeTokens(tokens) {
|
|
23
|
+
if (!this.validateTokens(tokens)) {
|
|
24
|
+
throw new Error('Invalid token format');
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
await Promise.all([
|
|
28
|
+
this.secureStore.setItemAsync(STORAGE_KEYS.ACCESS_TOKEN, tokens.access),
|
|
29
|
+
this.secureStore.setItemAsync(STORAGE_KEYS.REFRESH_TOKEN, tokens.refresh),
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new Error(`Failed to store tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async retrieveTokens() {
|
|
37
|
+
try {
|
|
38
|
+
const [access, refresh] = await Promise.all([
|
|
39
|
+
this.secureStore.getItemAsync(STORAGE_KEYS.ACCESS_TOKEN),
|
|
40
|
+
this.secureStore.getItemAsync(STORAGE_KEYS.REFRESH_TOKEN),
|
|
41
|
+
]);
|
|
42
|
+
if (!access || !refresh) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const tokens = { access, refresh };
|
|
46
|
+
if (!this.validateTokens(tokens)) {
|
|
47
|
+
// Clear invalid tokens
|
|
48
|
+
await this.clearTokens();
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return tokens;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('Failed to retrieve tokens:', error);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async clearTokens() {
|
|
59
|
+
try {
|
|
60
|
+
await Promise.all([
|
|
61
|
+
this.secureStore.deleteItemAsync(STORAGE_KEYS.ACCESS_TOKEN),
|
|
62
|
+
this.secureStore.deleteItemAsync(STORAGE_KEYS.REFRESH_TOKEN),
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Failed to clear tokens:', error);
|
|
67
|
+
// Don't throw - clearing should be idempotent
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async hasTokens() {
|
|
71
|
+
try {
|
|
72
|
+
const [access, refresh] = await Promise.all([
|
|
73
|
+
this.secureStore.getItemAsync(STORAGE_KEYS.ACCESS_TOKEN),
|
|
74
|
+
this.secureStore.getItemAsync(STORAGE_KEYS.REFRESH_TOKEN),
|
|
75
|
+
]);
|
|
76
|
+
return !!(access && refresh);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if secure storage is available
|
|
84
|
+
*/
|
|
85
|
+
async isAvailable() {
|
|
86
|
+
try {
|
|
87
|
+
// Try to perform a simple operation to check availability
|
|
88
|
+
await this.secureStore.isAvailableAsync?.();
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Factory function to create MobileTokenStorage with different backends
|
|
98
|
+
*/
|
|
99
|
+
export const createMobileTokenStorage = (secureStore) => {
|
|
100
|
+
return new MobileTokenStorage(secureStore);
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Expo SecureStore adapter
|
|
104
|
+
* Usage: createMobileTokenStorage(createExpoSecureStoreAdapter())
|
|
105
|
+
*/
|
|
106
|
+
export const createExpoSecureStoreAdapter = () => {
|
|
107
|
+
// This would be implemented when actually using Expo
|
|
108
|
+
// For now, return a mock implementation for type checking
|
|
109
|
+
return {
|
|
110
|
+
async setItemAsync(key, value) {
|
|
111
|
+
// In real implementation: await ExpoSecureStore.setItemAsync(key, value);
|
|
112
|
+
console.log(`Mock: Storing ${key} = ${value}`);
|
|
113
|
+
},
|
|
114
|
+
async getItemAsync(key) {
|
|
115
|
+
// In real implementation: return await ExpoSecureStore.getItemAsync(key);
|
|
116
|
+
console.log(`Mock: Retrieving ${key}`);
|
|
117
|
+
return null;
|
|
118
|
+
},
|
|
119
|
+
async deleteItemAsync(key) {
|
|
120
|
+
// In real implementation: await ExpoSecureStore.deleteItemAsync(key);
|
|
121
|
+
console.log(`Mock: Deleting ${key}`);
|
|
122
|
+
},
|
|
123
|
+
async isAvailableAsync() {
|
|
124
|
+
// In real implementation: return await ExpoSecureStore.isAvailableAsync();
|
|
125
|
+
return true;
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-specific API client implementation using fetch or axios
|
|
3
|
+
*
|
|
4
|
+
* This implementation is designed for web browsers and supports
|
|
5
|
+
* cookie-based authentication with proper CORS handling.
|
|
6
|
+
*/
|
|
7
|
+
import { BaseApiClient, type RequestConfig, type ApiResponse, type ApiClientConfig } from '../core/BaseApiClient';
|
|
8
|
+
export declare class WebApiClient extends BaseApiClient {
|
|
9
|
+
constructor(config: ApiClientConfig);
|
|
10
|
+
request<T = unknown>(config: RequestConfig): Promise<ApiResponse<T>>;
|
|
11
|
+
/**
|
|
12
|
+
* Parse response data based on content type
|
|
13
|
+
*/
|
|
14
|
+
private parseResponseData;
|
|
15
|
+
/**
|
|
16
|
+
* Convert Headers to plain object
|
|
17
|
+
*/
|
|
18
|
+
private parseResponseHeaders;
|
|
19
|
+
/**
|
|
20
|
+
* Check if error is an API error
|
|
21
|
+
*/
|
|
22
|
+
private isApiError;
|
|
23
|
+
/**
|
|
24
|
+
* Handle token refresh for web (cookie-based)
|
|
25
|
+
*/
|
|
26
|
+
protected handleTokenRefresh(): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=WebApiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebApiClient.d.ts","sourceRoot":"","sources":["../../src/adapters/WebApiClient.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAElH,qBAAa,YAAa,SAAQ,aAAa;gBACjC,MAAM,EAAE,eAAe;IAU7B,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAoE1E;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;OAEG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;cACa,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;CAcvD"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-specific API client implementation using fetch or axios
|
|
3
|
+
*
|
|
4
|
+
* This implementation is designed for web browsers and supports
|
|
5
|
+
* cookie-based authentication with proper CORS handling.
|
|
6
|
+
*/
|
|
7
|
+
import { HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_APPLICATION_JSON } from '@myportfolio/shared-constants';
|
|
8
|
+
import { BaseApiClient } from '../core/BaseApiClient';
|
|
9
|
+
export class WebApiClient extends BaseApiClient {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super({
|
|
12
|
+
...config,
|
|
13
|
+
defaultHeaders: {
|
|
14
|
+
[HTTP_HEADER_CONTENT_TYPE]: CONTENT_TYPE_APPLICATION_JSON,
|
|
15
|
+
...config.defaultHeaders,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async request(config) {
|
|
20
|
+
const url = this.buildUrl(config.url);
|
|
21
|
+
const headers = await this.buildHeaders(config.headers);
|
|
22
|
+
// Build fetch options
|
|
23
|
+
const fetchOptions = {
|
|
24
|
+
method: config.method || 'GET',
|
|
25
|
+
headers,
|
|
26
|
+
credentials: this.withCredentials ? 'include' : 'omit',
|
|
27
|
+
};
|
|
28
|
+
// Add body for non-GET requests
|
|
29
|
+
if (config.data && config.method !== 'GET') {
|
|
30
|
+
if (headers[HTTP_HEADER_CONTENT_TYPE] === CONTENT_TYPE_APPLICATION_JSON) {
|
|
31
|
+
fetchOptions.body = JSON.stringify(config.data);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
fetchOptions.body = config.data;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Add query parameters for GET requests
|
|
38
|
+
const requestUrl = config.params && config.method === 'GET'
|
|
39
|
+
? `${url}?${new URLSearchParams(config.params).toString()}`
|
|
40
|
+
: url;
|
|
41
|
+
try {
|
|
42
|
+
const response = await fetch(requestUrl, fetchOptions);
|
|
43
|
+
// Handle non-2xx responses
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const errorData = await this.parseResponseData(response);
|
|
46
|
+
throw this.createError(response.statusText || 'Request failed', response.status, 'HTTP_ERROR', errorData);
|
|
47
|
+
}
|
|
48
|
+
const data = await this.parseResponseData(response);
|
|
49
|
+
return {
|
|
50
|
+
data,
|
|
51
|
+
status: response.status,
|
|
52
|
+
statusText: response.statusText,
|
|
53
|
+
headers: this.parseResponseHeaders(response.headers),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// Handle network errors
|
|
58
|
+
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
59
|
+
throw this.createError('Network error', 0, 'NETWORK_ERROR');
|
|
60
|
+
}
|
|
61
|
+
// Re-throw API errors
|
|
62
|
+
if (this.isApiError(error)) {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
// Handle unexpected errors
|
|
66
|
+
throw this.createError(error instanceof Error ? error.message : 'Unknown error', 0, 'UNEXPECTED_ERROR');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse response data based on content type
|
|
71
|
+
*/
|
|
72
|
+
async parseResponseData(response) {
|
|
73
|
+
const contentType = response.headers.get('content-type') || '';
|
|
74
|
+
if (contentType.includes('application/json')) {
|
|
75
|
+
return response.json();
|
|
76
|
+
}
|
|
77
|
+
if (contentType.includes('text/')) {
|
|
78
|
+
return response.text();
|
|
79
|
+
}
|
|
80
|
+
// For other content types, return as blob
|
|
81
|
+
return response.blob();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Convert Headers to plain object
|
|
85
|
+
*/
|
|
86
|
+
parseResponseHeaders(headers) {
|
|
87
|
+
const headerObj = {};
|
|
88
|
+
headers.forEach((value, key) => {
|
|
89
|
+
headerObj[key] = value;
|
|
90
|
+
});
|
|
91
|
+
return headerObj;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Check if error is an API error
|
|
95
|
+
*/
|
|
96
|
+
isApiError(error) {
|
|
97
|
+
return typeof error === 'object' &&
|
|
98
|
+
error !== null &&
|
|
99
|
+
'message' in error &&
|
|
100
|
+
'status' in error;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Handle token refresh for web (cookie-based)
|
|
104
|
+
*/
|
|
105
|
+
async handleTokenRefresh() {
|
|
106
|
+
try {
|
|
107
|
+
// For cookie-based auth, make a request to refresh endpoint
|
|
108
|
+
// The server will set new cookies automatically
|
|
109
|
+
const response = await this.request({
|
|
110
|
+
url: '/auth/refresh',
|
|
111
|
+
method: 'POST',
|
|
112
|
+
});
|
|
113
|
+
return response.status === 200;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-specific token storage using HTTP-only cookies
|
|
3
|
+
*
|
|
4
|
+
* This implementation relies on the backend to set HTTP-only cookies
|
|
5
|
+
* and doesn't directly manage tokens on the client side for security.
|
|
6
|
+
*
|
|
7
|
+
* For web applications using cookie-based authentication, the tokens
|
|
8
|
+
* are automatically included in requests via browser cookie handling.
|
|
9
|
+
*/
|
|
10
|
+
import { BaseTokenStorage, type TokenPair } from '../core/TokenStorage';
|
|
11
|
+
export declare class WebTokenStorage extends BaseTokenStorage {
|
|
12
|
+
/**
|
|
13
|
+
* For cookie-based auth, tokens are managed by the browser/server
|
|
14
|
+
* This method would typically not store anything client-side
|
|
15
|
+
*/
|
|
16
|
+
storeTokens(tokens: TokenPair): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* For cookie-based auth, we can't directly access HTTP-only cookies
|
|
19
|
+
* This method checks if authentication cookies likely exist
|
|
20
|
+
*/
|
|
21
|
+
retrieveTokens(): Promise<TokenPair | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Clear authentication state
|
|
24
|
+
* In cookie systems, this would typically call a logout endpoint
|
|
25
|
+
*/
|
|
26
|
+
clearTokens(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Check authentication status
|
|
29
|
+
* In cookie systems, this would check if auth cookies exist
|
|
30
|
+
*/
|
|
31
|
+
hasTokens(): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if authentication cookies exist
|
|
34
|
+
* This is a web-specific method for cookie-based auth
|
|
35
|
+
*/
|
|
36
|
+
hasCookies(): boolean;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=WebTokenStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebTokenStorage.d.ts","sourceRoot":"","sources":["../../src/adapters/WebTokenStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAExE,qBAAa,eAAgB,SAAQ,gBAAgB;IACnD;;;OAGG;IACG,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAWnD;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAuBjD;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAUlC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAYnC;;;OAGG;IACH,UAAU,IAAI,OAAO;CAOtB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web-specific token storage using HTTP-only cookies
|
|
3
|
+
*
|
|
4
|
+
* This implementation relies on the backend to set HTTP-only cookies
|
|
5
|
+
* and doesn't directly manage tokens on the client side for security.
|
|
6
|
+
*
|
|
7
|
+
* For web applications using cookie-based authentication, the tokens
|
|
8
|
+
* are automatically included in requests via browser cookie handling.
|
|
9
|
+
*/
|
|
10
|
+
import { BaseTokenStorage } from '../core/TokenStorage';
|
|
11
|
+
export class WebTokenStorage extends BaseTokenStorage {
|
|
12
|
+
/**
|
|
13
|
+
* For cookie-based auth, tokens are managed by the browser/server
|
|
14
|
+
* This method would typically not store anything client-side
|
|
15
|
+
*/
|
|
16
|
+
async storeTokens(tokens) {
|
|
17
|
+
// In cookie-based systems, tokens are set by the server as HTTP-only cookies
|
|
18
|
+
// The client doesn't need to manually store them
|
|
19
|
+
// For development/testing, we could store in sessionStorage
|
|
20
|
+
// but in production, this would be handled by server-set cookies
|
|
21
|
+
if (typeof window !== 'undefined' && window.sessionStorage) {
|
|
22
|
+
sessionStorage.setItem('auth_tokens', JSON.stringify(tokens));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* For cookie-based auth, we can't directly access HTTP-only cookies
|
|
27
|
+
* This method checks if authentication cookies likely exist
|
|
28
|
+
*/
|
|
29
|
+
async retrieveTokens() {
|
|
30
|
+
// In a real cookie-based system, we can't access HTTP-only cookies from JS
|
|
31
|
+
// This would typically make a request to a /auth/status endpoint
|
|
32
|
+
// For development/testing, check sessionStorage
|
|
33
|
+
if (typeof window !== 'undefined' && window.sessionStorage) {
|
|
34
|
+
const stored = sessionStorage.getItem('auth_tokens');
|
|
35
|
+
if (stored) {
|
|
36
|
+
try {
|
|
37
|
+
const tokens = JSON.parse(stored);
|
|
38
|
+
if (this.validateTokens(tokens)) {
|
|
39
|
+
return tokens;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Invalid stored data, clear it
|
|
44
|
+
sessionStorage.removeItem('auth_tokens');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Clear authentication state
|
|
52
|
+
* In cookie systems, this would typically call a logout endpoint
|
|
53
|
+
*/
|
|
54
|
+
async clearTokens() {
|
|
55
|
+
// Clear any client-side storage
|
|
56
|
+
if (typeof window !== 'undefined' && window.sessionStorage) {
|
|
57
|
+
sessionStorage.removeItem('auth_tokens');
|
|
58
|
+
}
|
|
59
|
+
// In a real implementation, this would make a POST to /auth/signout
|
|
60
|
+
// to clear the HTTP-only cookies on the server side
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check authentication status
|
|
64
|
+
* In cookie systems, this would check if auth cookies exist
|
|
65
|
+
*/
|
|
66
|
+
async hasTokens() {
|
|
67
|
+
// For cookie-based systems, we'd typically check document.cookie
|
|
68
|
+
// or make a request to an auth status endpoint
|
|
69
|
+
// For development, check sessionStorage
|
|
70
|
+
if (typeof window !== 'undefined' && window.sessionStorage) {
|
|
71
|
+
return sessionStorage.getItem('auth_tokens') !== null;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if authentication cookies exist
|
|
77
|
+
* This is a web-specific method for cookie-based auth
|
|
78
|
+
*/
|
|
79
|
+
hasCookies() {
|
|
80
|
+
if (typeof document === 'undefined')
|
|
81
|
+
return false;
|
|
82
|
+
// Check for common auth cookie names
|
|
83
|
+
const cookies = document.cookie;
|
|
84
|
+
return cookies.includes('access') || cookies.includes('sessionid') || cookies.includes('auth');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform Authentication Manager
|
|
3
|
+
*
|
|
4
|
+
* Centralizes authentication logic and state management across web and mobile platforms.
|
|
5
|
+
* Handles token storage, automatic refresh, and authentication state persistence.
|
|
6
|
+
*/
|
|
7
|
+
import type { TokenStorage } from '../core/TokenStorage';
|
|
8
|
+
import type { BaseApiClient } from '../core/BaseApiClient';
|
|
9
|
+
import { type SigninPayload, type SignupPayload, type UserProfile } from '../services/AuthService';
|
|
10
|
+
export interface AuthState {
|
|
11
|
+
isAuthenticated: boolean;
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
user: UserProfile | null;
|
|
14
|
+
error: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface AuthManagerConfig {
|
|
17
|
+
apiClient: BaseApiClient;
|
|
18
|
+
tokenStorage: TokenStorage;
|
|
19
|
+
onAuthStateChange?: (state: AuthState) => void;
|
|
20
|
+
autoRefreshEnabled?: boolean;
|
|
21
|
+
refreshThreshold?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class AuthManager {
|
|
24
|
+
private apiClient;
|
|
25
|
+
private tokenStorage;
|
|
26
|
+
private authService;
|
|
27
|
+
private onAuthStateChange?;
|
|
28
|
+
private autoRefreshEnabled;
|
|
29
|
+
private refreshTimer?;
|
|
30
|
+
private currentState;
|
|
31
|
+
constructor(config: AuthManagerConfig);
|
|
32
|
+
/**
|
|
33
|
+
* Get current authentication state
|
|
34
|
+
*/
|
|
35
|
+
getState(): AuthState;
|
|
36
|
+
/**
|
|
37
|
+
* Initialize authentication manager
|
|
38
|
+
* Checks for existing tokens and validates authentication state
|
|
39
|
+
*/
|
|
40
|
+
initialize(): Promise<AuthState>;
|
|
41
|
+
/**
|
|
42
|
+
* Sign in user with credentials
|
|
43
|
+
*/
|
|
44
|
+
signin(credentials: SigninPayload): Promise<AuthState>;
|
|
45
|
+
/**
|
|
46
|
+
* Sign out user
|
|
47
|
+
*/
|
|
48
|
+
signout(): Promise<AuthState>;
|
|
49
|
+
/**
|
|
50
|
+
* Sign up new user
|
|
51
|
+
*/
|
|
52
|
+
signup(userData: SignupPayload): Promise<{
|
|
53
|
+
success: boolean;
|
|
54
|
+
error?: string;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Refresh authentication tokens
|
|
58
|
+
*/
|
|
59
|
+
refreshTokens(): Promise<boolean>;
|
|
60
|
+
/**
|
|
61
|
+
* Update user profile in state
|
|
62
|
+
*/
|
|
63
|
+
refreshProfile(): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Start automatic token refresh
|
|
66
|
+
*/
|
|
67
|
+
private startAutoRefresh;
|
|
68
|
+
/**
|
|
69
|
+
* Stop automatic token refresh
|
|
70
|
+
*/
|
|
71
|
+
private stopAutoRefresh;
|
|
72
|
+
/**
|
|
73
|
+
* Update authentication state and notify listeners
|
|
74
|
+
*/
|
|
75
|
+
private updateState;
|
|
76
|
+
/**
|
|
77
|
+
* Clean up resources
|
|
78
|
+
*/
|
|
79
|
+
dispose(): void;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=AuthManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthManager.d.ts","sourceRoot":"","sources":["../../src/auth/AuthManager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAa,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAe,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEhH,MAAM,WAAW,SAAS;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,aAAa,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;IAC3B,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,iBAAiB,CAAC,CAA6B;IACvD,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,YAAY,CAAC,CAAiB;IAEtC,OAAO,CAAC,YAAY,CAKlB;gBAEU,MAAM,EAAE,iBAAiB;IAWrC;;OAEG;IACH,QAAQ,IAAI,SAAS;IAIrB;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC;IA6CtC;;OAEG;IACG,MAAM,CAAC,WAAW,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC;IAsC5D;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC;IAyBnC;;OAEG;IACG,MAAM,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAsBpF;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IA2BvC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
|