@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 +8 -0
- package/build/browser.js +20 -4
- package/build/cjs/identity/memory-store.js +17 -1
- package/build/cjs/identity/service.js +13 -2
- package/build/esm/identity/memory-store.d.ts +10 -0
- package/build/esm/identity/memory-store.d.ts.map +1 -1
- package/build/esm/identity/memory-store.js +17 -1
- package/build/esm/identity/service.d.ts.map +1 -1
- package/build/esm/identity/service.js +13 -2
- package/build/esm/types.d.ts +5 -1
- package/build/esm/types.d.ts.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
}
|
package/build/esm/types.d.ts
CHANGED
|
@@ -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
|
|
80
|
+
hostResponses?: number[];
|
|
77
81
|
capabilities: Capability[];
|
|
78
82
|
/**
|
|
79
83
|
* Whether this package carries a handler for the operation. A host may still
|
package/build/esm/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAK/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,QAAO;gBAGb,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,QACH,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,UAAU,GACV,KAAK,GACL,MAAM,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEpE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,aAAa,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,SAAS,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,KAAM,SAAQ,cAAc;IAC3C,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED