@oxyhq/core 1.11.10 → 1.11.11

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.
Files changed (44) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/HttpService.js +13 -0
  3. package/dist/cjs/OxyServices.base.js +21 -0
  4. package/dist/cjs/mixins/OxyServices.managedAccounts.js +117 -0
  5. package/dist/cjs/mixins/OxyServices.utility.js +81 -2
  6. package/dist/cjs/mixins/index.js +2 -0
  7. package/dist/esm/.tsbuildinfo +1 -1
  8. package/dist/esm/HttpService.js +13 -0
  9. package/dist/esm/OxyServices.base.js +21 -0
  10. package/dist/esm/mixins/OxyServices.managedAccounts.js +114 -0
  11. package/dist/esm/mixins/OxyServices.utility.js +81 -2
  12. package/dist/esm/mixins/index.js +2 -0
  13. package/dist/types/.tsbuildinfo +1 -1
  14. package/dist/types/HttpService.d.ts +3 -0
  15. package/dist/types/OxyServices.base.d.ts +17 -0
  16. package/dist/types/index.d.ts +1 -0
  17. package/dist/types/mixins/OxyServices.analytics.d.ts +2 -0
  18. package/dist/types/mixins/OxyServices.assets.d.ts +2 -0
  19. package/dist/types/mixins/OxyServices.auth.d.ts +2 -0
  20. package/dist/types/mixins/OxyServices.developer.d.ts +2 -0
  21. package/dist/types/mixins/OxyServices.devices.d.ts +2 -0
  22. package/dist/types/mixins/OxyServices.features.d.ts +5 -1
  23. package/dist/types/mixins/OxyServices.fedcm.d.ts +2 -0
  24. package/dist/types/mixins/OxyServices.karma.d.ts +2 -0
  25. package/dist/types/mixins/OxyServices.language.d.ts +2 -0
  26. package/dist/types/mixins/OxyServices.location.d.ts +2 -0
  27. package/dist/types/mixins/OxyServices.managedAccounts.d.ts +125 -0
  28. package/dist/types/mixins/OxyServices.payment.d.ts +2 -0
  29. package/dist/types/mixins/OxyServices.popup.d.ts +2 -0
  30. package/dist/types/mixins/OxyServices.privacy.d.ts +2 -0
  31. package/dist/types/mixins/OxyServices.redirect.d.ts +2 -0
  32. package/dist/types/mixins/OxyServices.security.d.ts +2 -0
  33. package/dist/types/mixins/OxyServices.topics.d.ts +2 -0
  34. package/dist/types/mixins/OxyServices.user.d.ts +2 -0
  35. package/dist/types/mixins/OxyServices.utility.d.ts +22 -0
  36. package/dist/types/models/interfaces.d.ts +2 -0
  37. package/package.json +1 -1
  38. package/src/HttpService.ts +17 -0
  39. package/src/OxyServices.base.ts +23 -0
  40. package/src/index.ts +1 -0
  41. package/src/mixins/OxyServices.managedAccounts.ts +147 -0
  42. package/src/mixins/OxyServices.utility.ts +103 -2
  43. package/src/mixins/index.ts +2 -0
  44. package/src/models/interfaces.ts +3 -0
