@sasjs/api 0.3.0 → 0.3.1

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
@@ -89,6 +89,14 @@ Not implemented yet, in the order the plan sets: the rest of identity (`user`, `
89
89
 
90
90
  A status the document states but only a host can send - an account lockout (`429`), a provider failure (`500`), a session-store failure - is marked `hostResponses` in the overlay. It stays in the document, because a client still receives it, and is excluded from the parity check, whose other side is what this package answers; the host's own suite pins it.
91
91
 
92
+ ## What a host must do
93
+
94
+ Three of the sign-in flow's defences are the host's, because the package has no request to read and no clock to trust:
95
+
96
+ - **Re-resolve the caller on every request.** A host with its own session passes the subject in (`session`, `authorize`), so the service re-reads the account behind it and refuses a deactivated one; a host that caches the subject it resolved at sign-in defeats that check. `logout` is the exception and does not refuse: dropping a credential is not access, and refusing would leave the pair in the store.
97
+ - **Lock the account out after repeated failures.** `POST /SASLogon/login` answers `401 Username is not found.` for an unknown username and `401 Invalid Password.` for a wrong one, because that is what the server's own form tells a user - which also means the route cannot tell a caller to stop guessing on its own. The defence is the host's limiter, answering `429` with `Retry-After` in seconds; without it the route is a way to enumerate usernames and account state.
98
+ - **Answer a session-store or identity-provider failure with `500`.** Those are the host's own dependencies, and the overlay marks the statuses as `hostResponses`.
99
+
92
100
  ## Development
93
101
 
94
102
  ```bash
package/build/browser.js CHANGED
@@ -1,4 +1,4 @@
1
- /* @sasjs/api 0.3.0 - the SASjs Server API, for a service worker. https://github.com/sasjs/api */
1
+ /* @sasjs/api 0.3.1 - the SASjs Server API, for a service worker. https://github.com/sasjs/api */
2
2
  "use strict";
3
3
  var SASjsApi = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -2381,7 +2381,8 @@ var SASjsApi = (() => {
2381
2381
  if (!subject) throw unauthorized();
2382
2382
  if (!await store.client(clientId))
2383
2383
  throw new ApiError(403, "Invalid clientId.");
2384
- return store.issueCode({ userId: subject.userId, clientId });
2384
+ const named = await liveSubject(store, subject);
2385
+ return store.issueCode({ userId: named.userId, clientId });
2385
2386
  },
2386
2387
  async token({ clientId, code }) {
2387
2388
  const subject = await store.readCode(code);
@@ -2418,7 +2419,7 @@ var SASjsApi = (() => {
2418
2419
  async session(subject) {
2419
2420
  if (!subject) throw unauthorized();
2420
2421
  const user3 = await store.userById(subject.userId);
2421
- if (!user3) throw unauthorized();
2422
+ if (!user3 || !user3.isActive) throw unauthorized();
2422
2423
  return asSession(user3);
2423
2424
  }
2424
2425
  });
@@ -2656,8 +2657,23 @@ var SASjsApi = (() => {
2656
2657
  const pair = this.pairs.get(keyOf(subject));
2657
2658
  return pair && pair[kind] === token ? subject : void 0;
2658
2659
  }
2660
+ /**
2661
+ * The subject's saved pair, while both of its tokens are still live.
2662
+ *
2663
+ * An exchange hands a saved pair back rather than rotating a second tab's
2664
+ * tokens. That is only an answer if the pair can still be used: a saved pair
2665
+ * whose access token has aged out would sign the caller in and then refuse
2666
+ * every request it makes, so an aged-out pair is reported as absent and the
2667
+ * exchange mints a fresh one. `live()` reads the issue-time expiry, which a
2668
+ * seeded token does not carry - a seeded pair therefore never ages out here.
2669
+ */
2659
2670
  async pairFor(subject) {
2660
- return this.pairs.get(keyOf(subject));
2671
+ const pair = this.pairs.get(keyOf(subject));
2672
+ if (!pair) return void 0;
2673
+ const now = Date.now();
2674
+ const access = this.access.get(pair.accessToken);
2675
+ const refresh = this.refresh.get(pair.refreshToken);
2676
+ return live(access, now) && live(refresh, now) ? pair : void 0;
2661
2677
  }
2662
2678
  async savePair(subject, pair) {
2663
2679
  this.pairs.set(keyOf(subject), pair);
@@ -149,8 +149,24 @@ class MemoryIdentityStore {
149
149
  const pair = this.pairs.get(keyOf(subject));
150
150
  return pair && pair[kind] === token ? subject : undefined;
151
151
  }
152
+ /**
153
+ * The subject's saved pair, while both of its tokens are still live.
154
+ *
155
+ * An exchange hands a saved pair back rather than rotating a second tab's
156
+ * tokens. That is only an answer if the pair can still be used: a saved pair
157
+ * whose access token has aged out would sign the caller in and then refuse
158
+ * every request it makes, so an aged-out pair is reported as absent and the
159
+ * exchange mints a fresh one. `live()` reads the issue-time expiry, which a
160
+ * seeded token does not carry - a seeded pair therefore never ages out here.
161
+ */
152
162
  async pairFor(subject) {
153
- return this.pairs.get(keyOf(subject));
163
+ const pair = this.pairs.get(keyOf(subject));
164
+ if (!pair)
165
+ return undefined;
166
+ const now = Date.now();
167
+ const access = this.access.get(pair.accessToken);
168
+ const refresh = this.refresh.get(pair.refreshToken);
169
+ return live(access, now) && live(refresh, now) ? pair : undefined;
154
170
  }
155
171
  async savePair(subject, pair) {
156
172
  this.pairs.set(keyOf(subject), pair);
@@ -72,7 +72,12 @@ const createIdentityService = (store) => ({
72
72
  throw unauthorized();
73
73
  if (!(await store.client(clientId)))
74
74
  throw new types_js_1.ApiError(403, 'Invalid clientId.');
75
- return store.issueCode({ userId: subject.userId, clientId });
75
+ // A host resolves the caller once, at sign-in; the account may since have
76
+ // been deactivated. sasjs/server re-reads the user on every request and
77
+ // refuses an inactive one, so a session that outlived the account must not
78
+ // go on buying codes here.
79
+ const named = await liveSubject(store, subject);
80
+ return store.issueCode({ userId: named.userId, clientId });
76
81
  },
77
82
  async token({ clientId, code }) {
78
83
  // Read before claiming: a code presented with another client is refused
@@ -116,6 +121,10 @@ const createIdentityService = (store) => ({
116
121
  async logout(subject) {
117
122
  if (!subject)
118
123
  throw unauthorized();
124
+ // Deliberately no liveness check: the caller is asking for its credential to
125
+ // be dropped, and refusing would leave a pair in the store that nothing can
126
+ // use. Every path that READS the account refuses an inactive one, so a
127
+ // deactivated caller's sign-out is housekeeping rather than access.
119
128
  await store.deletePair(subject);
120
129
  },
121
130
  async logoutSession() {
@@ -124,8 +133,10 @@ const createIdentityService = (store) => ({
124
133
  async session(subject) {
125
134
  if (!subject)
126
135
  throw unauthorized();
136
+ // As with `authorize`: the account behind a host's session is re-read here,
137
+ // because the session outlives the account.
127
138
  const user = await store.userById(subject.userId);
128
- if (!user)
139
+ if (!user || !user.isActive)
129
140
  throw unauthorized();
130
141
  return (0, exports.asSession)(user);
131
142
  }
@@ -41,6 +41,16 @@ export declare class MemoryIdentityStore implements IdentityStore {
41
41
  readAccess(token: string): Promise<Subject | undefined>;
42
42
  readRefresh(token: string): Promise<Subject | undefined>;
43
43
  private current;
44
+ /**
45
+ * The subject's saved pair, while both of its tokens are still live.
46
+ *
47
+ * An exchange hands a saved pair back rather than rotating a second tab's
48
+ * tokens. That is only an answer if the pair can still be used: a saved pair
49
+ * whose access token has aged out would sign the caller in and then refuse
50
+ * every request it makes, so an aged-out pair is reported as absent and the
51
+ * exchange mints a fresh one. `live()` reads the issue-time expiry, which a
52
+ * seeded token does not carry - a seeded pair therefore never ages out here.
53
+ */
44
54
  pairFor(subject: Subject): Promise<TokenPair | undefined>;
45
55
  savePair(subject: Subject, pair: TokenPair): Promise<void>;
46
56
  deletePair(subject: Subject): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/identity/memory-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAyDrB;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAEhC,OAAO,GAAE,eAAkC;IAsDjD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI/D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI9D,0EAA0E;IACpE,eAAe,CACnB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC;IAIb,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI7D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;IAoBzE;;;;;OAKG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAIvD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAI9D,OAAO,CAAC,OAAO;IAeT,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAIzD,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAS5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAIpD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAKhD"}
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/identity/memory-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AAyDrB;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;gBAEhC,OAAO,GAAE,eAAkC;IAsDjD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI/D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAI9D,0EAA0E;IACpE,eAAe,CACnB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC;IAIb,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAI7D,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;IAoBzE;;;;;OAKG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAIvD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAI9D,OAAO,CAAC,OAAO;IAef;;;;;;;;;OASG;IACG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAWzD,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAS5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAIpD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAKhD"}
@@ -146,8 +146,24 @@ export class MemoryIdentityStore {
146
146
  const pair = this.pairs.get(keyOf(subject));
147
147
  return pair && pair[kind] === token ? subject : undefined;
148
148
  }
149
+ /**
150
+ * The subject's saved pair, while both of its tokens are still live.
151
+ *
152
+ * An exchange hands a saved pair back rather than rotating a second tab's
153
+ * tokens. That is only an answer if the pair can still be used: a saved pair
154
+ * whose access token has aged out would sign the caller in and then refuse
155
+ * every request it makes, so an aged-out pair is reported as absent and the
156
+ * exchange mints a fresh one. `live()` reads the issue-time expiry, which a
157
+ * seeded token does not carry - a seeded pair therefore never ages out here.
158
+ */
149
159
  async pairFor(subject) {
150
- return this.pairs.get(keyOf(subject));
160
+ const pair = this.pairs.get(keyOf(subject));
161
+ if (!pair)
162
+ return undefined;
163
+ const now = Date.now();
164
+ const access = this.access.get(pair.accessToken);
165
+ const refresh = this.refresh.get(pair.refreshToken);
166
+ return live(access, now) && live(refresh, now) ? pair : undefined;
151
167
  }
152
168
  async savePair(subject, pair) {
153
169
  this.pairs.set(keyOf(subject), pair);
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/identity/service.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACV,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAA;QACX,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,EAAE,MAAM,CAAA;QACnB,OAAO,EAAE,OAAO,CAAA;QAChB,qBAAqB,EAAE,OAAO,CAAA;KAC/B,CAAA;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,qBAAqB,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAA;IACrE,KAAK,CAAC,WAAW,EAAE;QACjB,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,EAAE,MAAM,CAAA;KACjB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACxB,sEAAsE;IACtE,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1E,iDAAiD;IACjD,KAAK,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IACtE,uDAAuD;IACvD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IACtD,sEAAsE;IACtE,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnD,iFAAiF;IACjF,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAChC,yDAAyD;IACzD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;CAC9D;AAsBD,eAAO,MAAM,qBAAqB,GAChC,OAAO,aAAa,KACnB,eAiHD,CAAA;AAEF,kDAAkD;AAClD,eAAO,MAAM,SAAS,GAAI,MAAM,YAAY,KAAG,aAM7C,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/identity/service.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACV,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAA;QACX,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,EAAE,MAAM,CAAA;QACnB,OAAO,EAAE,OAAO,CAAA;QAChB,qBAAqB,EAAE,OAAO,CAAA;KAC/B,CAAA;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,qBAAqB,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAA;IACrE,KAAK,CAAC,WAAW,EAAE;QACjB,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,EAAE,MAAM,CAAA;KACjB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACxB,sEAAsE;IACtE,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1E,iDAAiD;IACjD,KAAK,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IACtE,uDAAuD;IACvD,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IACtD,sEAAsE;IACtE,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnD,iFAAiF;IACjF,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAChC,yDAAyD;IACzD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;CAC9D;AAsBD,eAAO,MAAM,qBAAqB,GAChC,OAAO,aAAa,KACnB,eA6HD,CAAA;AAEF,kDAAkD;AAClD,eAAO,MAAM,SAAS,GAAI,MAAM,YAAY,KAAG,aAM7C,CAAA"}
@@ -69,7 +69,12 @@ export const createIdentityService = (store) => ({
69
69
  throw unauthorized();
70
70
  if (!(await store.client(clientId)))
71
71
  throw new ApiError(403, 'Invalid clientId.');
72
- return store.issueCode({ userId: subject.userId, clientId });
72
+ // A host resolves the caller once, at sign-in; the account may since have
73
+ // been deactivated. sasjs/server re-reads the user on every request and
74
+ // refuses an inactive one, so a session that outlived the account must not
75
+ // go on buying codes here.
76
+ const named = await liveSubject(store, subject);
77
+ return store.issueCode({ userId: named.userId, clientId });
73
78
  },
74
79
  async token({ clientId, code }) {
75
80
  // Read before claiming: a code presented with another client is refused
@@ -113,6 +118,10 @@ export const createIdentityService = (store) => ({
113
118
  async logout(subject) {
114
119
  if (!subject)
115
120
  throw unauthorized();
121
+ // Deliberately no liveness check: the caller is asking for its credential to
122
+ // be dropped, and refusing would leave a pair in the store that nothing can
123
+ // use. Every path that READS the account refuses an inactive one, so a
124
+ // deactivated caller's sign-out is housekeeping rather than access.
116
125
  await store.deletePair(subject);
117
126
  },
118
127
  async logoutSession() {
@@ -121,8 +130,10 @@ export const createIdentityService = (store) => ({
121
130
  async session(subject) {
122
131
  if (!subject)
123
132
  throw unauthorized();
133
+ // As with `authorize`: the account behind a host's session is re-read here,
134
+ // because the session outlives the account.
124
135
  const user = await store.userById(subject.userId);
125
- if (!user)
136
+ if (!user || !user.isActive)
126
137
  throw unauthorized();
127
138
  return asSession(user);
128
139
  }
@@ -72,8 +72,12 @@ export interface Route extends GeneratedRoute {
72
72
  * the host's limiter, a provider failure is its identity provider. The corpus
73
73
  * does not answer them, so the parity check compares the document without
74
74
  * them; the host's own suite pins them.
75
+ *
76
+ * Optional, and read as empty when absent: a host that builds a `Route` of its
77
+ * own is describing the document, not the lockout, and requiring the field
78
+ * made every such value a compile error.
75
79
  */
76
- hostResponses: number[];
80
+ hostResponses?: number[];
77
81
  capabilities: Capability[];
78
82
  /**
79
83
  * Whether this package carries a handler for the operation. A host may still
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sasjs/api",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "The SASjs Server API, described once - a generated route table, the services behind it, and the conformance corpus both hosts run",
5
5
  "license": "MIT",
6
6
  "author": "SASjs",