@oxyhq/core 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 +50 -0
- package/dist/cjs/AuthManager.js +361 -0
- package/dist/cjs/CrossDomainAuth.js +258 -0
- package/dist/cjs/HttpService.js +618 -0
- package/dist/cjs/OxyServices.base.js +263 -0
- package/dist/cjs/OxyServices.errors.js +22 -0
- package/dist/cjs/OxyServices.js +63 -0
- package/dist/cjs/constants/version.js +16 -0
- package/dist/cjs/crypto/index.js +20 -0
- package/dist/cjs/crypto/keyManager.js +887 -0
- package/dist/cjs/crypto/polyfill.js +64 -0
- package/dist/cjs/crypto/recoveryPhrase.js +169 -0
- package/dist/cjs/crypto/signatureService.js +296 -0
- package/dist/cjs/i18n/index.js +73 -0
- package/dist/cjs/i18n/locales/ar-SA.json +120 -0
- package/dist/cjs/i18n/locales/ca-ES.json +120 -0
- package/dist/cjs/i18n/locales/de-DE.json +120 -0
- package/dist/cjs/i18n/locales/en-US.json +956 -0
- package/dist/cjs/i18n/locales/es-ES.json +944 -0
- package/dist/cjs/i18n/locales/fr-FR.json +120 -0
- package/dist/cjs/i18n/locales/it-IT.json +120 -0
- package/dist/cjs/i18n/locales/ja-JP.json +119 -0
- package/dist/cjs/i18n/locales/ko-KR.json +120 -0
- package/dist/cjs/i18n/locales/locales/ar-SA.json +120 -0
- package/dist/cjs/i18n/locales/locales/ca-ES.json +120 -0
- package/dist/cjs/i18n/locales/locales/de-DE.json +120 -0
- package/dist/cjs/i18n/locales/locales/en-US.json +956 -0
- package/dist/cjs/i18n/locales/locales/es-ES.json +944 -0
- package/dist/cjs/i18n/locales/locales/fr-FR.json +120 -0
- package/dist/cjs/i18n/locales/locales/it-IT.json +120 -0
- package/dist/cjs/i18n/locales/locales/ja-JP.json +119 -0
- package/dist/cjs/i18n/locales/locales/ko-KR.json +120 -0
- package/dist/cjs/i18n/locales/locales/pt-PT.json +120 -0
- package/dist/cjs/i18n/locales/locales/zh-CN.json +120 -0
- package/dist/cjs/i18n/locales/pt-PT.json +120 -0
- package/dist/cjs/i18n/locales/zh-CN.json +120 -0
- package/dist/cjs/index.js +153 -0
- package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
- package/dist/cjs/mixins/OxyServices.assets.js +380 -0
- package/dist/cjs/mixins/OxyServices.auth.js +259 -0
- package/dist/cjs/mixins/OxyServices.developer.js +97 -0
- package/dist/cjs/mixins/OxyServices.devices.js +116 -0
- package/dist/cjs/mixins/OxyServices.features.js +309 -0
- package/dist/cjs/mixins/OxyServices.fedcm.js +435 -0
- package/dist/cjs/mixins/OxyServices.karma.js +108 -0
- package/dist/cjs/mixins/OxyServices.language.js +154 -0
- package/dist/cjs/mixins/OxyServices.location.js +43 -0
- package/dist/cjs/mixins/OxyServices.payment.js +158 -0
- package/dist/cjs/mixins/OxyServices.popup.js +371 -0
- package/dist/cjs/mixins/OxyServices.privacy.js +162 -0
- package/dist/cjs/mixins/OxyServices.redirect.js +345 -0
- package/dist/cjs/mixins/OxyServices.security.js +81 -0
- package/dist/cjs/mixins/OxyServices.user.js +355 -0
- package/dist/cjs/mixins/OxyServices.utility.js +156 -0
- package/dist/cjs/mixins/index.js +79 -0
- package/dist/cjs/mixins/mixinHelpers.js +53 -0
- package/dist/cjs/models/interfaces.js +20 -0
- package/dist/cjs/models/session.js +2 -0
- package/dist/cjs/shared/index.js +70 -0
- package/dist/cjs/shared/utils/colorUtils.js +153 -0
- package/dist/cjs/shared/utils/debugUtils.js +73 -0
- package/dist/cjs/shared/utils/errorUtils.js +183 -0
- package/dist/cjs/shared/utils/index.js +49 -0
- package/dist/cjs/shared/utils/networkUtils.js +183 -0
- package/dist/cjs/shared/utils/themeUtils.js +106 -0
- package/dist/cjs/utils/apiUtils.js +61 -0
- package/dist/cjs/utils/asyncUtils.js +194 -0
- package/dist/cjs/utils/cache.js +226 -0
- package/dist/cjs/utils/deviceManager.js +205 -0
- package/dist/cjs/utils/errorUtils.js +154 -0
- package/dist/cjs/utils/index.js +26 -0
- package/dist/cjs/utils/languageUtils.js +165 -0
- package/dist/cjs/utils/loggerUtils.js +126 -0
- package/dist/cjs/utils/platform.js +144 -0
- package/dist/cjs/utils/requestUtils.js +209 -0
- package/dist/cjs/utils/sessionUtils.js +181 -0
- package/dist/cjs/utils/validationUtils.js +173 -0
- package/dist/esm/AuthManager.js +356 -0
- package/dist/esm/CrossDomainAuth.js +253 -0
- package/dist/esm/HttpService.js +614 -0
- package/dist/esm/OxyServices.base.js +259 -0
- package/dist/esm/OxyServices.errors.js +17 -0
- package/dist/esm/OxyServices.js +59 -0
- package/dist/esm/constants/version.js +13 -0
- package/dist/esm/crypto/index.js +13 -0
- package/dist/esm/crypto/keyManager.js +850 -0
- package/dist/esm/crypto/polyfill.js +61 -0
- package/dist/esm/crypto/recoveryPhrase.js +132 -0
- package/dist/esm/crypto/signatureService.js +259 -0
- package/dist/esm/i18n/index.js +69 -0
- package/dist/esm/i18n/locales/ar-SA.json +120 -0
- package/dist/esm/i18n/locales/ca-ES.json +120 -0
- package/dist/esm/i18n/locales/de-DE.json +120 -0
- package/dist/esm/i18n/locales/en-US.json +956 -0
- package/dist/esm/i18n/locales/es-ES.json +944 -0
- package/dist/esm/i18n/locales/fr-FR.json +120 -0
- package/dist/esm/i18n/locales/it-IT.json +120 -0
- package/dist/esm/i18n/locales/ja-JP.json +119 -0
- package/dist/esm/i18n/locales/ko-KR.json +120 -0
- package/dist/esm/i18n/locales/locales/ar-SA.json +120 -0
- package/dist/esm/i18n/locales/locales/ca-ES.json +120 -0
- package/dist/esm/i18n/locales/locales/de-DE.json +120 -0
- package/dist/esm/i18n/locales/locales/en-US.json +956 -0
- package/dist/esm/i18n/locales/locales/es-ES.json +944 -0
- package/dist/esm/i18n/locales/locales/fr-FR.json +120 -0
- package/dist/esm/i18n/locales/locales/it-IT.json +120 -0
- package/dist/esm/i18n/locales/locales/ja-JP.json +119 -0
- package/dist/esm/i18n/locales/locales/ko-KR.json +120 -0
- package/dist/esm/i18n/locales/locales/pt-PT.json +120 -0
- package/dist/esm/i18n/locales/locales/zh-CN.json +120 -0
- package/dist/esm/i18n/locales/pt-PT.json +120 -0
- package/dist/esm/i18n/locales/zh-CN.json +120 -0
- package/dist/esm/index.js +55 -0
- package/dist/esm/mixins/OxyServices.analytics.js +46 -0
- package/dist/esm/mixins/OxyServices.assets.js +377 -0
- package/dist/esm/mixins/OxyServices.auth.js +256 -0
- package/dist/esm/mixins/OxyServices.developer.js +94 -0
- package/dist/esm/mixins/OxyServices.devices.js +113 -0
- package/dist/esm/mixins/OxyServices.features.js +306 -0
- package/dist/esm/mixins/OxyServices.fedcm.js +433 -0
- package/dist/esm/mixins/OxyServices.karma.js +105 -0
- package/dist/esm/mixins/OxyServices.language.js +118 -0
- package/dist/esm/mixins/OxyServices.location.js +40 -0
- package/dist/esm/mixins/OxyServices.payment.js +155 -0
- package/dist/esm/mixins/OxyServices.popup.js +369 -0
- package/dist/esm/mixins/OxyServices.privacy.js +159 -0
- package/dist/esm/mixins/OxyServices.redirect.js +343 -0
- package/dist/esm/mixins/OxyServices.security.js +78 -0
- package/dist/esm/mixins/OxyServices.user.js +352 -0
- package/dist/esm/mixins/OxyServices.utility.js +153 -0
- package/dist/esm/mixins/index.js +76 -0
- package/dist/esm/mixins/mixinHelpers.js +48 -0
- package/dist/esm/models/interfaces.js +17 -0
- package/dist/esm/models/session.js +1 -0
- package/dist/esm/shared/index.js +31 -0
- package/dist/esm/shared/utils/colorUtils.js +143 -0
- package/dist/esm/shared/utils/debugUtils.js +65 -0
- package/dist/esm/shared/utils/errorUtils.js +170 -0
- package/dist/esm/shared/utils/index.js +15 -0
- package/dist/esm/shared/utils/networkUtils.js +173 -0
- package/dist/esm/shared/utils/themeUtils.js +98 -0
- package/dist/esm/utils/apiUtils.js +55 -0
- package/dist/esm/utils/asyncUtils.js +179 -0
- package/dist/esm/utils/cache.js +218 -0
- package/dist/esm/utils/deviceManager.js +168 -0
- package/dist/esm/utils/errorUtils.js +146 -0
- package/dist/esm/utils/index.js +7 -0
- package/dist/esm/utils/languageUtils.js +158 -0
- package/dist/esm/utils/loggerUtils.js +115 -0
- package/dist/esm/utils/platform.js +102 -0
- package/dist/esm/utils/requestUtils.js +203 -0
- package/dist/esm/utils/sessionUtils.js +171 -0
- package/dist/esm/utils/validationUtils.js +153 -0
- package/dist/types/AuthManager.d.ts +143 -0
- package/dist/types/CrossDomainAuth.d.ts +160 -0
- package/dist/types/HttpService.d.ts +163 -0
- package/dist/types/OxyServices.base.d.ts +126 -0
- package/dist/types/OxyServices.d.ts +81 -0
- package/dist/types/OxyServices.errors.d.ts +11 -0
- package/dist/types/constants/version.d.ts +13 -0
- package/dist/types/crypto/index.d.ts +11 -0
- package/dist/types/crypto/keyManager.d.ts +189 -0
- package/dist/types/crypto/polyfill.d.ts +11 -0
- package/dist/types/crypto/recoveryPhrase.d.ts +58 -0
- package/dist/types/crypto/signatureService.d.ts +86 -0
- package/dist/types/i18n/index.d.ts +3 -0
- package/dist/types/index.d.ts +50 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +66 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +135 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +186 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +99 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +96 -0
- package/dist/types/mixins/OxyServices.features.d.ts +228 -0
- package/dist/types/mixins/OxyServices.fedcm.d.ts +200 -0
- package/dist/types/mixins/OxyServices.karma.d.ts +85 -0
- package/dist/types/mixins/OxyServices.language.d.ts +81 -0
- package/dist/types/mixins/OxyServices.location.d.ts +64 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +111 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +205 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +122 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +245 -0
- package/dist/types/mixins/OxyServices.security.d.ts +78 -0
- package/dist/types/mixins/OxyServices.user.d.ts +182 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +93 -0
- package/dist/types/mixins/index.d.ts +30 -0
- package/dist/types/mixins/mixinHelpers.d.ts +31 -0
- package/dist/types/models/interfaces.d.ts +415 -0
- package/dist/types/models/session.d.ts +27 -0
- package/dist/types/shared/index.d.ts +28 -0
- package/dist/types/shared/utils/colorUtils.d.ts +104 -0
- package/dist/types/shared/utils/debugUtils.d.ts +48 -0
- package/dist/types/shared/utils/errorUtils.d.ts +97 -0
- package/dist/types/shared/utils/index.d.ts +13 -0
- package/dist/types/shared/utils/networkUtils.d.ts +139 -0
- package/dist/types/shared/utils/themeUtils.d.ts +90 -0
- package/dist/types/utils/apiUtils.d.ts +53 -0
- package/dist/types/utils/asyncUtils.d.ts +58 -0
- package/dist/types/utils/cache.d.ts +127 -0
- package/dist/types/utils/deviceManager.d.ts +65 -0
- package/dist/types/utils/errorUtils.d.ts +46 -0
- package/dist/types/utils/index.d.ts +6 -0
- package/dist/types/utils/languageUtils.d.ts +37 -0
- package/dist/types/utils/loggerUtils.d.ts +48 -0
- package/dist/types/utils/platform.d.ts +40 -0
- package/dist/types/utils/requestUtils.d.ts +123 -0
- package/dist/types/utils/sessionUtils.d.ts +54 -0
- package/dist/types/utils/validationUtils.d.ts +85 -0
- package/package.json +84 -0
- package/src/AuthManager.ts +436 -0
- package/src/CrossDomainAuth.ts +307 -0
- package/src/HttpService.ts +752 -0
- package/src/OxyServices.base.ts +334 -0
- package/src/OxyServices.errors.ts +26 -0
- package/src/OxyServices.ts +129 -0
- package/src/constants/version.ts +15 -0
- package/src/crypto/index.ts +25 -0
- package/src/crypto/keyManager.ts +962 -0
- package/src/crypto/polyfill.ts +70 -0
- package/src/crypto/recoveryPhrase.ts +166 -0
- package/src/crypto/signatureService.ts +323 -0
- package/src/i18n/index.ts +75 -0
- package/src/i18n/locales/ar-SA.json +120 -0
- package/src/i18n/locales/ca-ES.json +120 -0
- package/src/i18n/locales/de-DE.json +120 -0
- package/src/i18n/locales/en-US.json +956 -0
- package/src/i18n/locales/es-ES.json +944 -0
- package/src/i18n/locales/fr-FR.json +120 -0
- package/src/i18n/locales/it-IT.json +120 -0
- package/src/i18n/locales/ja-JP.json +119 -0
- package/src/i18n/locales/ko-KR.json +120 -0
- package/src/i18n/locales/pt-PT.json +120 -0
- package/src/i18n/locales/zh-CN.json +120 -0
- package/src/index.ts +153 -0
- package/src/mixins/OxyServices.analytics.ts +53 -0
- package/src/mixins/OxyServices.assets.ts +412 -0
- package/src/mixins/OxyServices.auth.ts +358 -0
- package/src/mixins/OxyServices.developer.ts +114 -0
- package/src/mixins/OxyServices.devices.ts +119 -0
- package/src/mixins/OxyServices.features.ts +428 -0
- package/src/mixins/OxyServices.fedcm.ts +494 -0
- package/src/mixins/OxyServices.karma.ts +111 -0
- package/src/mixins/OxyServices.language.ts +127 -0
- package/src/mixins/OxyServices.location.ts +46 -0
- package/src/mixins/OxyServices.payment.ts +163 -0
- package/src/mixins/OxyServices.popup.ts +443 -0
- package/src/mixins/OxyServices.privacy.ts +182 -0
- package/src/mixins/OxyServices.redirect.ts +397 -0
- package/src/mixins/OxyServices.security.ts +103 -0
- package/src/mixins/OxyServices.user.ts +392 -0
- package/src/mixins/OxyServices.utility.ts +191 -0
- package/src/mixins/index.ts +91 -0
- package/src/mixins/mixinHelpers.ts +69 -0
- package/src/models/interfaces.ts +511 -0
- package/src/models/session.ts +30 -0
- package/src/shared/index.ts +82 -0
- package/src/shared/utils/colorUtils.ts +155 -0
- package/src/shared/utils/debugUtils.ts +73 -0
- package/src/shared/utils/errorUtils.ts +181 -0
- package/src/shared/utils/index.ts +59 -0
- package/src/shared/utils/networkUtils.ts +248 -0
- package/src/shared/utils/themeUtils.ts +115 -0
- package/src/types/bip39.d.ts +32 -0
- package/src/types/buffer.d.ts +97 -0
- package/src/types/color.d.ts +20 -0
- package/src/types/elliptic.d.ts +62 -0
- package/src/utils/apiUtils.ts +88 -0
- package/src/utils/asyncUtils.ts +252 -0
- package/src/utils/cache.ts +264 -0
- package/src/utils/deviceManager.ts +198 -0
- package/src/utils/errorUtils.ts +216 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/languageUtils.ts +174 -0
- package/src/utils/loggerUtils.ts +153 -0
- package/src/utils/platform.ts +117 -0
- package/src/utils/requestUtils.ts +237 -0
- package/src/utils/sessionUtils.ts +206 -0
- package/src/utils/validationUtils.ts +174 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import type { OxyServicesBase } from '../OxyServices.base';
|
|
2
|
+
import { OxyAuthenticationError } from '../OxyServices.errors';
|
|
3
|
+
import type { SessionLoginResponse } from '../models/session';
|
|
4
|
+
|
|
5
|
+
export interface RedirectAuthOptions {
|
|
6
|
+
redirectUri?: string;
|
|
7
|
+
mode?: 'login' | 'signup';
|
|
8
|
+
preserveUrl?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Redirect-based Cross-Domain Authentication Mixin
|
|
13
|
+
*
|
|
14
|
+
* Implements traditional OAuth2 redirect flow as a fallback when popup or
|
|
15
|
+
* FedCM are not available or fail (e.g., mobile browsers, popup blockers).
|
|
16
|
+
*
|
|
17
|
+
* Flow:
|
|
18
|
+
* 1. Save current URL
|
|
19
|
+
* 2. Redirect to auth.oxy.so/login
|
|
20
|
+
* 3. User signs in
|
|
21
|
+
* 4. Redirect back with token in URL
|
|
22
|
+
* 5. Extract token, restore session, clean URL
|
|
23
|
+
*
|
|
24
|
+
* Features:
|
|
25
|
+
* - Works on all browsers (including old mobile browsers)
|
|
26
|
+
* - Automatic URL cleanup after auth
|
|
27
|
+
* - State preservation option
|
|
28
|
+
* - CSRF protection via state parameter
|
|
29
|
+
*
|
|
30
|
+
* Trade-offs:
|
|
31
|
+
* - Loses JavaScript app state (full page navigation)
|
|
32
|
+
* - Visible redirect (user sees navigation)
|
|
33
|
+
* - Slower perceived performance
|
|
34
|
+
*/
|
|
35
|
+
export function OxyServicesRedirectAuthMixin<T extends typeof OxyServicesBase>(Base: T) {
|
|
36
|
+
return class extends Base {
|
|
37
|
+
constructor(...args: any[]) {
|
|
38
|
+
super(...(args as [any]));
|
|
39
|
+
}
|
|
40
|
+
public static readonly AUTH_URL = 'https://auth.oxy.so';
|
|
41
|
+
public static readonly TOKEN_STORAGE_KEY = 'oxy_access_token';
|
|
42
|
+
public static readonly SESSION_STORAGE_KEY = 'oxy_session_id';
|
|
43
|
+
public static readonly STATE_STORAGE_KEY = 'oxy_auth_state';
|
|
44
|
+
public static readonly PRE_AUTH_URL_KEY = 'oxy_pre_auth_url';
|
|
45
|
+
public static readonly NONCE_STORAGE_KEY = 'oxy_auth_nonce';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Sign in using full page redirect
|
|
49
|
+
*
|
|
50
|
+
* Redirects the user to auth.oxy.so for authentication. After successful
|
|
51
|
+
* sign-in, the user will be redirected back to the current page (or custom
|
|
52
|
+
* redirect URI) with authentication tokens in the URL.
|
|
53
|
+
*
|
|
54
|
+
* Call handleAuthCallback() on app startup to complete the flow.
|
|
55
|
+
*
|
|
56
|
+
* @param options - Redirect configuration options
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* // Initiate sign-in
|
|
61
|
+
* const handleSignIn = () => {
|
|
62
|
+
* oxyServices.signInWithRedirect();
|
|
63
|
+
* };
|
|
64
|
+
*
|
|
65
|
+
* // Handle callback on app startup
|
|
66
|
+
* useEffect(() => {
|
|
67
|
+
* const session = oxyServices.handleAuthCallback();
|
|
68
|
+
* if (session) {
|
|
69
|
+
* setUser(session.user);
|
|
70
|
+
* }
|
|
71
|
+
* }, []);
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
signInWithRedirect(options: RedirectAuthOptions = {}): void {
|
|
75
|
+
if (typeof window === 'undefined') {
|
|
76
|
+
throw new OxyAuthenticationError('Redirect authentication requires browser environment');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const redirectUri = options.redirectUri || window.location.href;
|
|
80
|
+
const mode = options.mode || 'login';
|
|
81
|
+
const state = this.generateState();
|
|
82
|
+
const nonce = this.generateNonce();
|
|
83
|
+
|
|
84
|
+
// Store state for CSRF protection
|
|
85
|
+
this.storeAuthState(state, nonce);
|
|
86
|
+
|
|
87
|
+
// Save current URL to restore after auth (optional)
|
|
88
|
+
if (options.preserveUrl !== false) {
|
|
89
|
+
this.savePreAuthUrl(window.location.href);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const authUrl = this.buildAuthUrl({
|
|
93
|
+
mode,
|
|
94
|
+
redirectUri,
|
|
95
|
+
state,
|
|
96
|
+
nonce,
|
|
97
|
+
clientId: window.location.origin,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Perform redirect
|
|
101
|
+
window.location.href = authUrl;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sign up using full page redirect
|
|
106
|
+
*
|
|
107
|
+
* Same as signInWithRedirect but opens the signup page by default.
|
|
108
|
+
*/
|
|
109
|
+
signUpWithRedirect(options: RedirectAuthOptions = {}): void {
|
|
110
|
+
this.signInWithRedirect({ ...options, mode: 'signup' });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Handle authentication callback
|
|
115
|
+
*
|
|
116
|
+
* Call this on app startup to check if the current page load is a
|
|
117
|
+
* redirect back from the authentication server. If it is, this method
|
|
118
|
+
* will extract the tokens, store them, and clean up the URL.
|
|
119
|
+
*
|
|
120
|
+
* @returns Session data if this is a callback, null otherwise
|
|
121
|
+
* @throws {OxyAuthenticationError} If state validation fails (CSRF attack)
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* // In your app's root component or startup logic
|
|
126
|
+
* useEffect(() => {
|
|
127
|
+
* try {
|
|
128
|
+
* const session = oxyServices.handleAuthCallback();
|
|
129
|
+
* if (session) {
|
|
130
|
+
* console.log('Logged in:', session.user);
|
|
131
|
+
* setUser(session.user);
|
|
132
|
+
* } else {
|
|
133
|
+
* // Not a callback, check for existing session
|
|
134
|
+
* const restored = oxyServices.restoreSession();
|
|
135
|
+
* if (!restored) {
|
|
136
|
+
* // No session, show login button
|
|
137
|
+
* }
|
|
138
|
+
* }
|
|
139
|
+
* } catch (error) {
|
|
140
|
+
* console.error('Auth callback failed:', error);
|
|
141
|
+
* }
|
|
142
|
+
* }, []);
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
handleAuthCallback(): SessionLoginResponse | null {
|
|
146
|
+
if (typeof window === 'undefined') {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const url = new URL(window.location.href);
|
|
151
|
+
const accessToken = url.searchParams.get('access_token');
|
|
152
|
+
const sessionId = url.searchParams.get('session_id');
|
|
153
|
+
const expiresAt = url.searchParams.get('expires_at');
|
|
154
|
+
const state = url.searchParams.get('state');
|
|
155
|
+
const error = url.searchParams.get('error');
|
|
156
|
+
const errorDescription = url.searchParams.get('error_description');
|
|
157
|
+
|
|
158
|
+
// Check if this is an error callback
|
|
159
|
+
if (error) {
|
|
160
|
+
this.clearAuthState();
|
|
161
|
+
throw new OxyAuthenticationError(errorDescription || error);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check if this is an auth callback
|
|
165
|
+
if (!accessToken || !sessionId) {
|
|
166
|
+
return null; // Not a callback
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Verify state to prevent CSRF attacks
|
|
170
|
+
const savedState = this.getStoredState();
|
|
171
|
+
if (!savedState || state !== savedState) {
|
|
172
|
+
this.clearAuthState();
|
|
173
|
+
throw new OxyAuthenticationError('Invalid state parameter. Possible CSRF attack.');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Store tokens
|
|
177
|
+
this.storeTokens(accessToken, sessionId);
|
|
178
|
+
this.httpService.setTokens(accessToken);
|
|
179
|
+
|
|
180
|
+
// Build session response (minimal - we'll fetch full user data separately)
|
|
181
|
+
const session: SessionLoginResponse = {
|
|
182
|
+
sessionId,
|
|
183
|
+
deviceId: '', // Not available in redirect flow
|
|
184
|
+
expiresAt: expiresAt || new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
|
|
185
|
+
user: {} as any, // Will be fetched separately
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Clean up URL (remove auth parameters)
|
|
189
|
+
this.cleanAuthCallbackUrl(url);
|
|
190
|
+
|
|
191
|
+
// Clean up storage
|
|
192
|
+
this.clearAuthState();
|
|
193
|
+
|
|
194
|
+
return session;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Restore session from storage
|
|
199
|
+
*
|
|
200
|
+
* Attempts to restore a previously authenticated session from localStorage.
|
|
201
|
+
* Call this on app startup if handleAuthCallback() returns null.
|
|
202
|
+
*
|
|
203
|
+
* @returns True if session was restored, false otherwise
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* useEffect(() => {
|
|
208
|
+
* const session = oxyServices.handleAuthCallback();
|
|
209
|
+
* if (!session) {
|
|
210
|
+
* const restored = oxyServices.restoreSession();
|
|
211
|
+
* if (!restored) {
|
|
212
|
+
* // No session, user needs to sign in
|
|
213
|
+
* setShowLogin(true);
|
|
214
|
+
* }
|
|
215
|
+
* }
|
|
216
|
+
* }, []);
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
restoreSession(): boolean {
|
|
220
|
+
if (typeof window === 'undefined') {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const token = localStorage.getItem((this.constructor as any).TOKEN_STORAGE_KEY);
|
|
225
|
+
const sessionId = localStorage.getItem((this.constructor as any).SESSION_STORAGE_KEY);
|
|
226
|
+
|
|
227
|
+
if (token && sessionId) {
|
|
228
|
+
this.httpService.setTokens(token);
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Clear stored session
|
|
237
|
+
*
|
|
238
|
+
* Removes all authentication data from storage. Call this on logout.
|
|
239
|
+
*/
|
|
240
|
+
clearStoredSession(): void {
|
|
241
|
+
if (typeof window === 'undefined') {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
localStorage.removeItem((this.constructor as any).TOKEN_STORAGE_KEY);
|
|
246
|
+
localStorage.removeItem((this.constructor as any).SESSION_STORAGE_KEY);
|
|
247
|
+
this.httpService.clearTokens();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get stored session ID
|
|
252
|
+
*/
|
|
253
|
+
getStoredSessionId(): string | null {
|
|
254
|
+
if (typeof window === 'undefined') {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return localStorage.getItem((this.constructor as any).SESSION_STORAGE_KEY);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Build authentication URL with query parameters
|
|
263
|
+
*
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
public buildAuthUrl(params: {
|
|
267
|
+
mode: string;
|
|
268
|
+
redirectUri: string;
|
|
269
|
+
state: string;
|
|
270
|
+
nonce: string;
|
|
271
|
+
clientId: string;
|
|
272
|
+
}): string {
|
|
273
|
+
const url = new URL(`${(this.constructor as any).AUTH_URL}/${params.mode}`);
|
|
274
|
+
url.searchParams.set('redirect_uri', params.redirectUri);
|
|
275
|
+
url.searchParams.set('state', params.state);
|
|
276
|
+
url.searchParams.set('nonce', params.nonce);
|
|
277
|
+
url.searchParams.set('client_id', params.clientId);
|
|
278
|
+
url.searchParams.set('response_type', 'token');
|
|
279
|
+
return url.toString();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Store tokens in localStorage
|
|
284
|
+
*
|
|
285
|
+
* @private
|
|
286
|
+
*/
|
|
287
|
+
public storeTokens(accessToken: string, sessionId: string): void {
|
|
288
|
+
if (typeof window === 'undefined') {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
localStorage.setItem((this.constructor as any).TOKEN_STORAGE_KEY, accessToken);
|
|
293
|
+
localStorage.setItem((this.constructor as any).SESSION_STORAGE_KEY, sessionId);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Generate cryptographically secure state for CSRF protection
|
|
298
|
+
*
|
|
299
|
+
* @private
|
|
300
|
+
*/
|
|
301
|
+
public generateState(): string {
|
|
302
|
+
if (typeof window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
|
|
303
|
+
return window.crypto.randomUUID();
|
|
304
|
+
}
|
|
305
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Generate nonce for replay attack prevention
|
|
310
|
+
*
|
|
311
|
+
* @private
|
|
312
|
+
*/
|
|
313
|
+
public generateNonce(): string {
|
|
314
|
+
if (typeof window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
|
|
315
|
+
return window.crypto.randomUUID();
|
|
316
|
+
}
|
|
317
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Store auth state in session storage
|
|
322
|
+
*
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
325
|
+
public storeAuthState(state: string, nonce: string): void {
|
|
326
|
+
if (typeof window === 'undefined') {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
sessionStorage.setItem((this.constructor as any).STATE_STORAGE_KEY, state);
|
|
331
|
+
sessionStorage.setItem((this.constructor as any).NONCE_STORAGE_KEY, nonce);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get stored state
|
|
336
|
+
*
|
|
337
|
+
* @private
|
|
338
|
+
*/
|
|
339
|
+
public getStoredState(): string | null {
|
|
340
|
+
if (typeof window === 'undefined') {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return sessionStorage.getItem((this.constructor as any).STATE_STORAGE_KEY);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Clear auth state from storage
|
|
349
|
+
*
|
|
350
|
+
* @private
|
|
351
|
+
*/
|
|
352
|
+
public clearAuthState(): void {
|
|
353
|
+
if (typeof window === 'undefined') {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
sessionStorage.removeItem((this.constructor as any).STATE_STORAGE_KEY);
|
|
358
|
+
sessionStorage.removeItem((this.constructor as any).NONCE_STORAGE_KEY);
|
|
359
|
+
sessionStorage.removeItem((this.constructor as any).PRE_AUTH_URL_KEY);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Save pre-authentication URL to restore later
|
|
364
|
+
*
|
|
365
|
+
* @private
|
|
366
|
+
*/
|
|
367
|
+
public savePreAuthUrl(url: string): void {
|
|
368
|
+
if (typeof window === 'undefined') {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
sessionStorage.setItem((this.constructor as any).PRE_AUTH_URL_KEY, url);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Clean authentication parameters from URL
|
|
377
|
+
*
|
|
378
|
+
* @private
|
|
379
|
+
*/
|
|
380
|
+
public cleanAuthCallbackUrl(url: URL): void {
|
|
381
|
+
// Remove auth parameters
|
|
382
|
+
url.searchParams.delete('access_token');
|
|
383
|
+
url.searchParams.delete('session_id');
|
|
384
|
+
url.searchParams.delete('expires_at');
|
|
385
|
+
url.searchParams.delete('state');
|
|
386
|
+
url.searchParams.delete('nonce');
|
|
387
|
+
url.searchParams.delete('error');
|
|
388
|
+
url.searchParams.delete('error_description');
|
|
389
|
+
|
|
390
|
+
// Update URL without reloading page
|
|
391
|
+
window.history.replaceState({}, '', url.toString());
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Export the mixin function as both named and default
|
|
397
|
+
export { OxyServicesRedirectAuthMixin as RedirectAuthMixin };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Methods Mixin
|
|
3
|
+
*/
|
|
4
|
+
import type { OxyServicesBase } from '../OxyServices.base';
|
|
5
|
+
import type { SecurityActivity, SecurityActivityResponse, SecurityEventType } from '../models/interfaces';
|
|
6
|
+
|
|
7
|
+
export function OxyServicesSecurityMixin<T extends typeof OxyServicesBase>(Base: T) {
|
|
8
|
+
return class extends Base {
|
|
9
|
+
constructor(...args: any[]) {
|
|
10
|
+
super(...(args as [any]));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get user's security activity with pagination
|
|
15
|
+
* @param limit - Number of results (default: 50, max: 100)
|
|
16
|
+
* @param offset - Pagination offset (default: 0)
|
|
17
|
+
* @param eventType - Optional filter by event type
|
|
18
|
+
* @returns Security activity response with pagination
|
|
19
|
+
*/
|
|
20
|
+
async getSecurityActivity(
|
|
21
|
+
limit?: number,
|
|
22
|
+
offset?: number,
|
|
23
|
+
eventType?: SecurityEventType
|
|
24
|
+
): Promise<SecurityActivityResponse> {
|
|
25
|
+
try {
|
|
26
|
+
const params: any = {};
|
|
27
|
+
if (limit !== undefined) params.limit = limit;
|
|
28
|
+
if (offset !== undefined) params.offset = offset;
|
|
29
|
+
if (eventType) params.eventType = eventType;
|
|
30
|
+
|
|
31
|
+
const response = await this.makeRequest<SecurityActivityResponse>(
|
|
32
|
+
'GET',
|
|
33
|
+
'/api/security/activity',
|
|
34
|
+
params,
|
|
35
|
+
{ cache: false }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return response;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw this.handleError(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get recent security activity (convenience method)
|
|
46
|
+
* @param limit - Number of recent events to fetch (default: 10)
|
|
47
|
+
* @returns Array of recent security activities
|
|
48
|
+
*/
|
|
49
|
+
async getRecentSecurityActivity(limit: number = 10): Promise<SecurityActivity[]> {
|
|
50
|
+
try {
|
|
51
|
+
const response = await this.getSecurityActivity(limit, 0);
|
|
52
|
+
return response.data || [];
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw this.handleError(error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Log private key exported event
|
|
60
|
+
* @param deviceId - Optional device ID for tracking
|
|
61
|
+
* @returns Promise that resolves when event is logged
|
|
62
|
+
*/
|
|
63
|
+
async logPrivateKeyExported(deviceId?: string): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
await this.makeRequest<{ success: boolean }>(
|
|
66
|
+
'POST',
|
|
67
|
+
'/api/security/activity/private-key-exported',
|
|
68
|
+
{ deviceId },
|
|
69
|
+
{ cache: false }
|
|
70
|
+
);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
// Don't throw - logging failures shouldn't break user flow
|
|
73
|
+
// But log for monitoring
|
|
74
|
+
if (__DEV__) {
|
|
75
|
+
console.warn('[OxyServices] Failed to log private key exported event:', error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Log backup created event
|
|
82
|
+
* @param deviceId - Optional device ID for tracking
|
|
83
|
+
* @returns Promise that resolves when event is logged
|
|
84
|
+
*/
|
|
85
|
+
async logBackupCreated(deviceId?: string): Promise<void> {
|
|
86
|
+
try {
|
|
87
|
+
await this.makeRequest<{ success: boolean }>(
|
|
88
|
+
'POST',
|
|
89
|
+
'/api/security/activity/backup-created',
|
|
90
|
+
{ deviceId },
|
|
91
|
+
{ cache: false }
|
|
92
|
+
);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
// Don't throw - logging failures shouldn't break user flow
|
|
95
|
+
// But log for monitoring
|
|
96
|
+
if (__DEV__) {
|
|
97
|
+
console.warn('[OxyServices] Failed to log backup created event:', error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|