@@ -50,6 +50,7 @@ export declare class HttpService {
50
50
  private tokenRefreshPromise;
51
51
  private tokenRefreshCooldownUntil;
52
52
  private _onTokenRefreshed;
53
+ private _actingAsUserId;
53
54
  private requestMetrics;
54
55
  constructor(config: OxyConfig);
55
56
  /**
@@ -93,6 +94,8 @@ export declare class HttpService {
93
94
  put<T = unknown>(url: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'url' | 'data'>): Promise<T>;
94
95
  patch<T = unknown>(url: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'url' | 'data'>): Promise<T>;
95
96
  delete<T = unknown>(url: string, config?: Omit<RequestConfig, 'method' | 'url'>): Promise<T>;
97
+ setActingAs(userId: string | null): void;
98
+ getActingAs(): string | null;
96
99
  setTokens(accessToken: string, refreshToken?: string): void;
97
100
  set onTokenRefreshed(callback: ((accessToken: string) => void) | null);
98
101
  clearTokens(): void;
@@ -81,6 +81,23 @@ export declare class OxyServicesBase {
81
81
  * Get the raw access token (for constructing anchor URLs when needed)
82
82
  */
83
83
  getAccessToken(): string | null;
84
+ /**
85
+ * Set the acting-as identity for managed accounts.
86
+ *
87
+ * When set, all subsequent API requests will include the `X-Acting-As` header,
88
+ * causing the server to attribute actions to the managed account. The
89
+ * authenticated user must be an authorized manager of the target account.
90
+ *
91
+ * Pass `null` to clear and revert to the authenticated user's own identity.
92
+ *
93
+ * @param userId - The managed account user ID, or null to clear
94
+ */
95
+ setActingAs(userId: string | null): void;
96
+ /**
97
+ * Get the current acting-as identity (managed account user ID), or null
98
+ * if operating as the authenticated user's own identity.
99
+ */
100
+ getActingAs(): string | null;
84
101
  /**
85
102
  * Wait for authentication to be ready
86
103
  *
@@ -25,6 +25,7 @@ export type { PopupAuthOptions } from './mixins/OxyServices.popup';
25
25
  export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
26
26
  export type { ServiceTokenResponse } from './mixins/OxyServices.auth';
27
27
  export type { ServiceApp } from './mixins/OxyServices.utility';
28
+ export type { CreateManagedAccountInput, ManagedAccountManager, ManagedAccount } from './mixins/OxyServices.managedAccounts';
28
29
  export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
29
30
  export type { KeyPair, SignedMessage, AuthChallenge, RecoveryPhraseResult } from './crypto';
30
31
  export * from './models/interfaces';
@@ -50,6 +50,8 @@ export declare function OxyServicesAnalyticsMixin<T extends typeof OxyServicesBa
50
50
  getCurrentUserId(): string | null;
51
51
  hasValidToken(): boolean;
52
52
  getAccessToken(): string | null;
53
+ setActingAs(userId: string | null): void;
54
+ getActingAs(): string | null;
53
55
  waitForAuth(timeoutMs?: number): Promise<boolean>;
54
56
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
55
57
  maxRetries?: number;
@@ -128,6 +128,8 @@ export declare function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>
128
128
  getCurrentUserId(): string | null;
129
129
  hasValidToken(): boolean;
130
130
  getAccessToken(): string | null;
131
+ setActingAs(userId: string | null): void;
132
+ getActingAs(): string | null;
131
133
  waitForAuth(timeoutMs?: number): Promise<boolean>;
132
134
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
133
135
  maxRetries?: number;
@@ -206,6 +206,8 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
206
206
  getCurrentUserId(): string | null;
207
207
  hasValidToken(): boolean;
208
208
  getAccessToken(): string | null;
209
+ setActingAs(userId: string | null): void;
210
+ getActingAs(): string | null;
209
211
  waitForAuth(timeoutMs?: number): Promise<boolean>;
210
212
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
211
213
  maxRetries?: number;
@@ -83,6 +83,8 @@ export declare function OxyServicesDeveloperMixin<T extends typeof OxyServicesBa
83
83
  getCurrentUserId(): string | null;
84
84
  hasValidToken(): boolean;
85
85
  getAccessToken(): string | null;
86
+ setActingAs(userId: string | null): void;
87
+ getActingAs(): string | null;
86
88
  waitForAuth(timeoutMs?: number): Promise<boolean>;
87
89
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
88
90
  maxRetries?: number;
@@ -80,6 +80,8 @@ export declare function OxyServicesDevicesMixin<T extends typeof OxyServicesBase
80
80
  getCurrentUserId(): string | null;
81
81
  hasValidToken(): boolean;
82
82
  getAccessToken(): string | null;
83
+ setActingAs(userId: string | null): void;
84
+ getActingAs(): string | null;
83
85
  waitForAuth(timeoutMs?: number): Promise<boolean>;
84
86
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
85
87
  maxRetries?: number;
@@ -212,6 +212,8 @@ export declare function OxyServicesFeaturesMixin<T extends typeof OxyServicesBas
212
212
  getCurrentUserId(): string | null;
213
213
  hasValidToken(): boolean;
214
214
  getAccessToken(): string | null;
215
+ setActingAs(userId: string | null): void;
216
+ getActingAs(): string | null;
215
217
  waitForAuth(timeoutMs?: number): Promise<boolean>;
216
218
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
217
219
  maxRetries?: number;
@@ -223,7 +225,9 @@ export declare function OxyServicesFeaturesMixin<T extends typeof OxyServicesBas
223
225
  healthCheck(): Promise<{
224
226
  status: string;
225
227
  users?: number;
226
- timestamp?: string;
228
+ timestamp? /**
229
+ * Get FAQs
230
+ */: string;
227
231
  [key: string]: any;
228
232
  }>;
