@assemora/auth 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 +202 -0
- package/README.md +69 -0
- package/dist/actors.d.ts +20 -0
- package/dist/actors.d.ts.map +1 -0
- package/dist/actors.js +37 -0
- package/dist/actors.js.map +1 -0
- package/dist/authorization.d.ts +36 -0
- package/dist/authorization.d.ts.map +1 -0
- package/dist/authorization.js +93 -0
- package/dist/authorization.js.map +1 -0
- package/dist/commands.d.ts +232 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +390 -0
- package/dist/commands.js.map +1 -0
- package/dist/credentials.d.ts +16 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +43 -0
- package/dist/credentials.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +136 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +92 -0
- package/dist/models.js.map +1 -0
- package/dist/module.d.ts +39 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +95 -0
- package/dist/module.js.map +1 -0
- package/dist/ownership.d.ts +67 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/ownership.js +51 -0
- package/dist/ownership.js.map +1 -0
- package/dist/permissions.d.ts +40 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +86 -0
- package/dist/permissions.js.map +1 -0
- package/dist/policies.d.ts +120 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/policies.js +92 -0
- package/dist/policies.js.map +1 -0
- package/dist/queries.d.ts +166 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +236 -0
- package/dist/queries.js.map +1 -0
- package/dist/sessions.d.ts +40 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +39 -0
- package/dist/sessions.js.map +1 -0
- package/dist/tokens.d.ts +40 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +60 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +42 -0
package/dist/queries.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading who exists and what they may do (SPEC.md §15, §50).
|
|
3
|
+
*
|
|
4
|
+
* Administration screens need to see users, roles, permissions, tokens and agents,
|
|
5
|
+
* and every one of those reads travels the Query Bus — so it is validated, it is
|
|
6
|
+
* authorized, and no layer above this one has to depend on it (ADR-0014).
|
|
7
|
+
*
|
|
8
|
+
* Nothing sensitive is projected. A password hash and a token digest exist in this
|
|
9
|
+
* package and stop here (SPEC.md §85).
|
|
10
|
+
*/
|
|
11
|
+
import { NotFoundError, query } from '@assemora/core';
|
|
12
|
+
import { array, boolean, email as emailSchema, number, object, string, timestamp, uuid, } from '@assemora/schema';
|
|
13
|
+
import { Agent, ApiToken, Permission, Role, RolePermission, User, UserRole } from './models.js';
|
|
14
|
+
import { permissionsOf } from './permissions.js';
|
|
15
|
+
const paging = {
|
|
16
|
+
page: number().integer().optional(),
|
|
17
|
+
perPage: number().integer().optional(),
|
|
18
|
+
};
|
|
19
|
+
/** What every listing answers beside its rows: the page it is, and how many there are. */
|
|
20
|
+
const paged = {
|
|
21
|
+
total: number().integer(),
|
|
22
|
+
page: number().integer(),
|
|
23
|
+
perPage: number().integer(),
|
|
24
|
+
lastPage: number().integer(),
|
|
25
|
+
};
|
|
26
|
+
const limit = (perPage) => Math.min(perPage ?? 20, 100);
|
|
27
|
+
/** The role names a set of users hold, in one query rather than one per user. */
|
|
28
|
+
const rolesByUser = async (userIds) => {
|
|
29
|
+
const byUser = new Map();
|
|
30
|
+
if (userIds.length === 0)
|
|
31
|
+
return byUser;
|
|
32
|
+
const links = await UserRole.whereIn('userId', [...userIds]);
|
|
33
|
+
if (links.length === 0)
|
|
34
|
+
return byUser;
|
|
35
|
+
const roles = await Role.whereIn('id', [...new Set(links.map((link) => link.roleId))]);
|
|
36
|
+
const nameById = new Map(roles.map((role) => [role.id, role.name]));
|
|
37
|
+
for (const link of links) {
|
|
38
|
+
const name = nameById.get(link.roleId);
|
|
39
|
+
if (name === undefined)
|
|
40
|
+
continue;
|
|
41
|
+
byUser.set(link.userId, [...(byUser.get(link.userId) ?? []), name]);
|
|
42
|
+
}
|
|
43
|
+
return byUser;
|
|
44
|
+
};
|
|
45
|
+
export const ListUsers = query('auth.users.list', {
|
|
46
|
+
description: 'A page of users, with the roles they hold',
|
|
47
|
+
input: { search: string().optional(), active: boolean().optional(), ...paging },
|
|
48
|
+
output: {
|
|
49
|
+
data: array(object({
|
|
50
|
+
id: uuid(),
|
|
51
|
+
email: emailSchema(),
|
|
52
|
+
name: string(),
|
|
53
|
+
active: boolean(),
|
|
54
|
+
roles: array(string()),
|
|
55
|
+
version: number().integer(),
|
|
56
|
+
createdAt: timestamp(),
|
|
57
|
+
})),
|
|
58
|
+
...paged,
|
|
59
|
+
},
|
|
60
|
+
handle: async ({ search, active, page, perPage }) => {
|
|
61
|
+
let found = User.orderBy('createdAt', 'desc');
|
|
62
|
+
if (active !== undefined)
|
|
63
|
+
found = found.where('active', active);
|
|
64
|
+
if (search !== undefined && search !== '') {
|
|
65
|
+
found = found.where((builder) => builder.whereLike('name', `%${search}%`).orWhere('email', 'like', `%${search}%`));
|
|
66
|
+
}
|
|
67
|
+
const listed = await found.paginate(page ?? 1, limit(perPage));
|
|
68
|
+
const roles = await rolesByUser(listed.data.map((user) => user.id));
|
|
69
|
+
return {
|
|
70
|
+
...listed,
|
|
71
|
+
data: listed.data.map((user) => ({
|
|
72
|
+
id: user.id,
|
|
73
|
+
email: user.email,
|
|
74
|
+
name: user.name,
|
|
75
|
+
active: user.active,
|
|
76
|
+
roles: roles.get(user.id) ?? [],
|
|
77
|
+
/** Echoed back as `expectedVersion` by any edit (SPEC.md §66). */
|
|
78
|
+
version: user.version,
|
|
79
|
+
createdAt: user.createdAt,
|
|
80
|
+
})),
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
export const GetUser = query('auth.users.get', {
|
|
85
|
+
description: 'One user, with their roles and everything those roles allow',
|
|
86
|
+
input: { id: uuid() },
|
|
87
|
+
output: {
|
|
88
|
+
id: uuid(),
|
|
89
|
+
email: emailSchema(),
|
|
90
|
+
name: string(),
|
|
91
|
+
active: boolean(),
|
|
92
|
+
roles: array(string()),
|
|
93
|
+
permissions: array(string()),
|
|
94
|
+
version: number().integer(),
|
|
95
|
+
createdAt: timestamp(),
|
|
96
|
+
updatedAt: timestamp(),
|
|
97
|
+
},
|
|
98
|
+
handle: async ({ id }) => {
|
|
99
|
+
const user = await User.find(id);
|
|
100
|
+
if (user === null)
|
|
101
|
+
throw new NotFoundError('user', id);
|
|
102
|
+
const roles = await rolesByUser([user.id]);
|
|
103
|
+
return {
|
|
104
|
+
id: user.id,
|
|
105
|
+
email: user.email,
|
|
106
|
+
name: user.name,
|
|
107
|
+
active: user.active,
|
|
108
|
+
roles: roles.get(user.id) ?? [],
|
|
109
|
+
permissions: [...(await permissionsOf({ type: 'user', id: user.id }))],
|
|
110
|
+
version: user.version,
|
|
111
|
+
createdAt: user.createdAt,
|
|
112
|
+
updatedAt: user.updatedAt,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
export const ListRoles = query('auth.roles.list', {
|
|
117
|
+
description: 'Every role, with the permissions it carries',
|
|
118
|
+
input: {},
|
|
119
|
+
output: {
|
|
120
|
+
data: array(object({
|
|
121
|
+
id: uuid(),
|
|
122
|
+
name: string(),
|
|
123
|
+
label: string(),
|
|
124
|
+
version: number().integer(),
|
|
125
|
+
permissions: array(string()),
|
|
126
|
+
})),
|
|
127
|
+
},
|
|
128
|
+
handle: async () => {
|
|
129
|
+
const roles = await Role.orderBy('name', 'asc').get();
|
|
130
|
+
const links = await RolePermission.whereIn('roleId', roles.map((role) => role.id));
|
|
131
|
+
const permissions = await Permission.whereIn('id', [
|
|
132
|
+
...new Set(links.map((link) => link.permissionId)),
|
|
133
|
+
]);
|
|
134
|
+
const nameById = new Map(permissions.map((permission) => [permission.id, permission.name]));
|
|
135
|
+
return {
|
|
136
|
+
data: roles.map((role) => ({
|
|
137
|
+
id: role.id,
|
|
138
|
+
name: role.name,
|
|
139
|
+
label: role.label,
|
|
140
|
+
version: role.version,
|
|
141
|
+
permissions: links
|
|
142
|
+
.filter((link) => link.roleId === role.id)
|
|
143
|
+
.map((link) => nameById.get(link.permissionId))
|
|
144
|
+
.filter((name) => name !== undefined)
|
|
145
|
+
.sort(),
|
|
146
|
+
})),
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
export const ListPermissions = query('auth.permissions.list', {
|
|
151
|
+
description: 'Every permission that has been recorded',
|
|
152
|
+
input: {},
|
|
153
|
+
output: {
|
|
154
|
+
data: array(object({ id: uuid(), name: string(), description: string().nullable() })),
|
|
155
|
+
},
|
|
156
|
+
handle: async () => ({
|
|
157
|
+
data: (await Permission.orderBy('name', 'asc').get()).map((permission) => ({
|
|
158
|
+
id: permission.id,
|
|
159
|
+
name: permission.name,
|
|
160
|
+
description: permission.description,
|
|
161
|
+
})),
|
|
162
|
+
}),
|
|
163
|
+
});
|
|
164
|
+
export const ListApiTokens = query('auth.tokens.list', {
|
|
165
|
+
description: 'Which API tokens exist. Never what they are',
|
|
166
|
+
input: { userId: uuid().optional(), ...paging },
|
|
167
|
+
output: {
|
|
168
|
+
data: array(object({
|
|
169
|
+
id: uuid(),
|
|
170
|
+
name: string(),
|
|
171
|
+
userId: uuid().nullable(),
|
|
172
|
+
/** As the column declares it: `array()` infers a mutable array, and the row's is not. */
|
|
173
|
+
permissions: array(string()),
|
|
174
|
+
expiresAt: timestamp().nullable(),
|
|
175
|
+
lastUsedAt: timestamp().nullable(),
|
|
176
|
+
createdAt: timestamp(),
|
|
177
|
+
})),
|
|
178
|
+
...paged,
|
|
179
|
+
},
|
|
180
|
+
handle: async ({ userId, page, perPage }) => {
|
|
181
|
+
let found = ApiToken.orderBy('createdAt', 'desc');
|
|
182
|
+
if (userId !== undefined)
|
|
183
|
+
found = found.where('userId', userId);
|
|
184
|
+
const listed = await found.paginate(page ?? 1, limit(perPage));
|
|
185
|
+
return {
|
|
186
|
+
...listed,
|
|
187
|
+
data: listed.data.map((token) => ({
|
|
188
|
+
id: token.id,
|
|
189
|
+
name: token.name,
|
|
190
|
+
userId: token.userId,
|
|
191
|
+
permissions: token.permissions,
|
|
192
|
+
expiresAt: token.expiresAt,
|
|
193
|
+
lastUsedAt: token.lastUsedAt,
|
|
194
|
+
createdAt: token.createdAt,
|
|
195
|
+
})),
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
export const ListAgents = query('auth.agents.list', {
|
|
200
|
+
description: 'The agent identities this application knows (SPEC.md §72)',
|
|
201
|
+
input: paging,
|
|
202
|
+
output: {
|
|
203
|
+
data: array(object({
|
|
204
|
+
id: uuid(),
|
|
205
|
+
name: string(),
|
|
206
|
+
description: string().nullable(),
|
|
207
|
+
permissions: array(string()),
|
|
208
|
+
enabled: boolean(),
|
|
209
|
+
createdAt: timestamp(),
|
|
210
|
+
})),
|
|
211
|
+
...paged,
|
|
212
|
+
},
|
|
213
|
+
handle: async ({ page, perPage }) => {
|
|
214
|
+
const listed = await Agent.orderBy('name', 'asc').paginate(page ?? 1, limit(perPage));
|
|
215
|
+
return {
|
|
216
|
+
...listed,
|
|
217
|
+
data: listed.data.map((agent) => ({
|
|
218
|
+
id: agent.id,
|
|
219
|
+
name: agent.name,
|
|
220
|
+
description: agent.description,
|
|
221
|
+
permissions: agent.permissions,
|
|
222
|
+
enabled: agent.enabled,
|
|
223
|
+
createdAt: agent.createdAt,
|
|
224
|
+
})),
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
export const authQueries = [
|
|
229
|
+
ListUsers,
|
|
230
|
+
GetUser,
|
|
231
|
+
ListRoles,
|
|
232
|
+
ListPermissions,
|
|
233
|
+
ListApiTokens,
|
|
234
|
+
ListAgents,
|
|
235
|
+
];
|
|
236
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio sessions (SPEC.md §49).
|
|
3
|
+
*
|
|
4
|
+
* The session lives on the server; the browser holds only a secret, which the HTTP
|
|
5
|
+
* layer keeps in an `httpOnly`, `Secure`, `SameSite` cookie. What is stored here is a
|
|
6
|
+
* digest of that secret, so a leaked database does not hand anyone a live session.
|
|
7
|
+
*/
|
|
8
|
+
import type { Actor } from '@assemora/core';
|
|
9
|
+
export declare const SESSION_PREFIX = "ses";
|
|
10
|
+
/** Two weeks, the usual compromise between a nuisance and a risk. */
|
|
11
|
+
export declare const DEFAULT_SESSION_TTL_MS: number;
|
|
12
|
+
export type StartedSession = {
|
|
13
|
+
/** Goes into the cookie. Never stored, never logged. */
|
|
14
|
+
readonly token: string;
|
|
15
|
+
readonly expiresAt: Date;
|
|
16
|
+
readonly sessionId: string;
|
|
17
|
+
};
|
|
18
|
+
export type SessionDetails = {
|
|
19
|
+
readonly userAgent?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Where the sign-in came from, when the host genuinely knows.
|
|
22
|
+
*
|
|
23
|
+
* `auth.login` does not fill it, and deliberately does not accept one either: the
|
|
24
|
+
* process cannot know the client's address on its own. Behind any proxy the socket
|
|
25
|
+
* peer is the proxy, and believing `X-Forwarded-For` without a configured chain of
|
|
26
|
+
* trusted hops is a value the client chose. Closing that means a trusted-proxy
|
|
27
|
+
* option on `createHttpServer` and an `ipAddress` on the context; until then an
|
|
28
|
+
* empty column is the truthful answer, and this stays for a host that has already
|
|
29
|
+
* resolved the address by other means (SPEC.md §85).
|
|
30
|
+
*/
|
|
31
|
+
readonly ipAddress?: string;
|
|
32
|
+
readonly ttlMs?: number;
|
|
33
|
+
};
|
|
34
|
+
export declare const startSession: (userId: string, details?: SessionDetails) => Promise<StartedSession>;
|
|
35
|
+
/** The actor behind a session secret, or nobody. */
|
|
36
|
+
export declare const sessionActor: (token: string) => Promise<Actor | undefined>;
|
|
37
|
+
export declare const endSession: (token: string) => Promise<void>;
|
|
38
|
+
/** Removes what has expired. Called on a schedule, never on the request path. */
|
|
39
|
+
export declare const purgeExpiredSessions: () => Promise<number>;
|
|
40
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAK3C,eAAO,MAAM,cAAc,QAAQ,CAAA;AAEnC,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,QAA2B,CAAA;AAE9D,MAAM,MAAM,cAAc,GAAG;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,YAAY,WACf,MAAM,YACL,cAAc,KACtB,OAAO,CAAC,cAAc,CAaxB,CAAA;AAED,oDAAoD;AACpD,eAAO,MAAM,YAAY,UAAiB,MAAM,KAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAU3E,CAAA;AAED,eAAO,MAAM,UAAU,UAAiB,MAAM,KAAG,OAAO,CAAC,IAAI,CAI5D,CAAA;AAED,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,MAAM,CAM3D,CAAA"}
|
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { hashToken, issueToken } from './credentials.js';
|
|
2
|
+
import { Session, User } from './models.js';
|
|
3
|
+
export const SESSION_PREFIX = 'ses';
|
|
4
|
+
/** Two weeks, the usual compromise between a nuisance and a risk. */
|
|
5
|
+
export const DEFAULT_SESSION_TTL_MS = 14 * 24 * 60 * 60 * 1000;
|
|
6
|
+
export const startSession = async (userId, details = {}) => {
|
|
7
|
+
const issued = issueToken(SESSION_PREFIX);
|
|
8
|
+
const expiresAt = new Date(Date.now() + (details.ttlMs ?? DEFAULT_SESSION_TTL_MS));
|
|
9
|
+
const session = await Session.create({
|
|
10
|
+
tokenHash: issued.hash,
|
|
11
|
+
userId,
|
|
12
|
+
userAgent: details.userAgent ?? null,
|
|
13
|
+
ipAddress: details.ipAddress ?? null,
|
|
14
|
+
expiresAt,
|
|
15
|
+
});
|
|
16
|
+
return { token: issued.token, expiresAt, sessionId: session.id };
|
|
17
|
+
};
|
|
18
|
+
/** The actor behind a session secret, or nobody. */
|
|
19
|
+
export const sessionActor = async (token) => {
|
|
20
|
+
const session = await Session.where('tokenHash', hashToken(token)).first();
|
|
21
|
+
if (session === null || session.expiresAt.getTime() <= Date.now())
|
|
22
|
+
return undefined;
|
|
23
|
+
const user = await User.find(session.userId);
|
|
24
|
+
if (user === null || !user.active)
|
|
25
|
+
return undefined;
|
|
26
|
+
return { type: 'user', id: user.id };
|
|
27
|
+
};
|
|
28
|
+
export const endSession = async (token) => {
|
|
29
|
+
const session = await Session.where('tokenHash', hashToken(token)).first();
|
|
30
|
+
await session?.delete();
|
|
31
|
+
};
|
|
32
|
+
/** Removes what has expired. Called on a schedule, never on the request path. */
|
|
33
|
+
export const purgeExpiredSessions = async () => {
|
|
34
|
+
const expired = await Session.where('expiresAt', '<=', new Date()).get();
|
|
35
|
+
for (const session of expired)
|
|
36
|
+
await session.delete();
|
|
37
|
+
return expired.length;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAA;AAEnC,qEAAqE;AACrE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AA0B9D,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,MAAc,EACd,OAAO,GAAmB,EAAE,EACH,EAAE;IAC3B,MAAM,MAAM,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAC,CAAA;IAElF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,IAAI;QACtB,MAAM;QACN,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;QACpC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;QACpC,SAAS;KACV,CAAC,CAAA;IAEF,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAA;AAClE,CAAC,CAAA;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,KAAa,EAA8B,EAAE;IAC9E,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAA;IAE1E,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,SAAS,CAAA;IAEnF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAE5C,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAEnD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAA;AACtC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,KAAa,EAAiB,EAAE;IAC/D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAA;IAE1E,MAAM,OAAO,EAAE,MAAM,EAAE,CAAA;AACzB,CAAC,CAAA;AAED,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,IAAqB,EAAE;IAC9D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,CAAA;IAExE,KAAK,MAAM,OAAO,IAAI,OAAO;QAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAA;IAErD,OAAO,OAAO,CAAC,MAAM,CAAA;AACvB,CAAC,CAAA"}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API tokens and agent tokens (SPEC.md §49, §72).
|
|
3
|
+
*
|
|
4
|
+
* A token is shown once, at creation, and stored only as a digest. Nothing in the
|
|
5
|
+
* system can recover it, which is the point: a database dump is not a set of keys.
|
|
6
|
+
*/
|
|
7
|
+
import type { Actor } from '@assemora/core';
|
|
8
|
+
export declare const API_TOKEN_PREFIX = "ast";
|
|
9
|
+
export declare const AGENT_TOKEN_PREFIX = "agt";
|
|
10
|
+
export type IssuedApiToken = {
|
|
11
|
+
/** Shown once. Store it somewhere safe or issue another. */
|
|
12
|
+
readonly token: string;
|
|
13
|
+
readonly id: string;
|
|
14
|
+
};
|
|
15
|
+
export type ApiTokenDetails = {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly permissions: readonly string[];
|
|
18
|
+
readonly userId?: string;
|
|
19
|
+
readonly expiresAt?: Date;
|
|
20
|
+
};
|
|
21
|
+
export declare const createApiToken: (details: ApiTokenDetails) => Promise<IssuedApiToken>;
|
|
22
|
+
export type AgentDetails = {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly description?: string;
|
|
25
|
+
readonly permissions: readonly string[];
|
|
26
|
+
};
|
|
27
|
+
export type CreatedAgent = {
|
|
28
|
+
readonly agentId: string;
|
|
29
|
+
readonly token: string;
|
|
30
|
+
readonly tokenId: string;
|
|
31
|
+
};
|
|
32
|
+
/** An agent is an actor of its own, with a token of its own (SPEC.md §72). */
|
|
33
|
+
export declare const createAgent: (details: AgentDetails) => Promise<CreatedAgent>;
|
|
34
|
+
/** A credential with no expiry, or one whose expiry has not arrived. */
|
|
35
|
+
export declare const isLive: (expiresAt: Date | null) => boolean;
|
|
36
|
+
/** The actor a bearer token stands for, or nobody. */
|
|
37
|
+
export declare const tokenActor: (token: string) => Promise<Actor | undefined>;
|
|
38
|
+
export declare const revokeApiToken: (id: string) => Promise<void>;
|
|
39
|
+
export declare const revokeAgentToken: (id: string) => Promise<void>;
|
|
40
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAK3C,eAAO,MAAM,gBAAgB,QAAQ,CAAA;AACrC,eAAO,MAAM,kBAAkB,QAAQ,CAAA;AAEvC,MAAM,MAAM,cAAc,GAAG;IAC3B,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAA;CAC1B,CAAA;AAED,eAAO,MAAM,cAAc,YAAmB,eAAe,KAAG,OAAO,CAAC,cAAc,CAYrF,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;CACxC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,WAAW,YAAmB,YAAY,KAAG,OAAO,CAAC,YAAY,CAkB7E,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,MAAM,cAAe,IAAI,GAAG,IAAI,KAAG,OACQ,CAAA;AAExD,sDAAsD;AACtD,eAAO,MAAM,UAAU,UAAiB,MAAM,KAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAwBzE,CAAA;AAED,eAAO,MAAM,cAAc,OAAc,MAAM,KAAG,OAAO,CAAC,IAAI,CAE7D,CAAA;AAED,eAAO,MAAM,gBAAgB,OAAc,MAAM,KAAG,OAAO,CAAC,IAAI,CAE/D,CAAA"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { hashToken, issueToken } from './credentials.js';
|
|
2
|
+
import { Agent, AgentToken, ApiToken } from './models.js';
|
|
3
|
+
export const API_TOKEN_PREFIX = 'ast';
|
|
4
|
+
export const AGENT_TOKEN_PREFIX = 'agt';
|
|
5
|
+
export const createApiToken = async (details) => {
|
|
6
|
+
const issued = issueToken(API_TOKEN_PREFIX);
|
|
7
|
+
const stored = await ApiToken.create({
|
|
8
|
+
name: details.name,
|
|
9
|
+
tokenHash: issued.hash,
|
|
10
|
+
userId: details.userId ?? null,
|
|
11
|
+
permissions: details.permissions,
|
|
12
|
+
expiresAt: details.expiresAt ?? null,
|
|
13
|
+
});
|
|
14
|
+
return { token: issued.token, id: stored.id };
|
|
15
|
+
};
|
|
16
|
+
/** An agent is an actor of its own, with a token of its own (SPEC.md §72). */
|
|
17
|
+
export const createAgent = async (details) => {
|
|
18
|
+
const agent = await Agent.create({
|
|
19
|
+
name: details.name,
|
|
20
|
+
description: details.description ?? null,
|
|
21
|
+
permissions: details.permissions,
|
|
22
|
+
enabled: true,
|
|
23
|
+
});
|
|
24
|
+
const issued = issueToken(AGENT_TOKEN_PREFIX);
|
|
25
|
+
const token = await AgentToken.create({
|
|
26
|
+
agentId: agent.id,
|
|
27
|
+
name: `${details.name} default`,
|
|
28
|
+
tokenHash: issued.hash,
|
|
29
|
+
expiresAt: null,
|
|
30
|
+
});
|
|
31
|
+
return { agentId: agent.id, token: issued.token, tokenId: token.id };
|
|
32
|
+
};
|
|
33
|
+
/** A credential with no expiry, or one whose expiry has not arrived. */
|
|
34
|
+
export const isLive = (expiresAt) => expiresAt === null || expiresAt.getTime() > Date.now();
|
|
35
|
+
/** The actor a bearer token stands for, or nobody. */
|
|
36
|
+
export const tokenActor = async (token) => {
|
|
37
|
+
const digest = hashToken(token);
|
|
38
|
+
if (token.startsWith(`${AGENT_TOKEN_PREFIX}_`)) {
|
|
39
|
+
const stored = await AgentToken.where('tokenHash', digest).first();
|
|
40
|
+
if (stored === null || !isLive(stored.expiresAt))
|
|
41
|
+
return undefined;
|
|
42
|
+
const agent = await Agent.find(stored.agentId);
|
|
43
|
+
if (agent === null || !agent.enabled)
|
|
44
|
+
return undefined;
|
|
45
|
+
await stored.update({ lastUsedAt: new Date() });
|
|
46
|
+
return { type: 'agent', id: agent.id };
|
|
47
|
+
}
|
|
48
|
+
const stored = await ApiToken.where('tokenHash', digest).first();
|
|
49
|
+
if (stored === null || !isLive(stored.expiresAt))
|
|
50
|
+
return undefined;
|
|
51
|
+
await stored.update({ lastUsedAt: new Date() });
|
|
52
|
+
return { type: 'api', id: stored.id };
|
|
53
|
+
};
|
|
54
|
+
export const revokeApiToken = async (id) => {
|
|
55
|
+
await (await ApiToken.find(id))?.delete();
|
|
56
|
+
};
|
|
57
|
+
export const revokeAgentToken = async (id) => {
|
|
58
|
+
await (await AgentToken.find(id))?.delete();
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@assemora/auth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Users, sessions, roles, permissions, policies, tokens",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/assemora/assemora.git",
|
|
12
|
+
"directory": "packages/auth"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"!dist/.tsbuildinfo"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@node-rs/argon2": "2.1.0",
|
|
29
|
+
"@assemora/schema": "0.1.0",
|
|
30
|
+
"@assemora/core": "0.1.0",
|
|
31
|
+
"@assemora/data": "0.1.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "24.13.3",
|
|
35
|
+
"@assemora/database": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -b tsconfig.build.json",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
40
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
41
|
+
}
|
|
42
|
+
}
|