@~lyre/auth 0.3.0 → 0.5.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/dist/admin.d.ts +110 -6
- package/dist/admin.js +83 -4
- package/package.json +71 -71
package/dist/admin.d.ts
CHANGED
|
@@ -61,11 +61,54 @@ interface AppMember {
|
|
|
61
61
|
via?: 'member' | 'role';
|
|
62
62
|
}
|
|
63
63
|
interface InviteResult {
|
|
64
|
-
/** `
|
|
65
|
-
* `
|
|
66
|
-
|
|
64
|
+
/** `invited` = an invite was created and an accept link emailed;
|
|
65
|
+
* `already_member` = that email already has access to the app (returned with HTTP 409).
|
|
66
|
+
* (`added` is legacy: Accounts no longer grants access without acceptance.) */
|
|
67
|
+
status: 'invited' | 'already_member' | 'added';
|
|
68
|
+
/** The workspace the invitee joins on acceptance, echoed back; null when none was requested. */
|
|
69
|
+
userGroupId?: string | null;
|
|
67
70
|
[key: string]: unknown;
|
|
68
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* A profile field an app collects about a user — the spec a consumer renders a form input from.
|
|
74
|
+
* Mirrors Accounts' `fields` registry, so a consumer never hardcodes one app's idea of a person.
|
|
75
|
+
*/
|
|
76
|
+
interface ProfileFieldSpec {
|
|
77
|
+
id: string;
|
|
78
|
+
fieldName: string;
|
|
79
|
+
label: string;
|
|
80
|
+
fieldType: string;
|
|
81
|
+
required: boolean;
|
|
82
|
+
hidden: boolean;
|
|
83
|
+
enumValues: string[] | null;
|
|
84
|
+
inputPrefix: string | null;
|
|
85
|
+
validationRegex: string | null;
|
|
86
|
+
validationMessage: string | null;
|
|
87
|
+
displayOrder: number;
|
|
88
|
+
}
|
|
89
|
+
/** Whether an email can be invited to an app. See `checkInviteEmail`. */
|
|
90
|
+
interface InviteEmailCheck {
|
|
91
|
+
email: string;
|
|
92
|
+
/** `member` = already has app access; `invited` = a live invite exists; `available` = go ahead. */
|
|
93
|
+
state: 'member' | 'invited' | 'available';
|
|
94
|
+
/** Whether an Accounts user already exists for this address (they keep their own profile). */
|
|
95
|
+
hasAccount: boolean;
|
|
96
|
+
/** When the pending invite lapses — only for `invited`. */
|
|
97
|
+
expiresAt?: string;
|
|
98
|
+
}
|
|
99
|
+
/** A pending (not yet accepted, not yet expired) app invitation. */
|
|
100
|
+
interface PendingInvite {
|
|
101
|
+
id: string;
|
|
102
|
+
email: string;
|
|
103
|
+
/** The workspace the invitee joins on acceptance; null for a group-less invite. */
|
|
104
|
+
userGroupId: string | null;
|
|
105
|
+
/** Values captured on the invite form, keyed by field name. Prefills the accept form. */
|
|
106
|
+
profileData?: Record<string, string>;
|
|
107
|
+
createdAt: string;
|
|
108
|
+
expiresAt: string;
|
|
109
|
+
/** The inviter's email, when still resolvable. */
|
|
110
|
+
invitedByEmail?: string | null;
|
|
111
|
+
}
|
|
69
112
|
interface GroupMember {
|
|
70
113
|
id: string;
|
|
71
114
|
email: string;
|
|
@@ -106,11 +149,36 @@ declare class AccountsAdmin {
|
|
|
106
149
|
confirmPassword?: string;
|
|
107
150
|
revokeOtherSessions?: boolean;
|
|
108
151
|
}): Promise<ChangePasswordResult>;
|
|
109
|
-
/**
|
|
110
|
-
*
|
|
152
|
+
/**
|
|
153
|
+
* Invite an email to an app, emailing them an accept link. Session-authed (the caller must have
|
|
154
|
+
* access to the app).
|
|
155
|
+
*
|
|
156
|
+
* Pass `userGroupId` to say which WORKSPACE the invitee joins when they accept. Omit it and they
|
|
157
|
+
* join the app but no workspace — Accounts then treats them as a brand-new user on first login and
|
|
158
|
+
* sends them to create their own, instead of landing in the workspace that invited them. Accounts
|
|
159
|
+
* validates the group against the CALLER's own memberships, so this cannot target a foreign group.
|
|
160
|
+
*/
|
|
111
161
|
inviteToApp(appSlug: string, input: {
|
|
112
162
|
email: string;
|
|
163
|
+
userGroupId?: string | null;
|
|
164
|
+
/** Profile values from the invite form, keyed by field name (see `listAppProfileFields`).
|
|
165
|
+
* Accounts drops any key the app does not collect, so authorization-shaped values
|
|
166
|
+
* (role, assignability) can never be set from here. */
|
|
167
|
+
profileData?: Record<string, string> | null;
|
|
113
168
|
}): Promise<InviteResult>;
|
|
169
|
+
/**
|
|
170
|
+
* The profile fields this app collects about a user — the spec to render an invite form from.
|
|
171
|
+
* Session-authed. Never includes email: that is the invite's identity, not a profile value.
|
|
172
|
+
*/
|
|
173
|
+
listAppProfileFields(appSlug: string): Promise<ProfileFieldSpec[]>;
|
|
174
|
+
/**
|
|
175
|
+
* Whether an email can be invited — for a live check on an invite form, so the inviter learns
|
|
176
|
+
* BEFORE submitting that someone is already a member or already invited. Email only, exact match.
|
|
177
|
+
* Session-authed.
|
|
178
|
+
*/
|
|
179
|
+
checkInviteEmail(appSlug: string, email: string): Promise<InviteEmailCheck>;
|
|
180
|
+
/** Revoke a pending invite (withdraw one sent in error). Session-authed. */
|
|
181
|
+
revokeAppInvite(appSlug: string, inviteId: string): Promise<void>;
|
|
114
182
|
/** Everyone with access to an app. Session-authed. */
|
|
115
183
|
listAppUsers(appSlug: string): Promise<AppMember[]>;
|
|
116
184
|
/** Revoke a user's access to an app. Session-authed. */
|
|
@@ -118,7 +186,43 @@ declare class AccountsAdmin {
|
|
|
118
186
|
/** Members of a user_group (the workspace's agents). Service-key authed (`stats:read`). */
|
|
119
187
|
listGroupMembers(groupId: string, opts?: {
|
|
120
188
|
appId?: string;
|
|
189
|
+
search?: string;
|
|
190
|
+
limit?: number;
|
|
191
|
+
offset?: number;
|
|
121
192
|
}): Promise<GroupMember[]>;
|
|
193
|
+
/**
|
|
194
|
+
* A PAGE of a group's members, plus the total before paging — what a paginated, searchable user
|
|
195
|
+
* list needs. `listGroupMembers` stays the "give me everyone" convenience for the agent picker.
|
|
196
|
+
*
|
|
197
|
+
* Search and paging are applied by Accounts in SQL: fetching every member to filter in the browser
|
|
198
|
+
* does not scale, and leaves the pager unable to know how many pages exist. Omit `limit` and the
|
|
199
|
+
* full list comes back (with `totalCount` equal to its length).
|
|
200
|
+
*/
|
|
201
|
+
listGroupMembersPage(groupId: string, opts?: {
|
|
202
|
+
appId?: string;
|
|
203
|
+
search?: string;
|
|
204
|
+
limit?: number;
|
|
205
|
+
offset?: number;
|
|
206
|
+
}): Promise<{
|
|
207
|
+
members: GroupMember[];
|
|
208
|
+
totalCount: number;
|
|
209
|
+
}>;
|
|
210
|
+
/**
|
|
211
|
+
* The app's PENDING invites — people invited but not yet accepted. Session-authed.
|
|
212
|
+
*
|
|
213
|
+
* An invitee is not a membership, so they appear in no member list; without this a successful
|
|
214
|
+
* invite looks like nothing happened until the person accepts. Pass `userGroupId` to scope to one
|
|
215
|
+
* workspace. Accounts excludes consumed and expired invites.
|
|
216
|
+
*/
|
|
217
|
+
listAppInvites(appSlug: string, opts?: {
|
|
218
|
+
userGroupId?: string;
|
|
219
|
+
search?: string;
|
|
220
|
+
limit?: number;
|
|
221
|
+
offset?: number;
|
|
222
|
+
}): Promise<{
|
|
223
|
+
invites: PendingInvite[];
|
|
224
|
+
totalCount: number;
|
|
225
|
+
}>;
|
|
122
226
|
/** Read a user's app-scoped `profileData` (the store for per-app preferences). Session-authed. */
|
|
123
227
|
getAppProfile(userId: string): Promise<Record<string, string>>;
|
|
124
228
|
/** Write app-scoped `profileData` fields for a user (e.g. availability, soundEnabled). All values
|
|
@@ -143,4 +247,4 @@ declare class AccountsAdmin {
|
|
|
143
247
|
/** Convenience factory mirroring the SDK's other `create*` helpers. */
|
|
144
248
|
declare function createAccountsAdmin(opts: AccountsAdminOptions): AccountsAdmin;
|
|
145
249
|
|
|
146
|
-
export { AccountsAdmin, AccountsAdminError, type AccountsAdminOptions, type AccountsUser, type AppMember, type ChangePasswordResult, type GroupMember, type InviteResult, type ListUsersQuery, type ListUsersResult, createAccountsAdmin };
|
|
250
|
+
export { AccountsAdmin, AccountsAdminError, type AccountsAdminOptions, type AccountsUser, type AppMember, type ChangePasswordResult, type GroupMember, type InviteEmailCheck, type InviteResult, type ListUsersQuery, type ListUsersResult, type PendingInvite, type ProfileFieldSpec, createAccountsAdmin };
|
package/dist/admin.js
CHANGED
|
@@ -64,14 +64,53 @@ var AccountsAdmin = class {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
// ── App membership (invite / list / remove) ─────────────────────────────────
|
|
67
|
-
/**
|
|
68
|
-
*
|
|
67
|
+
/**
|
|
68
|
+
* Invite an email to an app, emailing them an accept link. Session-authed (the caller must have
|
|
69
|
+
* access to the app).
|
|
70
|
+
*
|
|
71
|
+
* Pass `userGroupId` to say which WORKSPACE the invitee joins when they accept. Omit it and they
|
|
72
|
+
* join the app but no workspace — Accounts then treats them as a brand-new user on first login and
|
|
73
|
+
* sends them to create their own, instead of landing in the workspace that invited them. Accounts
|
|
74
|
+
* validates the group against the CALLER's own memberships, so this cannot target a foreign group.
|
|
75
|
+
*/
|
|
69
76
|
async inviteToApp(appSlug, input) {
|
|
70
77
|
return this.request("POST", `/api/auth/apps/${encodeURIComponent(appSlug)}/invites`, {
|
|
71
78
|
auth: "session",
|
|
72
79
|
body: input
|
|
73
80
|
});
|
|
74
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* The profile fields this app collects about a user — the spec to render an invite form from.
|
|
84
|
+
* Session-authed. Never includes email: that is the invite's identity, not a profile value.
|
|
85
|
+
*/
|
|
86
|
+
async listAppProfileFields(appSlug) {
|
|
87
|
+
const data = await this.request(
|
|
88
|
+
"GET",
|
|
89
|
+
`/api/auth/apps/${encodeURIComponent(appSlug)}/profile-fields`,
|
|
90
|
+
{ auth: "session" }
|
|
91
|
+
);
|
|
92
|
+
return data.fields ?? [];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether an email can be invited — for a live check on an invite form, so the inviter learns
|
|
96
|
+
* BEFORE submitting that someone is already a member or already invited. Email only, exact match.
|
|
97
|
+
* Session-authed.
|
|
98
|
+
*/
|
|
99
|
+
async checkInviteEmail(appSlug, email) {
|
|
100
|
+
return this.request(
|
|
101
|
+
"GET",
|
|
102
|
+
`/api/auth/apps/${encodeURIComponent(appSlug)}/invites/lookup?email=${encodeURIComponent(email)}`,
|
|
103
|
+
{ auth: "session" }
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
/** Revoke a pending invite (withdraw one sent in error). Session-authed. */
|
|
107
|
+
async revokeAppInvite(appSlug, inviteId) {
|
|
108
|
+
await this.request(
|
|
109
|
+
"DELETE",
|
|
110
|
+
`/api/auth/apps/${encodeURIComponent(appSlug)}/invites?inviteId=${encodeURIComponent(inviteId)}`,
|
|
111
|
+
{ auth: "session" }
|
|
112
|
+
);
|
|
113
|
+
}
|
|
75
114
|
/** Everyone with access to an app. Session-authed. */
|
|
76
115
|
async listAppUsers(appSlug) {
|
|
77
116
|
const data = await this.request(
|
|
@@ -92,13 +131,53 @@ var AccountsAdmin = class {
|
|
|
92
131
|
// ── Groups (agent picker) ───────────────────────────────────────────────────
|
|
93
132
|
/** Members of a user_group (the workspace's agents). Service-key authed (`stats:read`). */
|
|
94
133
|
async listGroupMembers(groupId, opts = {}) {
|
|
95
|
-
const
|
|
134
|
+
const { members } = await this.listGroupMembersPage(groupId, opts);
|
|
135
|
+
return members;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* A PAGE of a group's members, plus the total before paging — what a paginated, searchable user
|
|
139
|
+
* list needs. `listGroupMembers` stays the "give me everyone" convenience for the agent picker.
|
|
140
|
+
*
|
|
141
|
+
* Search and paging are applied by Accounts in SQL: fetching every member to filter in the browser
|
|
142
|
+
* does not scale, and leaves the pager unable to know how many pages exist. Omit `limit` and the
|
|
143
|
+
* full list comes back (with `totalCount` equal to its length).
|
|
144
|
+
*/
|
|
145
|
+
async listGroupMembersPage(groupId, opts = {}) {
|
|
146
|
+
const qs = new URLSearchParams();
|
|
147
|
+
if (opts.appId) qs.set("appId", opts.appId);
|
|
148
|
+
if (opts.search) qs.set("search", opts.search);
|
|
149
|
+
if (opts.limit !== void 0) qs.set("limit", String(opts.limit));
|
|
150
|
+
if (opts.offset !== void 0) qs.set("offset", String(opts.offset));
|
|
151
|
+
const suffix = qs.size ? `?${qs}` : "";
|
|
96
152
|
const data = await this.request(
|
|
97
153
|
"GET",
|
|
98
154
|
`/api/auth/user-groups/${encodeURIComponent(groupId)}/members${suffix}`,
|
|
99
155
|
{ auth: "key" }
|
|
100
156
|
);
|
|
101
|
-
|
|
157
|
+
const members = data.members ?? [];
|
|
158
|
+
return { members, totalCount: data.totalCount ?? members.length };
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The app's PENDING invites — people invited but not yet accepted. Session-authed.
|
|
162
|
+
*
|
|
163
|
+
* An invitee is not a membership, so they appear in no member list; without this a successful
|
|
164
|
+
* invite looks like nothing happened until the person accepts. Pass `userGroupId` to scope to one
|
|
165
|
+
* workspace. Accounts excludes consumed and expired invites.
|
|
166
|
+
*/
|
|
167
|
+
async listAppInvites(appSlug, opts = {}) {
|
|
168
|
+
const qs = new URLSearchParams();
|
|
169
|
+
if (opts.userGroupId) qs.set("userGroupId", opts.userGroupId);
|
|
170
|
+
if (opts.search) qs.set("search", opts.search);
|
|
171
|
+
if (opts.limit !== void 0) qs.set("limit", String(opts.limit));
|
|
172
|
+
if (opts.offset !== void 0) qs.set("offset", String(opts.offset));
|
|
173
|
+
const suffix = qs.size ? `?${qs}` : "";
|
|
174
|
+
const data = await this.request(
|
|
175
|
+
"GET",
|
|
176
|
+
`/api/auth/apps/${encodeURIComponent(appSlug)}/invites${suffix}`,
|
|
177
|
+
{ auth: "session" }
|
|
178
|
+
);
|
|
179
|
+
const invites = data.invites ?? [];
|
|
180
|
+
return { invites, totalCount: data.totalCount ?? invites.length };
|
|
102
181
|
}
|
|
103
182
|
// ── App-scoped profile (availability / sound / any per-app preference) ───────
|
|
104
183
|
/** Read a user's app-scoped `profileData` (the store for per-app preferences). Session-authed. */
|
package/package.json
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
2
|
+
"name": "@~lyre/auth",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Shared Axis Accounts auth SDK \u2014 framework-agnostic session/identity core (HMAC-signed cookies, accounts login/callback/logout) plus an optional turnkey SvelteKit adapter.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Britam Trust Services / Lyre",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/kigathi-chege/lyre-auth-sdk.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/kigathi-chege/lyre-auth-sdk#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/kigathi-chege/lyre-auth-sdk/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"auth",
|
|
19
|
+
"sso",
|
|
20
|
+
"accounts",
|
|
21
|
+
"session",
|
|
22
|
+
"sveltekit",
|
|
23
|
+
"hmac",
|
|
24
|
+
"lyre"
|
|
25
|
+
],
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./sveltekit": {
|
|
34
|
+
"types": "./dist/sveltekit.d.ts",
|
|
35
|
+
"import": "./dist/sveltekit.js"
|
|
36
|
+
},
|
|
37
|
+
"./nest": {
|
|
38
|
+
"types": "./dist/nest.d.ts",
|
|
39
|
+
"import": "./dist/nest.js"
|
|
40
|
+
},
|
|
41
|
+
"./admin": {
|
|
42
|
+
"types": "./dist/admin.d.ts",
|
|
43
|
+
"import": "./dist/admin.js"
|
|
44
|
+
},
|
|
45
|
+
"./package.json": "./package.json"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup src/index.ts src/sveltekit.ts src/nest.ts src/admin.ts --format esm --dts --clean",
|
|
54
|
+
"check": "tsc --noEmit"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@sveltejs/kit": "^2.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"@sveltejs/kit": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@sveltejs/kit": "^2.0.0",
|
|
66
|
+
"@types/node": "^22",
|
|
67
|
+
"tsup": "^8.3.5",
|
|
68
|
+
"typescript": "^6.0.2"
|
|
69
|
+
},
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public"
|
|
72
|
+
}
|
|
73
73
|
}
|