229
233
  };
@@ -188,6 +188,8 @@ export declare function OxyServicesFedCMMixin<T extends typeof OxyServicesBase>(
188
188
  getCurrentUserId(): string | null;
189
189
  hasValidToken(): boolean;
190
190
  getAccessToken(): string | null;
191
+ setActingAs(userId: string | null): void;
192
+ getActingAs(): string | null;
191
193
  waitForAuth(timeoutMs?: number): Promise<boolean>;
192
194
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
193
195
  maxRetries?: number;
@@ -69,6 +69,8 @@ export declare function OxyServicesKarmaMixin<T extends typeof OxyServicesBase>(
69
69
  getCurrentUserId(): string | null;
70
70
  hasValidToken(): boolean;
71
71
  getAccessToken(): string | null;
72
+ setActingAs(userId: string | null): void;
73
+ getActingAs(): string | null;
72
74
  waitForAuth(timeoutMs?: number): Promise<boolean>;
73
75
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
74
76
  maxRetries?: number;
@@ -65,6 +65,8 @@ export declare function OxyServicesLanguageMixin<T extends typeof OxyServicesBas
65
65
  getCurrentUserId(): string | null;
66
66
  hasValidToken(): boolean;
67
67
  getAccessToken(): string | null;
68
+ setActingAs(userId: string | null): void;
69
+ getActingAs(): string | null;
68
70
  waitForAuth(timeoutMs?: number): Promise<boolean>;
69
71
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
70
72
  maxRetries?: number;
@@ -48,6 +48,8 @@ export declare function OxyServicesLocationMixin<T extends typeof OxyServicesBas
48
48
  getCurrentUserId(): string | null;
49
49
  hasValidToken(): boolean;
50
50
  getAccessToken(): string | null;
51
+ setActingAs(userId: string | null): void;
52
+ getActingAs(): string | null;
51
53
  waitForAuth(timeoutMs?: number): Promise<boolean>;
52
54
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
53
55
  maxRetries?: number;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Managed Accounts Methods Mixin
3
+ *
4
+ * Provides SDK methods for creating and managing sub-accounts (managed identities).
5
+ * Managed accounts are full User documents without passwords, accessible only
6
+ * by their owners/managers via the X-Acting-As header mechanism.
7
+ */
8
+ import type { User } from '../models/interfaces';
9
+ import type { OxyServicesBase } from '../OxyServices.base';
10
+ export interface CreateManagedAccountInput {
11
+ username: string;
12
+ name?: {
13
+ first?: string;
14
+ last?: string;
15
+ };
16
+ bio?: string;
17
+ avatar?: string;
18
+ }
19
+ export interface ManagedAccountManager {
20
+ userId: string;
21
+ role: 'owner' | 'admin' | 'editor';
22
+ addedAt: string;
23
+ addedBy?: string;
24
+ }
25
+ export interface ManagedAccount {
26
+ accountId: string;
27
+ ownerId: string;
28
+ managers: ManagedAccountManager[];
29
+ account?: User;
30
+ createdAt?: string;
31
+ updatedAt?: string;
32
+ }
33
+ export declare function OxyServicesManagedAccountsMixin<T extends typeof OxyServicesBase>(Base: T): {
34
+ new (...args: any[]): {
35
+ /**
36
+ * Create a new managed account (sub-account).
37
+ *
38
+ * The server creates a User document with `isManagedAccount: true` and links
39
+ * it to the authenticated user as owner.
40
+ */
41
+ createManagedAccount(data: CreateManagedAccountInput): Promise<ManagedAccount>;
42
+ /**
43
+ * List all accounts the authenticated user manages.
44
+ */
45
+ getManagedAccounts(): Promise<ManagedAccount[]>;
46
+ /**
47
+ * Get details for a specific managed account.
48
+ */
49
+ getManagedAccountDetails(accountId: string): Promise<ManagedAccount>;
50
+ /**
51
+ * Update a managed account's profile data.
52
+ * Requires owner or admin role.
53
+ */
54
+ updateManagedAccount(accountId: string, data: Partial<CreateManagedAccountInput>): Promise<ManagedAccount>;
55
+ /**
56
+ * Delete a managed account permanently.
57
+ * Requires owner role.
58
+ */
59
+ deleteManagedAccount(accountId: string): Promise<void>;
60
+ /**
61
+ * Add a manager to a managed account.
62
+ * Requires owner or admin role on the account.
63
+ *
64
+ * @param accountId - The managed account to add the manager to
65
+ * @param userId - The user to grant management access
66
+ * @param role - The role to assign: 'admin' or 'editor'
67
+ */
68
+ addManager(accountId: string, userId: string, role: "admin" | "editor"): Promise<void>;
69
+ /**
70
+ * Remove a manager from a managed account.
71
+ * Requires owner role.
72
+ *
73
+ * @param accountId - The managed account
74
+ * @param userId - The manager to remove
75
+ */
76
+ removeManager(accountId: string, userId: string): Promise<void>;
77
+ httpService: import("../HttpService").HttpService;
78
+ cloudURL: string;
79
+ config: import("../OxyServices.base").OxyConfig;
80
+ __resetTokensForTests(): void;
81
+ makeRequest<T_1>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", url: string, data?: any, options?: import("../HttpService").RequestOptions): Promise<T_1>;
82
+ getBaseURL(): string;
83
+ getClient(): import("../HttpService").HttpService;
84
+ getMetrics(): {
85
+ totalRequests: number;
86
+ successfulRequests: number;
87
+ failedRequests: number;
88
+ cacheHits: number;
89
+ cacheMisses: number;
90
+ averageResponseTime: number;
91
+ };
92
+ clearCache(): void;
93
+ clearCacheEntry(key: string): void;
94
+ getCacheStats(): {
95
+ size: number;
96
+ hits: number;
97
+ misses: number;
98
+ hitRate: number;
99
+ };
100
+ getCloudURL(): string;
101
+ setTokens(accessToken: string, refreshToken?: string): void;
102
+ clearTokens(): void;
103
+ _cachedUserId: string | null | undefined;
104
+ _cachedAccessToken: string | null;
105
+ getCurrentUserId(): string | null;
106
+ hasValidToken(): boolean;
107
+ getAccessToken(): string | null;
108
+ setActingAs(userId: string | null): void;
109
+ getActingAs(): string | null;
110
+ waitForAuth(timeoutMs?: number): Promise<boolean>;
111
+ withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
112
+ maxRetries?: number;
113
+ retryDelay?: number;
114
+ authTimeoutMs?: number;
115
+ }): Promise<T_1>;
116
+ validate(): Promise<boolean>;
117
+ handleError(error: unknown): Error;
118
+ healthCheck(): Promise<{
119
+ status: string;
120
+ users?: number;
121
+ timestamp?: string;
122
+ [key: string]: any;
123
+ }>;
124
+ };
125
+ } & T;
@@ -95,6 +95,8 @@ export declare function OxyServicesPaymentMixin<T extends typeof OxyServicesBase
95
95
  getCurrentUserId(): string | null;
