@astram/frontend-core 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/LICENSE +21 -0
- package/dist/auth/accountService.d.ts +112 -0
- package/dist/auth/accountService.d.ts.map +1 -0
- package/dist/auth/accountService.js +210 -0
- package/dist/auth/accountService.js.map +1 -0
- package/dist/auth/auditService.d.ts +45 -0
- package/dist/auth/auditService.d.ts.map +1 -0
- package/dist/auth/auditService.js +58 -0
- package/dist/auth/auditService.js.map +1 -0
- package/dist/auth/authService.d.ts +96 -0
- package/dist/auth/authService.d.ts.map +1 -0
- package/dist/auth/authService.js +121 -0
- package/dist/auth/authService.js.map +1 -0
- package/dist/auth/identityService.d.ts +199 -0
- package/dist/auth/identityService.d.ts.map +1 -0
- package/dist/auth/identityService.js +374 -0
- package/dist/auth/identityService.js.map +1 -0
- package/dist/auth/index.d.ts +13 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +13 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/returnPath.d.ts +23 -0
- package/dist/auth/returnPath.d.ts.map +1 -0
- package/dist/auth/returnPath.js +95 -0
- package/dist/auth/returnPath.js.map +1 -0
- package/dist/auth/session.d.ts +38 -0
- package/dist/auth/session.d.ts.map +1 -0
- package/dist/auth/session.js +23 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/http/types.d.ts +33 -0
- package/dist/http/types.d.ts.map +1 -0
- package/dist/http/types.js +10 -0
- package/dist/http/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Astram
|
|
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.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createAccountService — platform-agnostic SELF-SCOPED account logic over an
|
|
3
|
+
* injected HTTP client (the authenticated user acting on their OWN credential/
|
|
4
|
+
* profile). Mirrors {@link createAuthService}: NO React / DOM / fetch / storage —
|
|
5
|
+
* the consuming app injects an {@link HttpClient} and the same service runs
|
|
6
|
+
* unchanged on web today and React Native next.
|
|
7
|
+
*
|
|
8
|
+
* Framework account routes (server-side, framework-owned, bearer-authenticated):
|
|
9
|
+
* POST /account/change-password { currentPassword, newPassword } → 204 | 401 | 422 | 501
|
|
10
|
+
* GET /account/mfa/devices → 200 { devices, mfaEnabled } | 401 | 501
|
|
11
|
+
* POST /account/mfa/devices/begin { name? } → 200 { deviceId, otpauthUri, secret } | 401 | 501
|
|
12
|
+
* POST /account/mfa/devices/confirm { deviceId, code } → 200 { recoveryCodes? } | 400 | 401 | 404 | 501
|
|
13
|
+
* PATCH /account/mfa/devices/:id/rename { name } → 200 { device } | 401 | 404 | 422 | 501
|
|
14
|
+
* DELETE /account/mfa/devices/:id { code } → 204 | 400 | 401 | 404 | 501
|
|
15
|
+
* POST /account/mfa/recovery-codes/regenerate { code } → 200 { recoveryCodes } | 400 | 401 | 501
|
|
16
|
+
* PATCH /account/profile { name?, email? } → 200 { …profile } | 401 | 422 | 501
|
|
17
|
+
*
|
|
18
|
+
* Every method resolves to a plain result object (never throws), so views stay dumb.
|
|
19
|
+
* A device `secret` is only ever returned by `beginMfaDevice` (the caller's own, for a
|
|
20
|
+
* QR); the display list/rename shapes never carry a secret.
|
|
21
|
+
*
|
|
22
|
+
* Verbatim TS port of astram3/shared/src/auth/accountService.js — logic, routes, and
|
|
23
|
+
* every result branch are unchanged; only types were added.
|
|
24
|
+
*/
|
|
25
|
+
import type { HttpClient } from '../http/types.js';
|
|
26
|
+
/** Display fields only — NEVER a secret or anything to render as a raw id. */
|
|
27
|
+
export interface MfaDevice {
|
|
28
|
+
id: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
confirmed?: boolean;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
export type ChangePasswordResult = {
|
|
34
|
+
ok: true;
|
|
35
|
+
} | {
|
|
36
|
+
ok: false;
|
|
37
|
+
status: number;
|
|
38
|
+
error: string;
|
|
39
|
+
};
|
|
40
|
+
export type ListMfaDevicesResult = {
|
|
41
|
+
ok: true;
|
|
42
|
+
devices: MfaDevice[];
|
|
43
|
+
mfaEnabled: boolean;
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
status: number;
|
|
47
|
+
error: string;
|
|
48
|
+
};
|
|
49
|
+
export type BeginMfaDeviceResult = {
|
|
50
|
+
ok: true;
|
|
51
|
+
deviceId: string | undefined;
|
|
52
|
+
otpauthUri: string | undefined;
|
|
53
|
+
secret: string | undefined;
|
|
54
|
+
} | {
|
|
55
|
+
ok: false;
|
|
56
|
+
status: number;
|
|
57
|
+
error: string;
|
|
58
|
+
};
|
|
59
|
+
export type ConfirmMfaDeviceResult = {
|
|
60
|
+
ok: true;
|
|
61
|
+
recoveryCodes: string[];
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
status: number;
|
|
65
|
+
error: string;
|
|
66
|
+
};
|
|
67
|
+
export type RenameMfaDeviceResult = {
|
|
68
|
+
ok: true;
|
|
69
|
+
device: MfaDevice | undefined;
|
|
70
|
+
} | {
|
|
71
|
+
ok: false;
|
|
72
|
+
status: number;
|
|
73
|
+
error: string;
|
|
74
|
+
};
|
|
75
|
+
export type RemoveMfaDeviceResult = {
|
|
76
|
+
ok: true;
|
|
77
|
+
} | {
|
|
78
|
+
ok: false;
|
|
79
|
+
status: number;
|
|
80
|
+
error: string;
|
|
81
|
+
};
|
|
82
|
+
export type RegenerateRecoveryCodesResult = {
|
|
83
|
+
ok: true;
|
|
84
|
+
recoveryCodes: string[];
|
|
85
|
+
} | {
|
|
86
|
+
ok: false;
|
|
87
|
+
status: number;
|
|
88
|
+
error: string;
|
|
89
|
+
};
|
|
90
|
+
export type UpdateProfileResult = {
|
|
91
|
+
ok: true;
|
|
92
|
+
profile: unknown;
|
|
93
|
+
} | {
|
|
94
|
+
ok: false;
|
|
95
|
+
status: number;
|
|
96
|
+
error: string;
|
|
97
|
+
};
|
|
98
|
+
export interface AccountService {
|
|
99
|
+
changePassword(accessToken: string, currentPassword: string, newPassword: string): Promise<ChangePasswordResult>;
|
|
100
|
+
listMfaDevices(accessToken: string): Promise<ListMfaDevicesResult>;
|
|
101
|
+
beginMfaDevice(accessToken: string, name?: string): Promise<BeginMfaDeviceResult>;
|
|
102
|
+
confirmMfaDevice(accessToken: string, deviceId: string, code: string): Promise<ConfirmMfaDeviceResult>;
|
|
103
|
+
renameMfaDevice(accessToken: string, deviceId: string, name: string): Promise<RenameMfaDeviceResult>;
|
|
104
|
+
removeMfaDevice(accessToken: string, deviceId: string, code: string): Promise<RemoveMfaDeviceResult>;
|
|
105
|
+
regenerateRecoveryCodes(accessToken: string, code: string): Promise<RegenerateRecoveryCodesResult>;
|
|
106
|
+
updateProfile(accessToken: string, changes: {
|
|
107
|
+
name?: string;
|
|
108
|
+
email?: string;
|
|
109
|
+
}): Promise<UpdateProfileResult>;
|
|
110
|
+
}
|
|
111
|
+
export declare function createAccountService(http: HttpClient): AccountService;
|
|
112
|
+
//# sourceMappingURL=accountService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountService.d.ts","sourceRoot":"","sources":["../../src/auth/accountService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
export function createAccountService(http) {
|
|
2
|
+
return {
|
|
3
|
+
/**
|
|
4
|
+
* Change the signed-in user's own password. The server verifies the current
|
|
5
|
+
* password, writes the new one, and revokes the user's OTHER sessions (the
|
|
6
|
+
* current session stays alive). Resolves to:
|
|
7
|
+
* { ok: true } — changed (204)
|
|
8
|
+
* { ok: false, status, error } — 401 (current incorrect), 422 (weak new
|
|
9
|
+
* password), 501 (not available), or other.
|
|
10
|
+
* @param accessToken the caller's bearer access token
|
|
11
|
+
*/
|
|
12
|
+
async changePassword(accessToken, currentPassword, newPassword) {
|
|
13
|
+
const r = await http.post('/account/change-password', { currentPassword, newPassword }, { bearerToken: accessToken });
|
|
14
|
+
if (r.status === 204)
|
|
15
|
+
return { ok: true };
|
|
16
|
+
if (r.status === 401)
|
|
17
|
+
return { ok: false, status: 401, error: 'Current password is incorrect.' };
|
|
18
|
+
if (r.status === 422) {
|
|
19
|
+
return { ok: false, status: 422, error: 'New password does not meet the requirements.' };
|
|
20
|
+
}
|
|
21
|
+
if (r.status === 501)
|
|
22
|
+
return { ok: false, status: 501, error: 'Password change is not available.' };
|
|
23
|
+
return { ok: false, status: r.status, error: 'Could not change password.' };
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* List the signed-in user's CONFIRMED authenticator devices (multi-device
|
|
27
|
+
* MFA). Each device carries display fields only (name, added/last-used timestamps,
|
|
28
|
+
* confirmed) — NEVER a secret or anything to render as a raw id to the user.
|
|
29
|
+
* Resolves to:
|
|
30
|
+
* { ok: true, devices, mfaEnabled } — 200
|
|
31
|
+
* { ok: false, status, error } — 401 (sign in again), 501 (not available), other
|
|
32
|
+
* @param accessToken the caller's bearer access token
|
|
33
|
+
*/
|
|
34
|
+
async listMfaDevices(accessToken) {
|
|
35
|
+
const r = await http.get('/account/mfa/devices', { bearerToken: accessToken });
|
|
36
|
+
if (r.status === 200) {
|
|
37
|
+
const data = r.data;
|
|
38
|
+
return {
|
|
39
|
+
ok: true,
|
|
40
|
+
devices: data?.devices ?? [],
|
|
41
|
+
mfaEnabled: data?.mfaEnabled ?? false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (r.status === 401)
|
|
45
|
+
return { ok: false, status: 401, error: 'Please sign in again.' };
|
|
46
|
+
if (r.status === 501) {
|
|
47
|
+
return { ok: false, status: 501, error: 'Two-factor authentication is not available.' };
|
|
48
|
+
}
|
|
49
|
+
return { ok: false, status: r.status, error: 'Could not load your two-factor devices.' };
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* Begin enrolling a NEW authenticator device. The server mints a fresh secret and
|
|
53
|
+
* returns the provisioning material — the `otpauthUri` (for a QR) + the `secret`
|
|
54
|
+
* (for manual entry) — plus the `deviceId` to confirm. NOT yet active; the user must
|
|
55
|
+
* confirm a live code. An optional `name` labels the device (blank → the server
|
|
56
|
+
* defaults it). Resolves to:
|
|
57
|
+
* { ok: true, deviceId, otpauthUri, secret } — begun (200)
|
|
58
|
+
* { ok: false, status, error } — 401, 501, other
|
|
59
|
+
* @param accessToken the caller's bearer access token
|
|
60
|
+
* @param name an optional device label
|
|
61
|
+
*/
|
|
62
|
+
async beginMfaDevice(accessToken, name) {
|
|
63
|
+
const r = await http.post('/account/mfa/devices/begin', typeof name === 'string' ? { name } : {}, { bearerToken: accessToken });
|
|
64
|
+
if (r.status === 200) {
|
|
65
|
+
const data = r.data;
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
deviceId: data?.deviceId,
|
|
69
|
+
otpauthUri: data?.otpauthUri,
|
|
70
|
+
secret: data?.secret,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (r.status === 401)
|
|
74
|
+
return { ok: false, status: 401, error: 'Please sign in again.' };
|
|
75
|
+
if (r.status === 501) {
|
|
76
|
+
return { ok: false, status: 501, error: 'Two-factor authentication is not available.' };
|
|
77
|
+
}
|
|
78
|
+
return { ok: false, status: r.status, error: 'Could not start two-factor setup.' };
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* Confirm a pending device by presenting a live code from it. On success the server
|
|
82
|
+
* activates the device; on the FIRST device it also returns the one-time recovery
|
|
83
|
+
* codes ONCE (show them prominently — they are never shown again). Subsequent devices
|
|
84
|
+
* return no recovery codes. Resolves to:
|
|
85
|
+
* { ok: true, recoveryCodes } — activated (200); recoveryCodes is [] for a
|
|
86
|
+
* non-first device.
|
|
87
|
+
* { ok: false, status, error } — 400 (wrong/expired code), 404 (unknown device),
|
|
88
|
+
* 401, 501, other
|
|
89
|
+
* @param deviceId the device from `beginMfaDevice`
|
|
90
|
+
* @param code the 6-digit code from the authenticator app
|
|
91
|
+
*/
|
|
92
|
+
async confirmMfaDevice(accessToken, deviceId, code) {
|
|
93
|
+
const r = await http.post('/account/mfa/devices/confirm', { deviceId, code }, { bearerToken: accessToken });
|
|
94
|
+
if (r.status === 200) {
|
|
95
|
+
const data = r.data;
|
|
96
|
+
return { ok: true, recoveryCodes: data?.recoveryCodes ?? [] };
|
|
97
|
+
}
|
|
98
|
+
if (r.status === 400) {
|
|
99
|
+
return { ok: false, status: 400, error: 'That code is incorrect or has expired. Try again.' };
|
|
100
|
+
}
|
|
101
|
+
if (r.status === 404)
|
|
102
|
+
return { ok: false, status: 404, error: 'That device was not found.' };
|
|
103
|
+
if (r.status === 401)
|
|
104
|
+
return { ok: false, status: 401, error: 'Please sign in again.' };
|
|
105
|
+
if (r.status === 501) {
|
|
106
|
+
return { ok: false, status: 501, error: 'Two-factor authentication is not available.' };
|
|
107
|
+
}
|
|
108
|
+
return { ok: false, status: r.status, error: 'Could not confirm two-factor setup.' };
|
|
109
|
+
},
|
|
110
|
+
/**
|
|
111
|
+
* Rename one of the signed-in user's devices (cosmetic — no code required). On
|
|
112
|
+
* success the server returns the refreshed device (display fields only). Resolves to:
|
|
113
|
+
* { ok: true, device } — renamed (200)
|
|
114
|
+
* { ok: false, status, error } — 422 (blank name), 404 (unknown/not owned), 401,
|
|
115
|
+
* 501, other
|
|
116
|
+
* @param name the new label (must be non-empty after trim)
|
|
117
|
+
*/
|
|
118
|
+
async renameMfaDevice(accessToken, deviceId, name) {
|
|
119
|
+
const r = await http.patch(`/account/mfa/devices/${encodeURIComponent(deviceId)}/rename`, { name }, { bearerToken: accessToken });
|
|
120
|
+
if (r.status === 200) {
|
|
121
|
+
const data = r.data;
|
|
122
|
+
return { ok: true, device: data?.device };
|
|
123
|
+
}
|
|
124
|
+
if (r.status === 422)
|
|
125
|
+
return { ok: false, status: 422, error: 'Please enter a device name.' };
|
|
126
|
+
if (r.status === 404)
|
|
127
|
+
return { ok: false, status: 404, error: 'That device was not found.' };
|
|
128
|
+
if (r.status === 401)
|
|
129
|
+
return { ok: false, status: 401, error: 'Please sign in again.' };
|
|
130
|
+
if (r.status === 501) {
|
|
131
|
+
return { ok: false, status: 501, error: 'Two-factor authentication is not available.' };
|
|
132
|
+
}
|
|
133
|
+
return { ok: false, status: r.status, error: 'Could not rename the device.' };
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* Remove one of the signed-in user's devices. Requires proof — a current TOTP code
|
|
137
|
+
* from ANY remaining device OR one of the saved recovery codes. Removing the LAST
|
|
138
|
+
* device turns MFA off (and wipes recovery codes). Resolves to:
|
|
139
|
+
* { ok: true } — removed (204)
|
|
140
|
+
* { ok: false, status, error } — 400 (wrong code), 404 (unknown/not owned), 401,
|
|
141
|
+
* 501, other
|
|
142
|
+
* @param code a TOTP or recovery code (proof)
|
|
143
|
+
*/
|
|
144
|
+
async removeMfaDevice(accessToken, deviceId, code) {
|
|
145
|
+
const r = await http.delete(`/account/mfa/devices/${encodeURIComponent(deviceId)}`, { code }, { bearerToken: accessToken });
|
|
146
|
+
if (r.status === 204)
|
|
147
|
+
return { ok: true };
|
|
148
|
+
if (r.status === 400) {
|
|
149
|
+
return { ok: false, status: 400, error: 'That code is incorrect. Enter a current or recovery code.' };
|
|
150
|
+
}
|
|
151
|
+
if (r.status === 404)
|
|
152
|
+
return { ok: false, status: 404, error: 'That device was not found.' };
|
|
153
|
+
if (r.status === 401)
|
|
154
|
+
return { ok: false, status: 401, error: 'Please sign in again.' };
|
|
155
|
+
if (r.status === 501) {
|
|
156
|
+
return { ok: false, status: 501, error: 'Two-factor authentication is not available.' };
|
|
157
|
+
}
|
|
158
|
+
return { ok: false, status: r.status, error: 'Could not remove the device.' };
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* Regenerate the account's recovery codes (replacing the old set). Requires MFA to be
|
|
162
|
+
* enabled and proof — a current TOTP from any device OR an existing recovery code. The
|
|
163
|
+
* fresh codes are returned ONCE. Resolves to:
|
|
164
|
+
* { ok: true, recoveryCodes } — regenerated (200)
|
|
165
|
+
* { ok: false, status, error } — 400 (MFA off / wrong code), 401, 501, other
|
|
166
|
+
* @param code a TOTP or recovery code (proof)
|
|
167
|
+
*/
|
|
168
|
+
async regenerateRecoveryCodes(accessToken, code) {
|
|
169
|
+
const r = await http.post('/account/mfa/recovery-codes/regenerate', { code }, { bearerToken: accessToken });
|
|
170
|
+
if (r.status === 200) {
|
|
171
|
+
const data = r.data;
|
|
172
|
+
return { ok: true, recoveryCodes: data?.recoveryCodes ?? [] };
|
|
173
|
+
}
|
|
174
|
+
if (r.status === 400) {
|
|
175
|
+
return { ok: false, status: 400, error: 'That code is incorrect, or two-factor is not enabled.' };
|
|
176
|
+
}
|
|
177
|
+
if (r.status === 401)
|
|
178
|
+
return { ok: false, status: 401, error: 'Please sign in again.' };
|
|
179
|
+
if (r.status === 501) {
|
|
180
|
+
return { ok: false, status: 501, error: 'Two-factor authentication is not available.' };
|
|
181
|
+
}
|
|
182
|
+
return { ok: false, status: r.status, error: 'Could not regenerate recovery codes.' };
|
|
183
|
+
},
|
|
184
|
+
/**
|
|
185
|
+
* Edit the signed-in user's own display profile (name and/or email). Only the
|
|
186
|
+
* supplied fields are written (partial edit). On success the server returns the
|
|
187
|
+
* REFRESHED display profile (names, never UUIDs) — hand it straight to the UI so
|
|
188
|
+
* the header/user card updates in place. Resolves to:
|
|
189
|
+
* { ok: true, profile } — updated (200)
|
|
190
|
+
* { ok: false, status, error } — 401 (sign in again), 422 (nothing to change /
|
|
191
|
+
* invalid email), 501 (not available), or other.
|
|
192
|
+
* @param accessToken the caller's bearer access token
|
|
193
|
+
*/
|
|
194
|
+
async updateProfile(accessToken, changes) {
|
|
195
|
+
const r = await http.patch('/account/profile', changes, { bearerToken: accessToken });
|
|
196
|
+
if (r.status === 200)
|
|
197
|
+
return { ok: true, profile: r.data };
|
|
198
|
+
if (r.status === 401)
|
|
199
|
+
return { ok: false, status: 401, error: 'Please sign in again.' };
|
|
200
|
+
if (r.status === 422) {
|
|
201
|
+
return { ok: false, status: 422, error: 'Please enter a valid name and email.' };
|
|
202
|
+
}
|
|
203
|
+
if (r.status === 501) {
|
|
204
|
+
return { ok: false, status: 501, error: 'Profile editing is not available.' };
|
|
205
|
+
}
|
|
206
|
+
return { ok: false, status: r.status, error: 'Could not update your profile.' };
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=accountService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accountService.js","sourceRoot":"","sources":["../../src/auth/accountService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createAuditService — platform-agnostic calls to the identity feature's audit-log
|
|
3
|
+
* API. Like identityService, it takes an injected HTTP client and carries no platform
|
|
4
|
+
* code, so web + mobile share it. The bearer token is passed per call (the UI holds
|
|
5
|
+
* the session; the shared logic stays stateless).
|
|
6
|
+
*
|
|
7
|
+
* Every method resolves to a plain result object (never throws): `{ ok:true, ... }`
|
|
8
|
+
* on success, `{ ok:false, status, error }` on any non-2xx (401 = not authenticated,
|
|
9
|
+
* 403 = denied by policy, anything else = a generic failure). Views stay dumb: they
|
|
10
|
+
* branch on `ok`/`status` and render the message.
|
|
11
|
+
*
|
|
12
|
+
* The server route this hits: GET /identity/audit — resolved audit rows, no raw
|
|
13
|
+
* uuid ever a display value (actor/tenant are resolved to names server-side).
|
|
14
|
+
*
|
|
15
|
+
* Verbatim TS port of astram3/shared/src/auth/auditService.js — logic, routes, and
|
|
16
|
+
* every result branch are unchanged; only types were added.
|
|
17
|
+
*/
|
|
18
|
+
import type { HttpClient } from '../http/types.js';
|
|
19
|
+
/** Filter/cursor bag for {@link AuditService.list}. All fields optional. */
|
|
20
|
+
export interface AuditQuery {
|
|
21
|
+
type?: string;
|
|
22
|
+
actor?: string;
|
|
23
|
+
tenant?: string;
|
|
24
|
+
before?: string;
|
|
25
|
+
limit?: number;
|
|
26
|
+
}
|
|
27
|
+
/** A single resolved audit row. Concrete shape is server-defined. */
|
|
28
|
+
export interface AuditRecord {
|
|
29
|
+
id?: string;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
export type ListAuditResult = {
|
|
33
|
+
ok: true;
|
|
34
|
+
data: AuditRecord[];
|
|
35
|
+
nextCursor: string | null;
|
|
36
|
+
} | {
|
|
37
|
+
ok: false;
|
|
38
|
+
status: number;
|
|
39
|
+
error: string;
|
|
40
|
+
};
|
|
41
|
+
export interface AuditService {
|
|
42
|
+
list(accessToken: string, query?: AuditQuery): Promise<ListAuditResult>;
|
|
43
|
+
}
|
|
44
|
+
export declare function createAuditService(http: HttpClient): AuditService;
|
|
45
|
+
//# sourceMappingURL=auditService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditService.d.ts","sourceRoot":"","sources":["../../src/auth/auditService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qEAAqE;AACrE,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,WAAW,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACzE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY,CA0DjE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function createAuditService(http) {
|
|
2
|
+
/**
|
|
3
|
+
* Build a `?type&actor&tenant&before&limit` query string from a filter/cursor bag,
|
|
4
|
+
* omitting anything absent/empty so the server applies its defaults. Values are
|
|
5
|
+
* URL-encoded. Returns '' (no leading '?') when nothing is set.
|
|
6
|
+
*
|
|
7
|
+
* Built with plain `encodeURIComponent` (not `URLSearchParams`) — this package has
|
|
8
|
+
* no `lib.dom`/Node ambient types (it must run unchanged on web AND React Native,
|
|
9
|
+
* where `URLSearchParams` isn't guaranteed to exist), and every key here is set at
|
|
10
|
+
* most once, so a manual `key=value` join is byte-identical to the source's
|
|
11
|
+
* `URLSearchParams#toString()` output.
|
|
12
|
+
*/
|
|
13
|
+
function listQueryString(query) {
|
|
14
|
+
const q = query ?? {};
|
|
15
|
+
const parts = [];
|
|
16
|
+
for (const [key, value] of Object.entries(q)) {
|
|
17
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
18
|
+
parts.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return parts.length > 0 ? `?${parts.join('&')}` : '';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Shared translator for a failure response → a friendly result. Centralizes the
|
|
25
|
+
* 401/403/other mapping so every method reports consistently (mirrors identityService).
|
|
26
|
+
*/
|
|
27
|
+
function fail(r, generic) {
|
|
28
|
+
if (r.status === 401) {
|
|
29
|
+
return { ok: false, status: 401, error: 'Not authenticated (no/invalid token).' };
|
|
30
|
+
}
|
|
31
|
+
if (r.status === 403) {
|
|
32
|
+
return { ok: false, status: 403, error: 'You need platform-admin access to do this.' };
|
|
33
|
+
}
|
|
34
|
+
return { ok: false, status: r.status, error: generic };
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
/**
|
|
38
|
+
* Page of resolved audit rows + a cursor for the next page.
|
|
39
|
+
* @param accessToken the caller's bearer access token
|
|
40
|
+
* Resolves: { ok:true, data, nextCursor } | { ok:false, status, error }
|
|
41
|
+
*/
|
|
42
|
+
async list(accessToken, query) {
|
|
43
|
+
const r = await http.get(`/identity/audit${listQueryString(query)}`, {
|
|
44
|
+
bearerToken: accessToken,
|
|
45
|
+
});
|
|
46
|
+
if (r.ok) {
|
|
47
|
+
const body = (r.data ?? {});
|
|
48
|
+
return {
|
|
49
|
+
ok: true,
|
|
50
|
+
data: body.data ?? [],
|
|
51
|
+
nextCursor: body.nextCursor ?? null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return fail(r, 'Could not load the audit log.');
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=auditService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditService.js","sourceRoot":"","sources":["../../src/auth/auditService.ts"],"names":[],"mappings":"AA0CA,MAAM,UAAU,kBAAkB,CAAC,IAAgB;IACjD;;;;;;;;;;OAUG;IACH,SAAS,eAAe,CAAC,KAAkB;QACzC,MAAM,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;gBAC1D,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,CAAC;IAED;;;OAGG;IACH,SAAS,IAAI,CAAC,CAAqB,EAAE,OAAe;QAClD,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,uCAAuC,EAAE,CAAC;QACpF,CAAC;QACD,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC;QACzF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACzD,CAAC;IAED,OAAO;QACL;;;;WAIG;QACH,KAAK,CAAC,IAAI,CAAC,WAAmB,EAAE,KAAkB;YAChD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,kBAAkB,eAAe,CAAC,KAAK,CAAC,EAAE,EAAE;gBACnE,WAAW,EAAE,WAAW;aACzB,CAAC,CAAC;YACH,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;gBACT,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAyD,CAAC;gBACpF,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;oBACrB,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;iBACpC,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAAC;QAClD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createAuthService — platform-agnostic auth logic over an injected HTTP client.
|
|
3
|
+
*
|
|
4
|
+
* This is the SINGLE source of truth for how a UI talks to the framework auth
|
|
5
|
+
* surface. It has NO React / DOM / fetch / storage — the consuming app injects a
|
|
6
|
+
* {@link HttpClient} (web: fetch-based; mobile: RN's). The same service therefore
|
|
7
|
+
* runs unchanged on web today and React Native next.
|
|
8
|
+
*
|
|
9
|
+
* Framework auth routes (server-side, framework-owned):
|
|
10
|
+
* POST /auth/login { identityHint, password } → { session, tokens } | { mfaRequired, challengeToken }
|
|
11
|
+
* POST /auth/mfa { challengeToken, factor, code } → { session, tokens }
|
|
12
|
+
* POST /auth/refresh { refreshToken } → { session, tokens }
|
|
13
|
+
* POST /auth/logout { sessionId } → 204
|
|
14
|
+
*
|
|
15
|
+
* Every method resolves to a plain result object (never throws), so views stay dumb.
|
|
16
|
+
*
|
|
17
|
+
* Verbatim TS port of astram3/shared/src/auth/authService.js — logic, routes, and
|
|
18
|
+
* every result branch are unchanged; only types were added.
|
|
19
|
+
*/
|
|
20
|
+
import type { HttpClient } from '../http/types.js';
|
|
21
|
+
import type { Session, SessionTokens } from './session.js';
|
|
22
|
+
/** Shape of a display profile returned by GET /auth/me. */
|
|
23
|
+
export interface AuthProfile {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
email?: string;
|
|
27
|
+
company?: {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
roles?: Array<{
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
}>;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
export type LoginResult = {
|
|
38
|
+
ok: true;
|
|
39
|
+
session: Session;
|
|
40
|
+
tokens: SessionTokens;
|
|
41
|
+
} | {
|
|
42
|
+
ok: 'mfa';
|
|
43
|
+
challengeToken: string;
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
error: string;
|
|
47
|
+
};
|
|
48
|
+
export type CompleteMfaResult = {
|
|
49
|
+
ok: true;
|
|
50
|
+
session: Session;
|
|
51
|
+
tokens: SessionTokens;
|
|
52
|
+
mfaRecoveryUsed: boolean;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
error: string;
|
|
56
|
+
};
|
|
57
|
+
export type RefreshResult = {
|
|
58
|
+
ok: true;
|
|
59
|
+
session: Session;
|
|
60
|
+
tokens: SessionTokens;
|
|
61
|
+
} | {
|
|
62
|
+
ok: false;
|
|
63
|
+
error: string;
|
|
64
|
+
};
|
|
65
|
+
export type LogoutResult = {
|
|
66
|
+
ok: true;
|
|
67
|
+
};
|
|
68
|
+
export type MeResult = {
|
|
69
|
+
ok: true;
|
|
70
|
+
profile: AuthProfile;
|
|
71
|
+
} | {
|
|
72
|
+
ok: false;
|
|
73
|
+
status: number;
|
|
74
|
+
error: string;
|
|
75
|
+
};
|
|
76
|
+
export type RequestResetResult = {
|
|
77
|
+
ok: true;
|
|
78
|
+
};
|
|
79
|
+
export type ResetPasswordResult = {
|
|
80
|
+
ok: true;
|
|
81
|
+
} | {
|
|
82
|
+
ok: false;
|
|
83
|
+
status: number;
|
|
84
|
+
error: string;
|
|
85
|
+
};
|
|
86
|
+
export interface AuthService {
|
|
87
|
+
login(identityHint: string, password: string): Promise<LoginResult>;
|
|
88
|
+
completeMfa(challengeToken: string, code: string, factor?: string): Promise<CompleteMfaResult>;
|
|
89
|
+
refresh(refreshToken: string): Promise<RefreshResult>;
|
|
90
|
+
logout(sessionId: string | undefined): Promise<LogoutResult>;
|
|
91
|
+
me(accessToken: string): Promise<MeResult>;
|
|
92
|
+
requestReset(identityHint: string): Promise<RequestResetResult>;
|
|
93
|
+
resetPassword(token: string, newPassword: string): Promise<ResetPasswordResult>;
|
|
94
|
+
}
|
|
95
|
+
export declare function createAuthService(http: HttpClient): AuthService;
|
|
96
|
+
//# sourceMappingURL=authService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/auth/authService.ts"],"names":[],"mappings":"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"}
|