@boopkit/cap-users 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/capability.json +19 -0
- package/dist/auth-middleware.d.ts +96 -2
- package/dist/auth-middleware.d.ts.map +1 -1
- package/dist/auth-middleware.js +131 -63
- package/dist/auth-middleware.js.map +1 -1
- package/dist/auth-service.d.ts +8 -0
- package/dist/auth-service.d.ts.map +1 -1
- package/dist/auth-service.js +93 -46
- package/dist/auth-service.js.map +1 -1
- package/dist/credential-migration.d.ts +76 -0
- package/dist/credential-migration.d.ts.map +1 -0
- package/dist/credential-migration.js +126 -0
- package/dist/credential-migration.js.map +1 -0
- package/dist/identity-service.d.ts +49 -0
- package/dist/identity-service.d.ts.map +1 -1
- package/dist/identity-service.js +34 -0
- package/dist/identity-service.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/info-schema-tags.d.ts +21 -0
- package/dist/info-schema-tags.d.ts.map +1 -0
- package/dist/info-schema-tags.js +24 -0
- package/dist/info-schema-tags.js.map +1 -0
- package/dist/team-invitation-service.d.ts.map +1 -1
- package/dist/team-invitation-service.js +42 -8
- package/dist/team-invitation-service.js.map +1 -1
- package/package.json +1 -1
- package/scaffold/reference/user-auth.ref.md +48 -4
- package/scaffold/src/scripts/migrate-credentials.ts +69 -0
- package/scaffold/src/types/express.ts +11 -1
|
@@ -9,12 +9,57 @@ export interface IdentityServiceOptions {
|
|
|
9
9
|
export interface IdentityQueryOptions {
|
|
10
10
|
includeEnded?: boolean;
|
|
11
11
|
}
|
|
12
|
+
/** Rows per page when a key-prefix scan is not told otherwise. */
|
|
13
|
+
export declare const IDENTITY_PREFIX_PAGE_SIZE = 100;
|
|
14
|
+
/** The most a single page of a key-prefix scan will return, however asked. */
|
|
15
|
+
export declare const IDENTITY_PREFIX_MAX_PAGE_SIZE = 500;
|
|
16
|
+
/** Options for the key-prefix scan. */
|
|
17
|
+
export interface IdentityPrefixQueryOptions extends IdentityQueryOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Rows in this page. Defaults to `IDENTITY_PREFIX_PAGE_SIZE` and is clamped
|
|
20
|
+
* to `IDENTITY_PREFIX_MAX_PAGE_SIZE`, so a caller cannot turn the scan into
|
|
21
|
+
* the whole-table read it exists to avoid.
|
|
22
|
+
*/
|
|
23
|
+
limit?: number;
|
|
24
|
+
/** Exclusive cursor — the previous page's `nextCursor`. */
|
|
25
|
+
after?: number;
|
|
26
|
+
}
|
|
27
|
+
/** One page of a key-prefix scan. */
|
|
28
|
+
export interface IdentityPrefixPage {
|
|
29
|
+
identities: UserIdentity[];
|
|
30
|
+
/**
|
|
31
|
+
* Feed back as `after` to continue. `null` when this page ended the scan.
|
|
32
|
+
*
|
|
33
|
+
* A full page always answers with a cursor, even when it happened to be the
|
|
34
|
+
* last one, so the caller's final request is an empty page rather than a
|
|
35
|
+
* count the query cannot know without a second read.
|
|
36
|
+
*/
|
|
37
|
+
nextCursor: number | null;
|
|
38
|
+
}
|
|
12
39
|
/** The service object returned by createIdentityService. */
|
|
13
40
|
export interface IdentityService {
|
|
14
41
|
create(userId: number, domain: string, key: string): Promise<UserIdentity>;
|
|
15
42
|
findByDomainAndKey(domain: string, key: string): Promise<UserIdentity | null>;
|
|
16
43
|
findByUserAndDomain(userId: number, domain: string): Promise<UserIdentity | null>;
|
|
17
44
|
findAllByUser(userId: number, opts?: IdentityQueryOptions): Promise<UserIdentity[]>;
|
|
45
|
+
/**
|
|
46
|
+
* One page of the rows in `domain` whose key starts with `keyPrefix`, in id
|
|
47
|
+
* order, for any user.
|
|
48
|
+
*
|
|
49
|
+
* This is the only read here that is not bounded by a user id, and it exists
|
|
50
|
+
* for one job: a credential domain whose keys are `"<issuer>|<secret hash>"`
|
|
51
|
+
* — cap-oauth-server's token rows — needs to enumerate everybody one issuer
|
|
52
|
+
* ever reached, so that a compromised client can be retired without the
|
|
53
|
+
* operator already knowing which accounts to sweep.
|
|
54
|
+
*
|
|
55
|
+
* Both narrowings are load-bearing rather than conveniences. The **domain is
|
|
56
|
+
* required** so the scan cannot wander into `email` or `api-key` rows, and an
|
|
57
|
+
* **empty prefix is refused** because it would walk a whole domain — which is
|
|
58
|
+
* the read this method is shaped to make unnecessary. Left-anchored, the
|
|
59
|
+
* comparison is a seek on the `(domain, key)` unique index rather than a
|
|
60
|
+
* table scan, which is the whole reason the key format puts the issuer first.
|
|
61
|
+
*/
|
|
62
|
+
findAllByDomainAndKeyPrefix(domain: string, keyPrefix: string, opts?: IdentityPrefixQueryOptions): Promise<IdentityPrefixPage>;
|
|
18
63
|
update(domain: string, key: string, newUserId: number): Promise<UserIdentity>;
|
|
19
64
|
endDate(domain: string, key: string): Promise<UserIdentity>;
|
|
20
65
|
}
|
|
@@ -31,6 +76,10 @@ interface IdentityDelegate {
|
|
|
31
76
|
}): Promise<Record<string, unknown> | null>;
|
|
32
77
|
findMany(args: {
|
|
33
78
|
where: Record<string, unknown>;
|
|
79
|
+
orderBy?: {
|
|
80
|
+
id?: 'asc' | 'desc';
|
|
81
|
+
};
|
|
82
|
+
take?: number;
|
|
34
83
|
}): Promise<Record<string, unknown>[]>;
|
|
35
84
|
update(args: {
|
|
36
85
|
where: Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-service.d.ts","sourceRoot":"","sources":["../src/identity-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,kDAAkD;AAClD,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IACzC,SAAS,CAAC,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;CACxC;AAED,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC3E,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9E,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAClF,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACpF,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9E,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7D;AAED,kEAAkE;AAClE,UAAU,gBAAgB;IACxB,MAAM,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClF,UAAU,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9F,SAAS,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7F,QAAQ,CAAC,IAAI,EAAE;
|
|
1
|
+
{"version":3,"file":"identity-service.d.ts","sourceRoot":"","sources":["../src/identity-service.ts"],"names":[],"mappings":"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"}
|
package/dist/identity-service.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IDENTITY_PREFIX_MAX_PAGE_SIZE = exports.IDENTITY_PREFIX_PAGE_SIZE = void 0;
|
|
3
4
|
exports.createIdentityService = createIdentityService;
|
|
4
5
|
const types_js_1 = require("./types.js");
|
|
6
|
+
/** Rows per page when a key-prefix scan is not told otherwise. */
|
|
7
|
+
exports.IDENTITY_PREFIX_PAGE_SIZE = 100;
|
|
8
|
+
/** The most a single page of a key-prefix scan will return, however asked. */
|
|
9
|
+
exports.IDENTITY_PREFIX_MAX_PAGE_SIZE = 500;
|
|
5
10
|
function toIdentity(row) {
|
|
6
11
|
const endedAt = row.endedAt;
|
|
7
12
|
return {
|
|
@@ -75,6 +80,35 @@ function createIdentityService(model, options) {
|
|
|
75
80
|
const rows = await model.findMany({ where });
|
|
76
81
|
return rows.map(toIdentity);
|
|
77
82
|
},
|
|
83
|
+
async findAllByDomainAndKeyPrefix(domain, keyPrefix, opts) {
|
|
84
|
+
if (!domain) {
|
|
85
|
+
throw new TypeError('Unable to scan identities by key prefix: no domain was given. ' +
|
|
86
|
+
'Pass the identity domain to scan, such as "oauth-access".');
|
|
87
|
+
}
|
|
88
|
+
if (!keyPrefix) {
|
|
89
|
+
throw new TypeError(`Unable to scan identities by key prefix: the prefix was empty, which would read every "${domain}" row. ` +
|
|
90
|
+
'Pass the prefix to match, or use findAllByUser for one user\'s identities.');
|
|
91
|
+
}
|
|
92
|
+
const limit = Math.min(Math.max(1, Math.trunc(opts?.limit ?? exports.IDENTITY_PREFIX_PAGE_SIZE)), exports.IDENTITY_PREFIX_MAX_PAGE_SIZE);
|
|
93
|
+
const where = {
|
|
94
|
+
domain,
|
|
95
|
+
key: { startsWith: normalizeKey(domain, keyPrefix) },
|
|
96
|
+
};
|
|
97
|
+
if (!opts?.includeEnded) {
|
|
98
|
+
where.endedAt = null;
|
|
99
|
+
}
|
|
100
|
+
// The cursor is the row id rather than an offset: pages are stable under
|
|
101
|
+
// rows arriving mid-scan, which for a token table is every request.
|
|
102
|
+
if (opts?.after != null) {
|
|
103
|
+
where.id = { gt: opts.after };
|
|
104
|
+
}
|
|
105
|
+
const rows = await model.findMany({ where, orderBy: { id: 'asc' }, take: limit });
|
|
106
|
+
const identities = rows.map(toIdentity);
|
|
107
|
+
return {
|
|
108
|
+
identities,
|
|
109
|
+
nextCursor: identities.length === limit ? identities[identities.length - 1].id : null,
|
|
110
|
+
};
|
|
111
|
+
},
|
|
78
112
|
async update(domain, key, newUserId) {
|
|
79
113
|
const row = await model.update({
|
|
80
114
|
where: domainKeyWhere(domain, normalizeKey(domain, key)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-service.js","sourceRoot":"","sources":["../src/identity-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"identity-service.js","sourceRoot":"","sources":["../src/identity-service.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,13 +9,14 @@ export type { BaseUserInfo, User, UserIdentity, UserProfile, CredentialEntry, Us
|
|
|
9
9
|
export { createUserService } from './user-service.js';
|
|
10
10
|
export type { UserService, UserServiceOptions } from './user-service.js';
|
|
11
11
|
export { createIdentityService } from './identity-service.js';
|
|
12
|
-
export
|
|
12
|
+
export { IDENTITY_PREFIX_PAGE_SIZE, IDENTITY_PREFIX_MAX_PAGE_SIZE, } from './identity-service.js';
|
|
13
|
+
export type { IdentityService, IdentityServiceOptions, IdentityQueryOptions, IdentityPrefixQueryOptions, IdentityPrefixPage, } from './identity-service.js';
|
|
13
14
|
export { createUserPropertyService } from './user-property-service.js';
|
|
14
15
|
export type { UserPropertyService, UserPropertyServiceOptions } from './user-property-service.js';
|
|
15
|
-
export { createAuthService, AuthValidationError, AuthError, generateToken, hashToken } from './auth-service.js';
|
|
16
|
+
export { createAuthService, AuthValidationError, AuthError, generateToken, hashToken, isHashedToken } from './auth-service.js';
|
|
16
17
|
export type { AuthService, AuthServiceDeps, AuthServiceOptions, AddAuthMethodOptions, SignupInput, SignupResult, LoginResult, VerifyEmailResult, } from './auth-service.js';
|
|
17
|
-
export { createAuthMiddleware } from './auth-middleware.js';
|
|
18
|
-
export type { AuthMiddleware, AuthMiddlewareDeps, AuthMiddlewareOptions, AuthLocals, } from './auth-middleware.js';
|
|
18
|
+
export { createAuthMiddleware, createBearerMiddleware } from './auth-middleware.js';
|
|
19
|
+
export type { AuthMiddleware, AuthMiddlewareDeps, AuthMiddlewareOptions, BearerMiddlewareOptions, AuthLocals, } from './auth-middleware.js';
|
|
19
20
|
export { createAccountService } from './account-service.js';
|
|
20
21
|
export type { AccountService, AccountServiceOptions } from './account-service.js';
|
|
21
22
|
export { createAccountPropertyService } from './account-property-service.js';
|
|
@@ -24,4 +25,7 @@ export { createAccountManagementService, AccountManagementError } from './accoun
|
|
|
24
25
|
export type { AccountManagementService, AccountManagementServiceDeps, AccountManagementServiceOptions, ProvisionAccountInput, ProvisionAccountResult, TeamMember, } from './account-management-service.js';
|
|
25
26
|
export { createTeamInvitationService, TeamInvitationError, DEFAULT_INVITATION_TTL_MS, MAX_PENDING_INVITATIONS, MAX_RESENDS, } from './team-invitation-service.js';
|
|
26
27
|
export type { TeamInvitation, TeamInvitationsPropertyInfo, TeamInvitationService, TeamInvitationServiceDeps, TeamInvitationServiceOptions, AccountUserRole, } from './team-invitation-service.js';
|
|
28
|
+
export { AUTHORIZATION_HASHED_INFO_SCHEMA, TEAM_INVITATIONS_HASHED_INFO_SCHEMA, } from './info-schema-tags.js';
|
|
29
|
+
export { migrateCredentialStore, rehashAuthorizationGrants, authorizationStore, rehashTeamInvitations, teamInvitationsStore, } from './credential-migration.js';
|
|
30
|
+
export type { MigrationDelegate, CredentialStore, RehashResult, MigrationReport, } from './credential-migration.js';
|
|
27
31
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAC/E,YAAY,EACV,YAAY,EACZ,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,OAAO,EACP,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAC/E,YAAY,EACV,YAAY,EACZ,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,OAAO,EACP,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EACL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,YAAY,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAElG,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC/H,YAAY,EACV,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACpF,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,GACX,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElF,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7E,YAAY,EAAE,sBAAsB,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAE3G,OAAO,EAAE,8BAA8B,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzG,YAAY,EACV,wBAAwB,EACxB,4BAA4B,EAC5B,+BAA+B,EAC/B,qBAAqB,EACrB,sBAAsB,EACtB,UAAU,GACX,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACvB,WAAW,GACZ,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,cAAc,EACd,2BAA2B,EAC3B,qBAAqB,EACrB,yBAAyB,EACzB,4BAA4B,EAC5B,eAAe,GAChB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,gCAAgC,EAChC,mCAAmC,GACpC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,eAAe,GAChB,MAAM,2BAA2B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* composing on top of @boopkit/cap-services primitives.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.MAX_RESENDS = exports.MAX_PENDING_INVITATIONS = exports.DEFAULT_INVITATION_TTL_MS = exports.TeamInvitationError = exports.createTeamInvitationService = exports.AccountManagementError = exports.createAccountManagementService = exports.createAccountPropertyService = exports.createAccountService = exports.createAuthMiddleware = exports.hashToken = exports.generateToken = exports.AuthError = exports.AuthValidationError = exports.createAuthService = exports.createUserPropertyService = exports.createIdentityService = exports.createUserService = exports.DuplicateAccountUserError = exports.DuplicateIdentityError = void 0;
|
|
9
|
+
exports.teamInvitationsStore = exports.rehashTeamInvitations = exports.authorizationStore = exports.rehashAuthorizationGrants = exports.migrateCredentialStore = exports.TEAM_INVITATIONS_HASHED_INFO_SCHEMA = exports.AUTHORIZATION_HASHED_INFO_SCHEMA = exports.MAX_RESENDS = exports.MAX_PENDING_INVITATIONS = exports.DEFAULT_INVITATION_TTL_MS = exports.TeamInvitationError = exports.createTeamInvitationService = exports.AccountManagementError = exports.createAccountManagementService = exports.createAccountPropertyService = exports.createAccountService = exports.createBearerMiddleware = exports.createAuthMiddleware = exports.isHashedToken = exports.hashToken = exports.generateToken = exports.AuthError = exports.AuthValidationError = exports.createAuthService = exports.createUserPropertyService = exports.IDENTITY_PREFIX_MAX_PAGE_SIZE = exports.IDENTITY_PREFIX_PAGE_SIZE = exports.createIdentityService = exports.createUserService = exports.DuplicateAccountUserError = exports.DuplicateIdentityError = void 0;
|
|
10
10
|
var types_js_1 = require("./types.js");
|
|
11
11
|
Object.defineProperty(exports, "DuplicateIdentityError", { enumerable: true, get: function () { return types_js_1.DuplicateIdentityError; } });
|
|
12
12
|
Object.defineProperty(exports, "DuplicateAccountUserError", { enumerable: true, get: function () { return types_js_1.DuplicateAccountUserError; } });
|
|
@@ -14,6 +14,9 @@ var user_service_js_1 = require("./user-service.js");
|
|
|
14
14
|
Object.defineProperty(exports, "createUserService", { enumerable: true, get: function () { return user_service_js_1.createUserService; } });
|
|
15
15
|
var identity_service_js_1 = require("./identity-service.js");
|
|
16
16
|
Object.defineProperty(exports, "createIdentityService", { enumerable: true, get: function () { return identity_service_js_1.createIdentityService; } });
|
|
17
|
+
var identity_service_js_2 = require("./identity-service.js");
|
|
18
|
+
Object.defineProperty(exports, "IDENTITY_PREFIX_PAGE_SIZE", { enumerable: true, get: function () { return identity_service_js_2.IDENTITY_PREFIX_PAGE_SIZE; } });
|
|
19
|
+
Object.defineProperty(exports, "IDENTITY_PREFIX_MAX_PAGE_SIZE", { enumerable: true, get: function () { return identity_service_js_2.IDENTITY_PREFIX_MAX_PAGE_SIZE; } });
|
|
17
20
|
var user_property_service_js_1 = require("./user-property-service.js");
|
|
18
21
|
Object.defineProperty(exports, "createUserPropertyService", { enumerable: true, get: function () { return user_property_service_js_1.createUserPropertyService; } });
|
|
19
22
|
var auth_service_js_1 = require("./auth-service.js");
|
|
@@ -22,8 +25,10 @@ Object.defineProperty(exports, "AuthValidationError", { enumerable: true, get: f
|
|
|
22
25
|
Object.defineProperty(exports, "AuthError", { enumerable: true, get: function () { return auth_service_js_1.AuthError; } });
|
|
23
26
|
Object.defineProperty(exports, "generateToken", { enumerable: true, get: function () { return auth_service_js_1.generateToken; } });
|
|
24
27
|
Object.defineProperty(exports, "hashToken", { enumerable: true, get: function () { return auth_service_js_1.hashToken; } });
|
|
28
|
+
Object.defineProperty(exports, "isHashedToken", { enumerable: true, get: function () { return auth_service_js_1.isHashedToken; } });
|
|
25
29
|
var auth_middleware_js_1 = require("./auth-middleware.js");
|
|
26
30
|
Object.defineProperty(exports, "createAuthMiddleware", { enumerable: true, get: function () { return auth_middleware_js_1.createAuthMiddleware; } });
|
|
31
|
+
Object.defineProperty(exports, "createBearerMiddleware", { enumerable: true, get: function () { return auth_middleware_js_1.createBearerMiddleware; } });
|
|
27
32
|
var account_service_js_1 = require("./account-service.js");
|
|
28
33
|
Object.defineProperty(exports, "createAccountService", { enumerable: true, get: function () { return account_service_js_1.createAccountService; } });
|
|
29
34
|
var account_property_service_js_1 = require("./account-property-service.js");
|
|
@@ -37,4 +42,13 @@ Object.defineProperty(exports, "TeamInvitationError", { enumerable: true, get: f
|
|
|
37
42
|
Object.defineProperty(exports, "DEFAULT_INVITATION_TTL_MS", { enumerable: true, get: function () { return team_invitation_service_js_1.DEFAULT_INVITATION_TTL_MS; } });
|
|
38
43
|
Object.defineProperty(exports, "MAX_PENDING_INVITATIONS", { enumerable: true, get: function () { return team_invitation_service_js_1.MAX_PENDING_INVITATIONS; } });
|
|
39
44
|
Object.defineProperty(exports, "MAX_RESENDS", { enumerable: true, get: function () { return team_invitation_service_js_1.MAX_RESENDS; } });
|
|
45
|
+
var info_schema_tags_js_1 = require("./info-schema-tags.js");
|
|
46
|
+
Object.defineProperty(exports, "AUTHORIZATION_HASHED_INFO_SCHEMA", { enumerable: true, get: function () { return info_schema_tags_js_1.AUTHORIZATION_HASHED_INFO_SCHEMA; } });
|
|
47
|
+
Object.defineProperty(exports, "TEAM_INVITATIONS_HASHED_INFO_SCHEMA", { enumerable: true, get: function () { return info_schema_tags_js_1.TEAM_INVITATIONS_HASHED_INFO_SCHEMA; } });
|
|
48
|
+
var credential_migration_js_1 = require("./credential-migration.js");
|
|
49
|
+
Object.defineProperty(exports, "migrateCredentialStore", { enumerable: true, get: function () { return credential_migration_js_1.migrateCredentialStore; } });
|
|
50
|
+
Object.defineProperty(exports, "rehashAuthorizationGrants", { enumerable: true, get: function () { return credential_migration_js_1.rehashAuthorizationGrants; } });
|
|
51
|
+
Object.defineProperty(exports, "authorizationStore", { enumerable: true, get: function () { return credential_migration_js_1.authorizationStore; } });
|
|
52
|
+
Object.defineProperty(exports, "rehashTeamInvitations", { enumerable: true, get: function () { return credential_migration_js_1.rehashTeamInvitations; } });
|
|
53
|
+
Object.defineProperty(exports, "teamInvitationsStore", { enumerable: true, get: function () { return credential_migration_js_1.teamInvitationsStore; } });
|
|
40
54
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,uCAA+E;AAAtE,kHAAA,sBAAsB,OAAA;AAAE,qHAAA,yBAAyB,OAAA;AAmB1D,qDAAsD;AAA7C,oHAAA,iBAAiB,OAAA;AAG1B,6DAA8D;AAArD,4HAAA,qBAAqB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,uCAA+E;AAAtE,kHAAA,sBAAsB,OAAA;AAAE,qHAAA,yBAAyB,OAAA;AAmB1D,qDAAsD;AAA7C,oHAAA,iBAAiB,OAAA;AAG1B,6DAA8D;AAArD,4HAAA,qBAAqB,OAAA;AAC9B,6DAG+B;AAF7B,gIAAA,yBAAyB,OAAA;AACzB,oIAAA,6BAA6B,OAAA;AAU/B,uEAAuE;AAA9D,qIAAA,yBAAyB,OAAA;AAGlC,qDAA+H;AAAtH,oHAAA,iBAAiB,OAAA;AAAE,sHAAA,mBAAmB,OAAA;AAAE,4GAAA,SAAS,OAAA;AAAE,gHAAA,aAAa,OAAA;AAAE,4GAAA,SAAS,OAAA;AAAE,gHAAA,aAAa,OAAA;AAYnG,2DAAoF;AAA3E,0HAAA,oBAAoB,OAAA;AAAE,4HAAA,sBAAsB,OAAA;AASrD,2DAA4D;AAAnD,0HAAA,oBAAoB,OAAA;AAG7B,6EAA6E;AAApE,2IAAA,4BAA4B,OAAA;AAGrC,iFAAyG;AAAhG,+IAAA,8BAA8B,OAAA;AAAE,uIAAA,sBAAsB,OAAA;AAU/D,2EAMsC;AALpC,yIAAA,2BAA2B,OAAA;AAC3B,iIAAA,mBAAmB,OAAA;AACnB,uIAAA,yBAAyB,OAAA;AACzB,qIAAA,uBAAuB,OAAA;AACvB,yHAAA,WAAW,OAAA;AAWb,6DAG+B;AAF7B,uIAAA,gCAAgC,OAAA;AAChC,0IAAA,mCAAmC,OAAA;AAGrC,qEAMmC;AALjC,iIAAA,sBAAsB,OAAA;AACtB,oIAAA,yBAAyB,OAAA;AACzB,6HAAA,kBAAkB,OAAA;AAClB,gIAAA,qBAAqB,OAAA;AACrB,+HAAA,oBAAoB,OAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* info_schema tags that mark a property payload as written by the
|
|
3
|
+
* credential-hashing library (boop-j8xc epic).
|
|
4
|
+
*
|
|
5
|
+
* Both property tables carry an `info_schema` column that versions the payload
|
|
6
|
+
* shape. For most properties it stays `''`. The two properties that once stored
|
|
7
|
+
* a raw, redeemable credential get a distinct tag the moment their values are
|
|
8
|
+
* hashed — stamped by the fixed library on every new write and by the migration
|
|
9
|
+
* tool on every row it rewrites (boop-j8xc.2 and boop-j8xc.3).
|
|
10
|
+
*
|
|
11
|
+
* The `boop status` data probe uses these tags as a fast, exact migration
|
|
12
|
+
* signal: a row missing the tag predates the fix and may hold a cleartext
|
|
13
|
+
* credential, so the probe needs no JSON parsing — one column comparison with
|
|
14
|
+
* an early exit. Keep each literal in sync with the matching probe query in
|
|
15
|
+
* `capability.json`.
|
|
16
|
+
*/
|
|
17
|
+
/** Stamped on the `authorization` user-property once its grants store hashed identity keys. */
|
|
18
|
+
export declare const AUTHORIZATION_HASHED_INFO_SCHEMA = "authorization-hashed-01";
|
|
19
|
+
/** Stamped on the `team_invitations` account-property once its tokens are stored hashed. */
|
|
20
|
+
export declare const TEAM_INVITATIONS_HASHED_INFO_SCHEMA = "team-invitations-hashed-01";
|
|
21
|
+
//# sourceMappingURL=info-schema-tags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info-schema-tags.d.ts","sourceRoot":"","sources":["../src/info-schema-tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,+FAA+F;AAC/F,eAAO,MAAM,gCAAgC,4BAA4B,CAAC;AAE1E,4FAA4F;AAC5F,eAAO,MAAM,mCAAmC,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* info_schema tags that mark a property payload as written by the
|
|
4
|
+
* credential-hashing library (boop-j8xc epic).
|
|
5
|
+
*
|
|
6
|
+
* Both property tables carry an `info_schema` column that versions the payload
|
|
7
|
+
* shape. For most properties it stays `''`. The two properties that once stored
|
|
8
|
+
* a raw, redeemable credential get a distinct tag the moment their values are
|
|
9
|
+
* hashed — stamped by the fixed library on every new write and by the migration
|
|
10
|
+
* tool on every row it rewrites (boop-j8xc.2 and boop-j8xc.3).
|
|
11
|
+
*
|
|
12
|
+
* The `boop status` data probe uses these tags as a fast, exact migration
|
|
13
|
+
* signal: a row missing the tag predates the fix and may hold a cleartext
|
|
14
|
+
* credential, so the probe needs no JSON parsing — one column comparison with
|
|
15
|
+
* an early exit. Keep each literal in sync with the matching probe query in
|
|
16
|
+
* `capability.json`.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.TEAM_INVITATIONS_HASHED_INFO_SCHEMA = exports.AUTHORIZATION_HASHED_INFO_SCHEMA = void 0;
|
|
20
|
+
/** Stamped on the `authorization` user-property once its grants store hashed identity keys. */
|
|
21
|
+
exports.AUTHORIZATION_HASHED_INFO_SCHEMA = 'authorization-hashed-01';
|
|
22
|
+
/** Stamped on the `team_invitations` account-property once its tokens are stored hashed. */
|
|
23
|
+
exports.TEAM_INVITATIONS_HASHED_INFO_SCHEMA = 'team-invitations-hashed-01';
|
|
24
|
+
//# sourceMappingURL=info-schema-tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info-schema-tags.js","sourceRoot":"","sources":["../src/info-schema-tags.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAEH,+FAA+F;AAClF,QAAA,gCAAgC,GAAG,yBAAyB,CAAC;AAE1E,4FAA4F;AAC/E,QAAA,mCAAmC,GAAG,4BAA4B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-invitation-service.d.ts","sourceRoot":"","sources":["../src/team-invitation-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"team-invitation-service.d.ts","sourceRoot":"","sources":["../src/team-invitation-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.TeamInvitationError = exports.PRUNE_AGE_MS = exports.PRUNE_THRESHOLD = exports.MAX_RESENDS = exports.MAX_PENDING_INVITATIONS = exports.DEFAULT_INVITATION_TTL_MS = void 0;
|
|
4
4
|
exports.createTeamInvitationService = createTeamInvitationService;
|
|
5
5
|
const auth_service_js_1 = require("./auth-service.js");
|
|
6
|
+
const info_schema_tags_js_1 = require("./info-schema-tags.js");
|
|
6
7
|
const cap_services_1 = require("@boopkit/cap-services");
|
|
8
|
+
const logger_1 = require("@boopkit/logger");
|
|
9
|
+
const logger = (0, logger_1.createLogger)('users', 'invitations');
|
|
7
10
|
// ---------- Constants ----------
|
|
8
11
|
exports.DEFAULT_INVITATION_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
9
12
|
exports.MAX_PENDING_INVITATIONS = 20;
|
|
@@ -11,9 +14,35 @@ exports.MAX_RESENDS = 3;
|
|
|
11
14
|
exports.PRUNE_THRESHOLD = 100;
|
|
12
15
|
exports.PRUNE_AGE_MS = 90 * 24 * 60 * 60 * 1000; // 90 days
|
|
13
16
|
const PROPERTY = 'team_invitations';
|
|
14
|
-
const SCHEMA = 'team-invitations-01';
|
|
15
17
|
const MAX_CONCURRENCY_RETRIES = 3;
|
|
16
18
|
const TERMINAL_STATUSES = new Set(['accepted', 'cancelled', 'expired']);
|
|
19
|
+
// An invitation token is a bearer credential: presenting it joins the account at
|
|
20
|
+
// the role the invitation grants. The stored `token` is therefore hashed, exactly
|
|
21
|
+
// as session cookies and API keys are — the raw value is emailed to the invitee
|
|
22
|
+
// once and never persisted. The read path hashes the presented token before
|
|
23
|
+
// comparing. Rows written before this fix stored the RAW token, and boop cannot
|
|
24
|
+
// know whether a downstream has run the migration, so the read tolerates BOTH
|
|
25
|
+
// forms permanently. The raw branch logs once per process to nudge an operator
|
|
26
|
+
// toward the migration; it goes quiet on its own once the data is hashed.
|
|
27
|
+
let warnedRawInvitation = false;
|
|
28
|
+
/**
|
|
29
|
+
* Does a stored invitation `storedToken` correspond to the raw `rawToken` a
|
|
30
|
+
* caller presented? Matches the hashed form (new writes and migrated rows) and
|
|
31
|
+
* the raw form (pre-hash rows). The raw branch warns once per process. Mirrors
|
|
32
|
+
* `grantMatchesIdentityKey` in auth-service, for the same reason.
|
|
33
|
+
*/
|
|
34
|
+
function invitationMatchesToken(storedToken, rawToken) {
|
|
35
|
+
if (storedToken === (0, auth_service_js_1.hashToken)(rawToken))
|
|
36
|
+
return true;
|
|
37
|
+
if (storedToken === rawToken && !(0, auth_service_js_1.isHashedToken)(storedToken)) {
|
|
38
|
+
if (!warnedRawInvitation) {
|
|
39
|
+
warnedRawInvitation = true;
|
|
40
|
+
logger.warn('invitation matched a raw (pre-hash) token; run the credential-hashing migration to rehash it');
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
17
46
|
// ---------- Errors ----------
|
|
18
47
|
class TeamInvitationError extends Error {
|
|
19
48
|
constructor(message) {
|
|
@@ -57,7 +86,7 @@ function createTeamInvitationService(deps, options) {
|
|
|
57
86
|
}
|
|
58
87
|
async function saveInvitations(accountId, invitations, expectedRevision) {
|
|
59
88
|
pruneTerminalInvitations(invitations);
|
|
60
|
-
await accountPropertyService.put(accountId, PROPERTY, { invitations }, { infoSchema:
|
|
89
|
+
await accountPropertyService.put(accountId, PROPERTY, { invitations }, { infoSchema: info_schema_tags_js_1.TEAM_INVITATIONS_HASHED_INFO_SCHEMA, expectedRevision });
|
|
61
90
|
}
|
|
62
91
|
/**
|
|
63
92
|
* Retry a read-modify-write operation on StaleEntityError (optimistic concurrency conflict).
|
|
@@ -129,9 +158,11 @@ function createTeamInvitationService(deps, options) {
|
|
|
129
158
|
throw new TeamInvitationError(`Maximum of ${maxPending} pending invitations reached`);
|
|
130
159
|
}
|
|
131
160
|
const now = new Date();
|
|
161
|
+
// The raw token is emailed to the invitee; only its hash is persisted.
|
|
162
|
+
const rawToken = (0, auth_service_js_1.generateToken)();
|
|
132
163
|
const invitation = {
|
|
133
164
|
id: (0, auth_service_js_1.generateToken)(),
|
|
134
|
-
token: (0, auth_service_js_1.
|
|
165
|
+
token: (0, auth_service_js_1.hashToken)(rawToken),
|
|
135
166
|
email: normalizedEmail,
|
|
136
167
|
role,
|
|
137
168
|
status: 'pending',
|
|
@@ -143,7 +174,8 @@ function createTeamInvitationService(deps, options) {
|
|
|
143
174
|
};
|
|
144
175
|
snapshot.invitations.push(invitation);
|
|
145
176
|
await saveInvitations(accountId, snapshot.invitations, snapshot.revision);
|
|
146
|
-
|
|
177
|
+
// Hand the caller the RAW token to email; the stored record keeps only the hash.
|
|
178
|
+
return { ...invitation, token: rawToken };
|
|
147
179
|
});
|
|
148
180
|
}
|
|
149
181
|
async function cancelInvitation(accountId, invitationId) {
|
|
@@ -171,11 +203,13 @@ function createTeamInvitationService(deps, options) {
|
|
|
171
203
|
if (inv.resendCount >= maxResends) {
|
|
172
204
|
throw new TeamInvitationError(`Maximum of ${maxResends} resends reached for this invitation`);
|
|
173
205
|
}
|
|
174
|
-
|
|
206
|
+
// Mint a fresh raw token, persist only its hash, and email the raw value.
|
|
207
|
+
const rawToken = (0, auth_service_js_1.generateToken)();
|
|
208
|
+
inv.token = (0, auth_service_js_1.hashToken)(rawToken);
|
|
175
209
|
inv.expiresAt = new Date(Date.now() + invitationTtl).toISOString();
|
|
176
210
|
inv.resendCount += 1;
|
|
177
211
|
await saveInvitations(accountId, snapshot.invitations, snapshot.revision);
|
|
178
|
-
return inv;
|
|
212
|
+
return { ...inv, token: rawToken };
|
|
179
213
|
});
|
|
180
214
|
}
|
|
181
215
|
/**
|
|
@@ -188,7 +222,7 @@ function createTeamInvitationService(deps, options) {
|
|
|
188
222
|
const snapshot = await loadInvitations(accountId);
|
|
189
223
|
applyLazyExpiry(snapshot.invitations);
|
|
190
224
|
const now = new Date().toISOString();
|
|
191
|
-
const inv = snapshot.invitations.find((i) => i.
|
|
225
|
+
const inv = snapshot.invitations.find((i) => i.status === 'pending' && invitationMatchesToken(i.token, token));
|
|
192
226
|
if (!inv) {
|
|
193
227
|
throw new TeamInvitationError('Invalid or expired invitation token');
|
|
194
228
|
}
|
|
@@ -206,7 +240,7 @@ function createTeamInvitationService(deps, options) {
|
|
|
206
240
|
}
|
|
207
241
|
async function findByToken(accountId, token) {
|
|
208
242
|
const all = await getInvitations(accountId);
|
|
209
|
-
return all.find((i) => i.
|
|
243
|
+
return all.find((i) => i.status === 'pending' && invitationMatchesToken(i.token, token)) ?? null;
|
|
210
244
|
}
|
|
211
245
|
/**
|
|
212
246
|
* Resolve emails for account members by looking up identities in the 'email' domain.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-invitation-service.js","sourceRoot":"","sources":["../src/team-invitation-service.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"team-invitation-service.js","sourceRoot":"","sources":["../src/team-invitation-service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -130,7 +130,7 @@ An API key is simultaneously an identity, a credential, and a bearer token. All
|
|
|
130
130
|
| **Identity** | `domain: "api-key", key: "a3f1..."` — the SHA-256 of the key, as the cookie domain holds a session token. Lookup hashes the presented key and searches for that |
|
|
131
131
|
| **Authentication** | `{ strategy: "api-key", accepted: true }` — user can use API keys |
|
|
132
132
|
| **Credentials** | `{ strategy: "api-key", secret: "$2b$..." }` — bcrypt hash of the key |
|
|
133
|
-
| **Authorization** | `{ identity_key: "
|
|
133
|
+
| **Authorization** | `{ identity_key: "a3f1...", strategy: "api-key", trust: "authenticated", expires_at: 0 }` — standing per-key grant, keyed on `hashToken(key)` (the same hash the identity row holds) |
|
|
134
134
|
|
|
135
135
|
**Three independent levers to control access:**
|
|
136
136
|
1. **Revoke the key** — end-date the identity. Immediate.
|
|
@@ -159,6 +159,10 @@ A request with no `Authorization` header passes through with `req.user` unset, s
|
|
|
159
159
|
A token that does not resolve gets `401 { error: 'invalid_token' }` and a `WWW-Authenticate` challenge, and does **not** fall back to the session cookie — revoking a key has to stop the request even from a browser that is still logged in.
|
|
160
160
|
It refuses a malformed token on every route it covers, open ones included, which is why the health check is mounted ahead of it: a route registered before `resolveBearer` never sees the middleware, so a monitor carrying a stale or garbled `Authorization` header still gets its `200` rather than a `401`.
|
|
161
161
|
|
|
162
|
+
`resolveBearer` is itself `createBearerMiddleware(deps, { resolve, strategy, challenge })` — exported from `@boopkit/cap-users` — with `validateApiKey` as the resolver and `'api-key'` as the strategy.
|
|
163
|
+
If your app issues another kind of bearer token, build a second middleware from that factory rather than writing one: you get the same parsing and the same refusal semantics, and both doors refuse instead of falling through to the cookie, which is what makes two of them safe on one header.
|
|
164
|
+
Give it its own strategy label — `req.authStrategy` is typed `string` so you can — because `requireApiKey` gates on the `'api-key'` literal, and reusing that label would let your token through the one gate that exists to demand a key.
|
|
165
|
+
|
|
162
166
|
**Issuing a key is yours to design.**
|
|
163
167
|
`authService.createApiKey(userId)` mints one and returns the raw key exactly once; the identity row holds its SHA-256 and the credentials entry a bcrypt hash, so nothing can hand it back later and any surface you build has to say so at the moment it is shown.
|
|
164
168
|
This capability deliberately ships no route, screen, or command for it.
|
|
@@ -242,6 +246,32 @@ All services compose from `@boopkit/cap-services` primitives:
|
|
|
242
246
|
| `userPropertyService` | `createUserPropertyService()` | Read/write user properties (credentials gated) |
|
|
243
247
|
| `authService` | `createAuthService(deps, opts)` | Orchestrates signup, login, sessions, API keys |
|
|
244
248
|
|
|
249
|
+
### IdentityService reads
|
|
250
|
+
|
|
251
|
+
Every read is bounded by a user id or an exact `(domain, key)` pair — except one.
|
|
252
|
+
|
|
253
|
+
`findAllByDomainAndKeyPrefix(domain, keyPrefix, opts?)` returns one page of the rows in a single
|
|
254
|
+
domain whose key starts with `keyPrefix`, for **any** user, in id order:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const page = await identityService.findAllByDomainAndKeyPrefix('oauth-access', `${publicId}|`, {
|
|
258
|
+
includeEnded: true,
|
|
259
|
+
limit: 100, // clamped to 500
|
|
260
|
+
after: previousPage.nextCursor ?? undefined,
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
It exists for credential domains whose keys are `"<issuer>|<secret hash>"` — cap-oauth-server's
|
|
265
|
+
token rows — where retiring one issuer means finding everybody it ever reached, and the operator
|
|
266
|
+
should not have to know the blast radius in advance. Left-anchored, the comparison is a seek on the
|
|
267
|
+
`(domain, key)` unique index rather than a table scan.
|
|
268
|
+
|
|
269
|
+
Both narrowings are load-bearing rather than ergonomic. The **domain is required**, so the scan
|
|
270
|
+
cannot wander into `email` or `api-key` rows; an **empty prefix throws**, because it would walk a
|
|
271
|
+
whole domain, which is the read this method exists to make unnecessary. It **pages** rather than
|
|
272
|
+
materialising a result set of unknown size: keep passing `nextCursor` back as `after` until it comes
|
|
273
|
+
back `null`.
|
|
274
|
+
|
|
245
275
|
### AuthService key operations
|
|
246
276
|
|
|
247
277
|
**Signup**: `signup({ email, password, name })` — creates user, email identity, profile, credentials, auth method, session token, and authorization grant in one call. Each step is also available individually for graduated control.
|
|
@@ -258,9 +288,23 @@ The raw key is returned once and is not what `user_identities` holds: the row st
|
|
|
258
288
|
To authenticate an HTTP request with one, mount `resolveBearer` — it reads `Authorization: Bearer <key>`, sets the same `req.user` the cookie path yields, and answers `401 { error: 'invalid_token' }` with a `WWW-Authenticate: Bearer` challenge when the token does not resolve.
|
|
259
289
|
It never falls back to the session cookie, so revoking a key stops the request even from a browser that still holds a session.
|
|
260
290
|
|
|
261
|
-
> The authorization *grant*
|
|
262
|
-
> `resolveBearer`
|
|
263
|
-
|
|
291
|
+
> The authorization *grant* records `hashToken(key)` as its `identity_key`, never the raw key — the same treatment the identity row and credential entry get. Session grants store their token the same way. `identity_key` is only ever compared for equality, so `setAuthorization` hashes the value it is handed, and `isAuthorized` / `getTrustTier` hash the raw token a caller presents before comparing. The raw value never lands in the readable `authorization` property.
|
|
292
|
+
> `resolveBearer` still records the presented **raw** key as `req.sessionToken`, and the gate calls `isAuthorized(user.id, req.sessionToken, …)`. That keeps working because `isAuthorized` hashes its argument — so the call site does **not** hash, and must not: hashing at both ends would double-hash and never match.
|
|
293
|
+
|
|
294
|
+
### Migrating existing deployments
|
|
295
|
+
|
|
296
|
+
A deployment that stored grants before this fix keeps a raw session token or API key in each `authorization` row. The read path tolerates the old form permanently — it matches either `identity_key === hashToken(token)` or `identity_key === token` — so **nobody is logged out by upgrading**. The first time a raw grant is matched, the service logs one warning per process nudging you to migrate; it goes quiet on its own once the data is hashed.
|
|
297
|
+
|
|
298
|
+
`boop status` flags the un-migrated rows (its probe checks that every `authorization` row carries the `authorization-hashed-01` info_schema tag). To clear them, rehash in place:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
npm run credentials:migrate # REPORT ONLY — counts what would change, writes nothing
|
|
302
|
+
npm run credentials:migrate -- --run # apply the rehash
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The same tool and command also rehash **team invitation tokens**. An invitation token is a bearer credential — presenting it joins the account at the role the invitation grants — so it is hashed at rest exactly as session cookies and API keys are: the raw token is emailed to the invitee once and only its hash is stored in the `team_invitations` account-property, and `acceptInvitation` / `findByToken` hash the presented token before comparing. The read path tolerates a pre-fix row whose stored token is still raw (matching `token === hashToken(presented)` or `token === presented`), so **no pending invitation is voided by upgrading**, and it warns once per process until the data is migrated. `boop status` has a matching probe (every `team_invitations` row must carry the `team-invitations-hashed-01` tag), and the migration run above stamps it.
|
|
306
|
+
|
|
307
|
+
Because the raw values are still present, the tool rehashes them in place rather than invalidating them — every session, standing key, and pending invitation keeps working. It is **one-way and has no rollback**: once a row is rewritten the raw value is gone, so **take a database backup before `--run`**. Re-running is safe: a value that is already a SHA-256 digest is left untouched, so nothing is ever double-hashed.
|
|
264
308
|
|
|
265
309
|
**Trust queries**: `getTrustTier(userId, identityKey)`, `isAuthorized(userId, identityKey, requiredTrust?)`.
|
|
266
310
|
|