96
96
  hasValidToken(): boolean;
97
97
  getAccessToken(): string | null;
98
+ setActingAs(userId: string | null): void;
99
+ getActingAs(): string | null;
98
100
  waitForAuth(timeoutMs?: number): Promise<boolean>;
99
101
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
100
102
  maxRetries?: number;
@@ -185,6 +185,8 @@ export declare function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBa
185
185
  getCurrentUserId(): string | null;
186
186
  hasValidToken(): boolean;
187
187
  getAccessToken(): string | null;
188
+ setActingAs(userId: string | null): void;
189
+ getActingAs(): string | null;
188
190
  waitForAuth(timeoutMs?: number): Promise<boolean>;
189
191
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
190
192
  maxRetries?: number;
@@ -106,6 +106,8 @@ export declare function OxyServicesPrivacyMixin<T extends typeof OxyServicesBase
106
106
  getCurrentUserId(): string | null;
107
107
  hasValidToken(): boolean;
108
108
  getAccessToken(): string | null;
109
+ setActingAs(userId: string | null): void;
110
+ getActingAs(): string | null;
109
111
  waitForAuth(timeoutMs?: number): Promise<boolean>;
110
112
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
111
113
  maxRetries?: number;
@@ -222,6 +222,8 @@ export declare function OxyServicesRedirectAuthMixin<T extends typeof OxyService
222
222
  getCurrentUserId(): string | null;
