@adonis-agora/authkit-server 0.82.0 → 0.82.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/build/src/accounts/lucid_store/organizations.js +9 -1
- package/build/src/adapters/database_adapter.js +2 -1
- package/build/src/adapters/redis_adapter.js +13 -5
- package/build/src/host/persistent_rp_session.js +20 -1
- package/build/src/host/ui-dist/assets/index-C1eSfG8B.js +67 -0
- package/build/src/host/ui-dist/index.html +1 -1
- package/package.json +4 -4
- package/build/src/host/ui-dist/assets/index-d00IbXWz.js +0 -155
|
@@ -79,9 +79,17 @@ export function buildOrganizations(ctx) {
|
|
|
79
79
|
},
|
|
80
80
|
async listOrgsForAccount(accountId) {
|
|
81
81
|
const memberships = await MemberModel.query().where('account_id', accountId);
|
|
82
|
+
if (memberships.length === 0)
|
|
83
|
+
return [];
|
|
84
|
+
// One query for every org (by primary key, like `find`), instead of one per
|
|
85
|
+
// membership. The result keeps the memberships' order.
|
|
86
|
+
const ids = [...new Set(memberships.map((m) => m.organizationId))];
|
|
87
|
+
const orgs = new Map();
|
|
88
|
+
for (const org of await OrgModel.findMany(ids))
|
|
89
|
+
orgs.set(String(org.id), org);
|
|
82
90
|
const result = [];
|
|
83
91
|
for (const m of memberships) {
|
|
84
|
-
const org =
|
|
92
|
+
const org = orgs.get(String(m.organizationId));
|
|
85
93
|
if (org)
|
|
86
94
|
result.push({ ...toOrgSummary(org), role: m.role });
|
|
87
95
|
}
|
|
@@ -119,7 +119,8 @@ export class DatabaseAdapter {
|
|
|
119
119
|
const accountId = payload.accountId;
|
|
120
120
|
row.account_id = typeof accountId === 'string' && accountId ? accountId : null;
|
|
121
121
|
}
|
|
122
|
-
|
|
122
|
+
// Only whether the row exists matters: don't read the (possibly large) payload back.
|
|
123
|
+
const existing = await this.#query().where('id', id).select('id').first();
|
|
123
124
|
if (existing) {
|
|
124
125
|
await this.#query().where('id', id).update(row);
|
|
125
126
|
}
|
|
@@ -101,15 +101,23 @@ export class RedisAdapter {
|
|
|
101
101
|
return this.find(id);
|
|
102
102
|
}
|
|
103
103
|
async consume(id) {
|
|
104
|
-
const
|
|
105
|
-
|
|
104
|
+
const key = this.#key(id);
|
|
105
|
+
// GET + TTL in one round trip (a pipeline, read-only: nothing changes before the SET).
|
|
106
|
+
const results = await this.redis.pipeline().get(key).ttl(key).exec();
|
|
107
|
+
const [getError, data] = results?.[0] ?? [null, null];
|
|
108
|
+
if (getError)
|
|
109
|
+
throw getError;
|
|
110
|
+
if (!data)
|
|
106
111
|
return;
|
|
112
|
+
const [ttlError, ttl] = results?.[1] ?? [null, -1];
|
|
113
|
+
if (ttlError)
|
|
114
|
+
throw ttlError;
|
|
115
|
+
const found = JSON.parse(data);
|
|
107
116
|
found.consumed = Math.floor(Date.now() / 1000);
|
|
108
|
-
const ttl = await this.redis.ttl(this.#key(id));
|
|
109
117
|
if (ttl > 0)
|
|
110
|
-
await this.redis.set(
|
|
118
|
+
await this.redis.set(key, JSON.stringify(found), 'EX', ttl);
|
|
111
119
|
else
|
|
112
|
-
await this.redis.set(
|
|
120
|
+
await this.redis.set(key, JSON.stringify(found));
|
|
113
121
|
}
|
|
114
122
|
async destroy(id) {
|
|
115
123
|
await this.redis.del(this.#key(id));
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { ACCOUNT_IDP_SESSION_KEY, readIdpSession } from './idp_session_bridge.js';
|
|
2
2
|
import { impersonationState } from './impersonation_session.js';
|
|
3
3
|
import { assertAccountEnabled } from './login_attempt.js';
|
|
4
|
+
/**
|
|
5
|
+
* How long (seconds) a renewal stays good enough: a linked RP whose IdP session was
|
|
6
|
+
* renewed less than this ago, with the current TTL, is not renewed again. Without
|
|
7
|
+
* it every authenticated request wrote the IdP session row and re-sent its cookie.
|
|
8
|
+
*/
|
|
9
|
+
const RENEW_RESOLUTION_SEC = 60;
|
|
10
|
+
/**
|
|
11
|
+
* The linked RP's IdP session already expires between `ttl - RENEW_RESOLUTION_SEC`
|
|
12
|
+
* and `ttl` from now, i.e. it was renewed with this TTL moments ago. Renewing again
|
|
13
|
+
* would only move the expiry by those few seconds. A session that expires LATER
|
|
14
|
+
* than `now + ttl` (the TTL was lowered) is still renewed, which shortens it.
|
|
15
|
+
* Restoration (no local login yet) always renews.
|
|
16
|
+
*/
|
|
17
|
+
function renewedRecently(idp, ttl, current, linkedUid) {
|
|
18
|
+
if (!current || linkedUid !== idp.uid || typeof idp.exp !== 'number')
|
|
19
|
+
return false;
|
|
20
|
+
const remaining = idp.exp - Date.now() / 1000;
|
|
21
|
+
return remaining <= ttl && remaining > ttl - RENEW_RESOLUTION_SEC;
|
|
22
|
+
}
|
|
4
23
|
/** Same-host RP restoration: the signed IdP cookie is the persistent credential. */
|
|
5
24
|
export async function ensureRpSession(ctx, sessionKey) {
|
|
6
25
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
@@ -39,7 +58,7 @@ export async function ensureRpSession(ctx, sessionKey) {
|
|
|
39
58
|
}
|
|
40
59
|
const adapter = service.provider.Session.adapter;
|
|
41
60
|
const ttl = service.sessionTtlHolder.rememberSec;
|
|
42
|
-
if (adapter.renewSession) {
|
|
61
|
+
if (adapter.renewSession && !renewedRecently(idp, ttl, current, linkedUid)) {
|
|
43
62
|
if (!(await adapter.renewSession(idp.id, ttl))) {
|
|
44
63
|
if (linkedUid)
|
|
45
64
|
ctx.session.clear();
|