@occupop/lib-auth 4.1.0 → 4.2.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 CHANGED
@@ -188,3 +188,35 @@ const response = await fetch(gatewayUrl, {
188
188
  - The token is identical to the one `@occupop/lib-context` 2.0.1's `makeGetAuthUserToken`
189
189
  produces for the same user, secret and time. `src/index_test.ts` checks this against tokens
190
190
  recorded from lib-context (`src/auth-user-token-vectors.json`).
191
+
192
+ ## Signing a scoped link
193
+
194
+ `signScopedUserToken(scopedUser, jwtid)` signs a **scope** — a grant over named entities and fields,
195
+ rather than a user — with `jwtid` as the token's `jti`, for a link a service hands out: an approval
196
+ link, a one-entity grant.
197
+
198
+ ```typescript
199
+ import { signScopedUserToken } from '@occupop/lib-auth'
200
+
201
+ const scope = { [jobUuid]: { allowedFields: ['signOffWorkflow', 'userAssignments'] } }
202
+ const token = signScopedUserToken(scope, approverUuid) // a string; throws for a null scope
203
+ const link = `${appUrl}/sign-off/${token}`
204
+ ```
205
+
206
+ `getScopedUser` reads such a token back, and `getAuthUser` returns `null` for it, so the two readers
207
+ stay disjoint.
208
+
209
+ - HS256 with `JWT_SECRET`, read when the function is called. If it is not set, the function throws
210
+ `'Env var JWT_SECRET not set?'`, as verification does.
211
+ - `jwtid` becomes the token's `jti` claim. Callers commonly use the uuid of the row the link grants
212
+ access to, so a link can be traced back to it.
213
+ - The payload is signed as given: no expiry, issuer or audience is added, and jsonwebtoken adds `iat`
214
+ only when the payload has none — a payload that already carries one keeps it.
215
+ - **It throws `'scope not defined'` for a null scope**, where `signAuthUserToken` returns `null` for a
216
+ null user. That difference is deliberate, not an oversight: a caller minting a link has no use for a
217
+ null token.
218
+ - Passing an `AuthUser` does not compile. The parameter is `ScopedUser | null`, and an `AuthUser`'s
219
+ `userUuid: string` conflicts with `ScopedUser`'s `userUuid?: undefined`.
220
+ - The token is identical to the one `@occupop/lib-context` 2.0.1's `makeGetScopedUserToken` produces
221
+ for the same scope, `jti`, secret and time. `src/index_test.ts` checks this against recorded tokens
222
+ (`src/scoped-user-token-vectors.json`).
package/dist/index.d.ts CHANGED
@@ -8,9 +8,24 @@ export type AuthUser = {
8
8
  readonly hiringCompanyUuid?: string;
9
9
  readonly recruitmentAgencyUuid?: string;
10
10
  readonly sysAdmin?: boolean;
11
+ readonly sysAdminEmail?: string;
11
12
  readonly isAdmin?: boolean;
12
13
  readonly isSuperAdmin?: boolean;
14
+ readonly permissions?: Permission;
13
15
  };
16
+ export interface Permission {
17
+ isSuperAdmin: boolean;
18
+ isAdmin: boolean;
19
+ candidates: boolean;
20
+ favorites: boolean;
21
+ recruiters: boolean;
22
+ formsDocuments: boolean;
23
+ contracts: boolean;
24
+ jobRequisition: boolean;
25
+ jobRequisitionCreate: boolean;
26
+ jobsCreate: boolean;
27
+ jobsPublish: boolean;
28
+ }
14
29
  export type ScopedUser = {
15
30
  readonly userUuid?: undefined;
16
31
  readonly [entityUuid: string]: {
@@ -26,6 +41,15 @@ export declare function getScopedUser(req: Request): ScopedUser | null;
26
41
  * `@occupop/lib-context` 2.0.1's `makeGetAuthUserToken` produces. Returns `null` for no user.
27
42
  */
28
43
  export declare function signAuthUserToken(authUser: AuthUser | null): string | null;
44
+ /**
45
+ * Signs `scopedUser` with `JWT_SECRET` (HS256) and `jwtid` as the token's `jti`, for a scoped link
46
+ * a service hands out — an approval link, a one-entity grant. The payload is signed as given: no
47
+ * expiry, issuer or audience is added, and jsonwebtoken adds `iat` when the payload has none. The
48
+ * token is the one `@occupop/lib-context` 2.0.1's `makeGetScopedUserToken` produces. Throws for an
49
+ * absent scope — unlike `signAuthUserToken`, which returns `null` for an absent user, because a
50
+ * caller minting a link has no meaningful use for a null token.
51
+ */
52
+ export declare function signScopedUserToken(scopedUser: ScopedUser | null, jwtid: string): string;
29
53
  export declare function makeAuthContextValueFunction<IContainer extends AwilixContainer>({ container }: {
30
54
  container: IContainer;
31
55
  }): ({ req }: {
package/dist/index.js CHANGED
@@ -44,6 +44,21 @@ export function signAuthUserToken(authUser) {
44
44
  throw 'Env var JWT_SECRET not set?';
45
45
  return jwt.sign(authUser, process.env.JWT_SECRET);
46
46
  }
47
+ /**
48
+ * Signs `scopedUser` with `JWT_SECRET` (HS256) and `jwtid` as the token's `jti`, for a scoped link
49
+ * a service hands out — an approval link, a one-entity grant. The payload is signed as given: no
50
+ * expiry, issuer or audience is added, and jsonwebtoken adds `iat` when the payload has none. The
51
+ * token is the one `@occupop/lib-context` 2.0.1's `makeGetScopedUserToken` produces. Throws for an
52
+ * absent scope — unlike `signAuthUserToken`, which returns `null` for an absent user, because a
53
+ * caller minting a link has no meaningful use for a null token.
54
+ */
55
+ export function signScopedUserToken(scopedUser, jwtid) {
56
+ if (!scopedUser)
57
+ throw new Error('scope not defined');
58
+ if (!process.env.JWT_SECRET)
59
+ throw 'Env var JWT_SECRET not set?';
60
+ return jwt.sign(scopedUser, process.env.JWT_SECRET, { jwtid });
61
+ }
47
62
  export function makeAuthContextValueFunction({ container }) {
48
63
  return async ({ req }) => {
49
64
  const authUser = getAuthUser(req);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@occupop/lib-auth",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "index.ts",
6
6
  "description": "...",