223
223
  hasValidToken(): boolean;
224
224
  getAccessToken(): string | null;
225
+ setActingAs(userId: string | null): void;
226
+ getActingAs(): string | null;
225
227
  waitForAuth(timeoutMs?: number): Promise<boolean>;
226
228
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
227
229
  maxRetries?: number;
@@ -62,6 +62,8 @@ export declare function OxyServicesSecurityMixin<T extends typeof OxyServicesBas
62
62
  getCurrentUserId(): string | null;
63
63
  hasValidToken(): boolean;
64
64
  getAccessToken(): string | null;
65
+ setActingAs(userId: string | null): void;
66
+ getActingAs(): string | null;
65
67
  waitForAuth(timeoutMs?: number): Promise<boolean>;
66
68
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
67
69
  maxRetries?: number;
@@ -88,6 +88,8 @@ export declare function OxyServicesTopicsMixin<T extends typeof OxyServicesBase>
88
88
  getCurrentUserId(): string | null;
89
89
  hasValidToken(): boolean;
90
90
  getAccessToken(): string | null;
91
+ setActingAs(userId: string | null): void;
92
+ getActingAs(): string | null;
91
93
  waitForAuth(timeoutMs?: number): Promise<boolean>;
92
94
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
93
95
  maxRetries?: number;
@@ -205,6 +205,8 @@ export declare function OxyServicesUserMixin<T extends typeof OxyServicesBase>(B
205
205
  getCurrentUserId(): string | null;
206
206
  hasValidToken(): boolean;
207
207
  getAccessToken(): string | null;
208
+ setActingAs(userId: string | null): void;
209
+ getActingAs(): string | null;
208
210
  waitForAuth(timeoutMs?: number): Promise<boolean>;
209
211
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
210
212
  maxRetries?: number;
@@ -1,5 +1,13 @@
1
1
  import type { ApiError } from '../models/interfaces';
2
2
  import type { OxyServicesBase } from '../OxyServices.base';
3
+ /**
4
+ * Result from the managed-accounts verification endpoint.
5
+ * Indicates whether a user is authorized to act as a given managed account.
6
+ */
7
+ interface ActingAsVerification {
8
+ authorized: boolean;
9
+ role: 'owner' | 'admin' | 'editor';
10
+ }
3
11
  /**
4
12
  * Service app metadata attached to requests authenticated with service tokens
5
13
  */
@@ -28,6 +36,18 @@ interface AuthMiddlewareOptions {
28
36
  }
29
37
  export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase>(Base: T): {
30
38
  new (...args: any[]): {
39
+ /** @internal In-memory cache for acting-as verification results (TTL: 5 min) */
40
+ _actingAsCache: Map<string, {
41
+ result: ActingAsVerification | null;
42
+ expiresAt: number;
43
+ }>;
44
+ /**
45
+ * Verify that a user is authorized to act as a managed account.
46
+ * Results are cached in-memory for 5 minutes to avoid repeated API calls.
47
+ *
48
+ * @internal Used by the auth() middleware — not part of the public API
49
+ */
50
+ verifyActingAs(userId: string, accountId: string): Promise<ActingAsVerification | null>;
31
51
  /**
32
52
  * Fetch link metadata
33
53
  */
@@ -157,6 +177,8 @@ export declare function OxyServicesUtilityMixin<T extends typeof OxyServicesBase
157
177
  getCurrentUserId(): string | null;
158
178
  hasValidToken(): boolean;
159
179
  getAccessToken(): string | null;
180
+ setActingAs(userId: string | null): void;
181
+ getActingAs(): string | null;
160
182
  waitForAuth(timeoutMs?: number): Promise<boolean>;
161
183
  withAuthRetry<T_1>(operation: () => Promise<T_1>, operationName: string, options?: {
162
184
  maxRetries?: number;
@@ -64,6 +64,8 @@ export interface User {
64
64
  automation?: {
65
65
  ownerId?: string;
66
66
  };
67
+ isManagedAccount?: boolean;
68
+ managedBy?: string;
67
69
  [key: string]: unknown;
68
70
  }
69
71
  export interface LoginResponse {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/core",
3
- "version": "1.11.10",
3
+ "version": "1.11.11",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -131,6 +131,9 @@ export class HttpService {
131
131
  private tokenRefreshCooldownUntil: number = 0;
132
132
  private _onTokenRefreshed: ((accessToken: string) => void) | null = null;
133
133
 
134
+ // Acting-as identity for managed accounts
135
+ private _actingAsUserId: string | null = null;
136
+
134
137
  // Performance monitoring
135
138
  private requestMetrics = {
136
139
  totalRequests: 0,
@@ -291,6 +294,11 @@ export class HttpService {
291
294
  });
292
295
  }
293
296
 
297
+ // Add X-Acting-As header for managed account identity delegation
298
+ if (this._actingAsUserId) {
299
+ headers['X-Acting-As'] = this._actingAsUserId;
300
+ }
301
+
294
302
  // Merge custom headers if provided
295
303
  if (config.headers) {
296
304
  Object.entries(config.headers).forEach(([key, value]) => {
@@ -706,6 +714,15 @@ export class HttpService {
706
714
  return this.request<T>({ method: 'DELETE', url, ...config });
707
715
  }
708
716
 
717
+ // Acting-as identity management (managed accounts)
718
+ setActingAs(userId: string | null): void {
719
+ this._actingAsUserId = userId;
720
+ }
721
+
722
+ getActingAs(): string | null {
723
+ return this._actingAsUserId;
724
+ }
725
+
709
726
  // Token management
710
727
  setTokens(accessToken: string, refreshToken = ''): void {
711
728
  this.tokenStore.setTokens(accessToken, refreshToken);
@@ -182,6 +182,29 @@ export class OxyServicesBase {
182
182
  return this.httpService.getAccessToken();
183
183
  }
184
184
 
185
+ /**
186
+ * Set the acting-as identity for managed accounts.
187
+ *
188
+ * When set, all subsequent API requests will include the `X-Acting-As` header,
189
+ * causing the server to attribute actions to the managed account. The
190
+ * authenticated user must be an authorized manager of the target account.
191
+ *
192
+ * Pass `null` to clear and revert to the authenticated user's own identity.
193
+ *
194
+ * @param userId - The managed account user ID, or null to clear
195
+ */
196
+ public setActingAs(userId: string | null): void {
197
+ this.httpService.setActingAs(userId);
198
+ }
199
+
200
+ /**
201
+ * Get the current acting-as identity (managed account user ID), or null
202
+ * if operating as the authenticated user's own identity.
203
+ */
204
+ public getActingAs(): string | null {
205
+ return this.httpService.getActingAs();
206
+ }
207
+
185
208
  /**
186
209
  * Wait for authentication to be ready
187
210
  *
package/src/index.ts CHANGED
@@ -32,6 +32,7 @@ export type { PopupAuthOptions } from './mixins/OxyServices.popup';
32
32
  export type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
33
33
  export type { ServiceTokenResponse } from './mixins/OxyServices.auth';
34
34
  export type { ServiceApp } from './mixins/OxyServices.utility';
35
+ export type { CreateManagedAccountInput, ManagedAccountManager, ManagedAccount } from './mixins/OxyServices.managedAccounts';
35
36
 
36
37
  // --- Crypto / Identity ---
37
38
  export { KeyManager, SignatureService, RecoveryPhraseService } from './crypto';
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Managed Accounts Methods Mixin
3
+ *
4
+ * Provides SDK methods for creating and managing sub-accounts (managed identities).
5
+ * Managed accounts are full User documents without passwords, accessible only
6
+ * by their owners/managers via the X-Acting-As header mechanism.
7
+ */
8
+ import type { User } from '../models/interfaces';
9
+ import type { OxyServicesBase } from '../OxyServices.base';
10
+
11
+ export interface CreateManagedAccountInput {
12
+ username: string;
13
+ name?: { first?: string; last?: string };
14
+ bio?: string;
15
+ avatar?: string;
16
+ }
17
+
18
+ export interface ManagedAccountManager {
19
+ userId: string;
20
+ role: 'owner' | 'admin' | 'editor';
21
+ addedAt: string;
22
+ addedBy?: string;
23
+ }
24
+
25
+ export interface ManagedAccount {
26
+ accountId: string;
27
+ ownerId: string;
28
+ managers: ManagedAccountManager[];
29
+ account?: User;
30
+ createdAt?: string;
31
+ updatedAt?: string;
32
+ }
33
+
34
+ export function OxyServicesManagedAccountsMixin<T extends typeof OxyServicesBase>(Base: T) {
35
+ return class extends Base {
36
+ constructor(...args: any[]) {
37
+ super(...(args as [any]));
38
+ }
39
+
40
+ /**
41
+ * Create a new managed account (sub-account).
42
+ *
43
+ * The server creates a User document with `isManagedAccount: true` and links
44
+ * it to the authenticated user as owner.
45
+ */
46
+ async createManagedAccount(data: CreateManagedAccountInput): Promise<ManagedAccount> {
47
+ try {
48
+ return await this.makeRequest<ManagedAccount>('POST', '/managed-accounts', data, {
49
+ cache: false,
50
+ });
51
+ } catch (error) {
52
+ throw this.handleError(error);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * List all accounts the authenticated user manages.
58
+ */
59
+ async getManagedAccounts(): Promise<ManagedAccount[]> {
60
+ try {
61
+ return await this.makeRequest<ManagedAccount[]>('GET', '/managed-accounts', undefined, {
62
+ cache: true,
63
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
64
+ });
65
+ } catch (error) {
66
+ throw this.handleError(error);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Get details for a specific managed account.
72
+ */
73
+ async getManagedAccountDetails(accountId: string): Promise<ManagedAccount> {
74
+ try {
75
+ return await this.makeRequest<ManagedAccount>('GET', `/managed-accounts/${accountId}`, undefined, {
76
+ cache: true,
77
+ cacheTTL: 2 * 60 * 1000,
78
+ });
79
+ } catch (error) {
80
+ throw this.handleError(error);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Update a managed account's profile data.
86
+ * Requires owner or admin role.
87
+ */
88
+ async updateManagedAccount(accountId: string, data: Partial<CreateManagedAccountInput>): Promise<ManagedAccount> {
89
+ try {
90
+ return await this.makeRequest<ManagedAccount>('PUT', `/managed-accounts/${accountId}`, data, {
91
+ cache: false,
92
+ });
93
+ } catch (error) {
94
+ throw this.handleError(error);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Delete a managed account permanently.
100
+ * Requires owner role.
101
+ */
102
+ async deleteManagedAccount(accountId: string): Promise<void> {
103
+ try {
104
+ await this.makeRequest<void>('DELETE', `/managed-accounts/${accountId}`, undefined, {
105
+ cache: false,
106
+ });
107
+ } catch (error) {
108
+ throw this.handleError(error);
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Add a manager to a managed account.
114
+ * Requires owner or admin role on the account.
115
+ *
116
+ * @param accountId - The managed account to add the manager to
117
+ * @param userId - The user to grant management access
118
+ * @param role - The role to assign: 'admin' or 'editor'
119
+ */
120
+ async addManager(accountId: string, userId: string, role: 'admin' | 'editor'): Promise<void> {
121
+ try {
122
+ await this.makeRequest<void>('POST', `/managed-accounts/${accountId}/managers`, { userId, role }, {
123
+ cache: false,
124
+ });
125
+ } catch (error) {
126
+ throw this.handleError(error);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Remove a manager from a managed account.
132
+ * Requires owner role.
133
+ *
134
+ * @param accountId - The managed account
135
+ * @param userId - The manager to remove
136
+ */
137
+ async removeManager(accountId: string, userId: string): Promise<void> {
138
+ try {
139
+ await this.makeRequest<void>('DELETE', `/managed-accounts/${accountId}/managers/${userId}`, undefined, {
140
+ cache: false,
141
+ });
142
+ } catch (error) {
143
+ throw this.handleError(error);
144
+ }
145
+ }
146
+ };
147
+ }