@soulcode/bexio-oauth 0.1.0 → 0.3.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 +9 -5
- package/dist/claims.d.ts +3 -3
- package/dist/claims.d.ts.map +1 -1
- package/dist/claims.js +5 -5
- package/dist/claims.js.map +1 -1
- package/dist/client.d.ts +4 -137
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +18 -629
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/connect-flow.d.ts +15 -0
- package/dist/connect-flow.d.ts.map +1 -0
- package/dist/connect-flow.js +123 -0
- package/dist/connect-flow.js.map +1 -0
- package/dist/crypto.d.ts +2 -2
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +20 -15
- package/dist/crypto.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/external-tokens.d.ts +8 -0
- package/dist/external-tokens.d.ts.map +1 -0
- package/dist/external-tokens.js +73 -0
- package/dist/external-tokens.js.map +1 -0
- package/dist/flow-cookie.d.ts +23 -0
- package/dist/flow-cookie.d.ts.map +1 -0
- package/dist/flow-cookie.js +44 -0
- package/dist/flow-cookie.js.map +1 -0
- package/dist/grants.d.ts +45 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +143 -0
- package/dist/grants.js.map +1 -0
- package/dist/idle-sweep.d.ts +11 -0
- package/dist/idle-sweep.d.ts.map +1 -0
- package/dist/idle-sweep.js +70 -0
- package/dist/idle-sweep.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations.generated.d.ts +3 -0
- package/dist/migrations.generated.d.ts.map +1 -0
- package/dist/migrations.generated.js +56 -0
- package/dist/migrations.generated.js.map +1 -0
- package/dist/oidc.d.ts +30 -0
- package/dist/oidc.d.ts.map +1 -0
- package/dist/oidc.js +67 -0
- package/dist/oidc.js.map +1 -0
- package/dist/refresh.d.ts +30 -0
- package/dist/refresh.d.ts.map +1 -0
- package/dist/refresh.js +147 -0
- package/dist/refresh.js.map +1 -0
- package/dist/return-to.d.ts +8 -0
- package/dist/return-to.d.ts.map +1 -0
- package/dist/return-to.js +31 -0
- package/dist/return-to.js.map +1 -0
- package/dist/sql.d.ts +6 -1
- package/dist/sql.d.ts.map +1 -1
- package/dist/sql.js +8 -12
- package/dist/sql.js.map +1 -1
- package/dist/store/memory.d.ts.map +1 -1
- package/dist/store/memory.js +82 -46
- package/dist/store/memory.js.map +1 -1
- package/dist/store/postgres.d.ts +4 -4
- package/dist/store/postgres.d.ts.map +1 -1
- package/dist/store/postgres.js +137 -59
- package/dist/store/postgres.js.map +1 -1
- package/dist/store/types.d.ts +61 -21
- package/dist/store/types.d.ts.map +1 -1
- package/dist/types.d.ts +164 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +3 -1
- package/sql/0001_bexio_oauth.sql +48 -0
- package/sql/0001_bexio_grants.sql +0 -32
package/dist/store/postgres.js
CHANGED
|
@@ -1,16 +1,53 @@
|
|
|
1
|
-
|
|
1
|
+
import { BexioOAuthError } from "../errors.js";
|
|
2
|
+
// `id` columns are uuids: any other value matches no row, and would make
|
|
3
|
+
// Postgres reject the cast instead of returning nothing.
|
|
4
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
|
+
const CONNECTION_COLUMNS = `id, bexio_company_id, bexio_user_id, bexio_company_user_id,
|
|
2
6
|
company_name, user_name, user_email, role, status, access_token, refresh_token,
|
|
3
7
|
access_expires_at, refresh_expires_at, scope, refresh_locked_until,
|
|
4
|
-
last_refreshed_at, created_at, updated_at`;
|
|
8
|
+
authorized_at, last_refreshed_at, created_at, updated_at`;
|
|
9
|
+
/** Connection columns qualified with `c.`, for joins. */
|
|
10
|
+
const C_COLUMNS = CONNECTION_COLUMNS.split(",")
|
|
11
|
+
.map((column) => `c.${column.trim()}`)
|
|
12
|
+
.join(", ");
|
|
13
|
+
/** A grant joined with its connection (`c`) and the grant (`g`). */
|
|
14
|
+
const GRANT_COLUMNS = `g.id AS grant_id, g.owner_id, g.created_at AS grant_created_at, ${C_COLUMNS}`;
|
|
15
|
+
/**
|
|
16
|
+
* The connection's columns an upsert sets, and the value each takes. They
|
|
17
|
+
* change only for an authorization at least as new as the stored one: Bexio
|
|
18
|
+
* has invalidated the older chain, so a slow connect that authorized earlier
|
|
19
|
+
* must not overwrite a later one. The grant is linked either way.
|
|
20
|
+
*/
|
|
21
|
+
const UPSERT_VALUES = [
|
|
22
|
+
["bexio_company_user_id", "EXCLUDED.bexio_company_user_id"],
|
|
23
|
+
["company_name", "EXCLUDED.company_name"],
|
|
24
|
+
["user_name", "EXCLUDED.user_name"],
|
|
25
|
+
["user_email", "EXCLUDED.user_email"],
|
|
26
|
+
["role", "EXCLUDED.role"],
|
|
27
|
+
["status", "'active'"],
|
|
28
|
+
["access_token", "EXCLUDED.access_token"],
|
|
29
|
+
["refresh_token", "EXCLUDED.refresh_token"],
|
|
30
|
+
["access_expires_at", "EXCLUDED.access_expires_at"],
|
|
31
|
+
["refresh_expires_at", "EXCLUDED.refresh_expires_at"],
|
|
32
|
+
["scope", "EXCLUDED.scope"],
|
|
33
|
+
["refresh_locked_until", "NULL"],
|
|
34
|
+
["refresh_lock_token", "NULL"],
|
|
35
|
+
["authorized_at", "EXCLUDED.authorized_at"],
|
|
36
|
+
["last_refreshed_at", "EXCLUDED.last_refreshed_at"],
|
|
37
|
+
["updated_at", "EXCLUDED.updated_at"],
|
|
38
|
+
];
|
|
39
|
+
const NEWER_AUTHORIZATION_WINS = UPSERT_VALUES.map(([column, value]) => `${column} = CASE WHEN EXCLUDED.authorized_at >= bexio_connections.authorized_at
|
|
40
|
+
THEN ${value} ELSE bexio_connections.${column} END`).join(",\n ");
|
|
41
|
+
/** SQLSTATE for a foreign key violation. */
|
|
42
|
+
const FOREIGN_KEY_VIOLATION = "23503";
|
|
5
43
|
function date(value) {
|
|
6
44
|
if (value === null)
|
|
7
45
|
return null;
|
|
8
46
|
return value instanceof Date ? value : new Date(value);
|
|
9
47
|
}
|
|
10
|
-
function
|
|
48
|
+
function toConnection(row) {
|
|
11
49
|
return {
|
|
12
50
|
id: row.id,
|
|
13
|
-
ownerId: row.owner_id,
|
|
14
51
|
companyId: row.bexio_company_id,
|
|
15
52
|
userId: row.bexio_user_id,
|
|
16
53
|
companyUserId: row.bexio_company_user_id === null
|
|
@@ -27,49 +64,49 @@ function toRecord(row) {
|
|
|
27
64
|
refreshExpiresAt: date(row.refresh_expires_at),
|
|
28
65
|
scope: row.scope,
|
|
29
66
|
refreshLockedUntil: date(row.refresh_locked_until),
|
|
67
|
+
authorizedAt: date(row.authorized_at),
|
|
30
68
|
lastRefreshedAt: date(row.last_refreshed_at),
|
|
31
69
|
createdAt: date(row.created_at),
|
|
32
70
|
updatedAt: date(row.updated_at),
|
|
33
71
|
};
|
|
34
72
|
}
|
|
73
|
+
function toGrant(row) {
|
|
74
|
+
return {
|
|
75
|
+
id: row.grant_id,
|
|
76
|
+
ownerId: row.owner_id,
|
|
77
|
+
createdAt: date(row.grant_created_at),
|
|
78
|
+
connection: toConnection(row),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
35
81
|
/**
|
|
36
|
-
*
|
|
37
|
-
* `npx @soulcode/bexio-oauth sql`). Every operation is a single
|
|
38
|
-
* so it works through transaction-mode poolers and HTTP drivers.
|
|
82
|
+
* Store on the `bexio_connections` and `bexio_grants` tables (create them
|
|
83
|
+
* with `npx @soulcode/bexio-oauth sql`). Every operation is a single
|
|
84
|
+
* statement, so it works through transaction-mode poolers and HTTP drivers.
|
|
39
85
|
*/
|
|
40
86
|
export function postgresStore(sql) {
|
|
41
|
-
const
|
|
87
|
+
const connection = async (text, params) => {
|
|
42
88
|
const { rows } = await sql.query(text, params);
|
|
43
|
-
return rows[0] ?
|
|
44
|
-
};
|
|
45
|
-
const many = async (text, params) => {
|
|
46
|
-
const { rows } = await sql.query(text, params);
|
|
47
|
-
return rows.map(toRecord);
|
|
89
|
+
return rows[0] ? toConnection(rows[0]) : null;
|
|
48
90
|
};
|
|
49
91
|
return {
|
|
50
92
|
async upsertGrant(g) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
refresh_locked_until = NULL,
|
|
69
|
-
refresh_lock_token = NULL,
|
|
70
|
-
last_refreshed_at = EXCLUDED.last_refreshed_at,
|
|
71
|
-
updated_at = EXCLUDED.updated_at
|
|
72
|
-
RETURNING ${COLUMNS}`, [
|
|
93
|
+
const { rows } = await sql.query(`WITH connection AS (
|
|
94
|
+
INSERT INTO bexio_connections (bexio_company_id, bexio_user_id,
|
|
95
|
+
bexio_company_user_id, company_name, user_name, user_email, role, status,
|
|
96
|
+
access_token, refresh_token, access_expires_at, refresh_expires_at, scope,
|
|
97
|
+
refresh_locked_until, refresh_lock_token, authorized_at, last_refreshed_at,
|
|
98
|
+
created_at, updated_at)
|
|
99
|
+
VALUES ($2, $3, $4, $5, $6, $7, $8, 'active', $9, $10, $11, $12, $13, NULL, NULL, $15, $14, $14, $14)
|
|
100
|
+
ON CONFLICT (bexio_company_id, bexio_user_id) DO UPDATE SET
|
|
101
|
+
${NEWER_AUTHORIZATION_WINS}
|
|
102
|
+
RETURNING ${CONNECTION_COLUMNS}
|
|
103
|
+
), g AS (
|
|
104
|
+
INSERT INTO bexio_grants (owner_id, connection_id, created_at)
|
|
105
|
+
SELECT $1, id, $14 FROM connection
|
|
106
|
+
ON CONFLICT (owner_id, connection_id) DO UPDATE SET owner_id = EXCLUDED.owner_id
|
|
107
|
+
RETURNING id, owner_id, created_at
|
|
108
|
+
)
|
|
109
|
+
SELECT ${GRANT_COLUMNS} FROM g, connection c`, [
|
|
73
110
|
g.ownerId,
|
|
74
111
|
g.companyId,
|
|
75
112
|
g.userId,
|
|
@@ -84,23 +121,64 @@ export function postgresStore(sql) {
|
|
|
84
121
|
g.refreshExpiresAt,
|
|
85
122
|
g.scope,
|
|
86
123
|
g.now,
|
|
124
|
+
g.authorizedAt,
|
|
87
125
|
]);
|
|
88
|
-
if (!
|
|
89
|
-
throw new
|
|
90
|
-
|
|
126
|
+
if (!rows[0]) {
|
|
127
|
+
throw new BexioOAuthError("invalid_config", "SqlClient returned no row for INSERT … RETURNING; check the driver wrapper");
|
|
128
|
+
}
|
|
129
|
+
return toGrant(rows[0]);
|
|
91
130
|
},
|
|
92
131
|
async findGrants({ ownerId, companyId, grantId }) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
132
|
+
if (grantId !== undefined && !UUID.test(grantId))
|
|
133
|
+
return [];
|
|
134
|
+
const { rows } = await sql.query(`SELECT ${GRANT_COLUMNS}
|
|
135
|
+
FROM bexio_grants g JOIN bexio_connections c ON c.id = g.connection_id
|
|
136
|
+
WHERE g.owner_id = $1
|
|
137
|
+
AND ($2::text IS NULL OR c.bexio_company_id = $2)
|
|
138
|
+
AND ($3::uuid IS NULL OR g.id = $3::uuid)
|
|
139
|
+
ORDER BY g.created_at, g.id`, [ownerId, companyId ?? null, grantId ?? null]);
|
|
140
|
+
return rows.map(toGrant);
|
|
141
|
+
},
|
|
142
|
+
async deleteGrant(id) {
|
|
143
|
+
if (!UUID.test(id))
|
|
144
|
+
return;
|
|
145
|
+
await sql.query(`DELETE FROM bexio_grants WHERE id = $1`, [id]);
|
|
98
146
|
},
|
|
99
|
-
async
|
|
100
|
-
|
|
147
|
+
async getConnection(id) {
|
|
148
|
+
if (!UUID.test(id))
|
|
149
|
+
return null;
|
|
150
|
+
return connection(`SELECT ${CONNECTION_COLUMNS} FROM bexio_connections WHERE id = $1`, [id]);
|
|
151
|
+
},
|
|
152
|
+
// NOT EXISTS reads the statement's snapshot, so it can miss a grant that
|
|
153
|
+
// a concurrent upsertGrant commits meanwhile. The foreign key catches
|
|
154
|
+
// that case: it checks against committed data and fails the delete.
|
|
155
|
+
async deleteUnusedConnection(id) {
|
|
156
|
+
if (!UUID.test(id))
|
|
157
|
+
return null;
|
|
158
|
+
try {
|
|
159
|
+
const { rows } = await sql.query(`DELETE FROM bexio_connections c
|
|
160
|
+
WHERE c.id = $1
|
|
161
|
+
AND NOT EXISTS (SELECT 1 FROM bexio_grants g WHERE g.connection_id = c.id)
|
|
162
|
+
RETURNING ${C_COLUMNS}, EXISTS (
|
|
163
|
+
SELECT 1 FROM bexio_connections o
|
|
164
|
+
WHERE o.bexio_user_id = c.bexio_user_id AND o.id <> c.id
|
|
165
|
+
) AS user_has_other_connections`, [id]);
|
|
166
|
+
const row = rows[0];
|
|
167
|
+
return row
|
|
168
|
+
? {
|
|
169
|
+
connection: toConnection(row),
|
|
170
|
+
userHasOtherConnections: row.user_has_other_connections,
|
|
171
|
+
}
|
|
172
|
+
: null;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (error.code === FOREIGN_KEY_VIOLATION)
|
|
176
|
+
return null;
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
101
179
|
},
|
|
102
180
|
async acquireRefreshLock({ id, now, lockUntil, lockToken, accessExpiresBefore, refreshedBefore, }) {
|
|
103
|
-
return
|
|
181
|
+
return connection(`UPDATE bexio_connections SET refresh_locked_until = $3, refresh_lock_token = $6
|
|
104
182
|
WHERE id = $1
|
|
105
183
|
AND status = 'active'
|
|
106
184
|
AND (refresh_locked_until IS NULL OR refresh_locked_until <= $2)
|
|
@@ -108,7 +186,7 @@ export function postgresStore(sql) {
|
|
|
108
186
|
($4::timestamptz IS NOT NULL AND (access_expires_at IS NULL OR access_expires_at <= $4))
|
|
109
187
|
OR ($5::timestamptz IS NOT NULL AND last_refreshed_at < $5)
|
|
110
188
|
)
|
|
111
|
-
RETURNING ${
|
|
189
|
+
RETURNING ${CONNECTION_COLUMNS}`, [
|
|
112
190
|
id,
|
|
113
191
|
now,
|
|
114
192
|
lockUntil,
|
|
@@ -120,7 +198,7 @@ export function postgresStore(sql) {
|
|
|
120
198
|
// Holder writes are fenced by refresh_lock_token: a holder whose lock
|
|
121
199
|
// expired or was cleared by a reconnect matches no row and changes nothing.
|
|
122
200
|
async saveRefreshed(id, lockToken, r) {
|
|
123
|
-
return
|
|
201
|
+
return connection(`UPDATE bexio_connections SET
|
|
124
202
|
access_token = $2,
|
|
125
203
|
refresh_token = COALESCE($3, refresh_token),
|
|
126
204
|
access_expires_at = $4,
|
|
@@ -133,7 +211,7 @@ export function postgresStore(sql) {
|
|
|
133
211
|
last_refreshed_at = $8,
|
|
134
212
|
updated_at = $8
|
|
135
213
|
WHERE id = $1 AND refresh_lock_token = $9
|
|
136
|
-
RETURNING ${
|
|
214
|
+
RETURNING ${CONNECTION_COLUMNS}`, [
|
|
137
215
|
id,
|
|
138
216
|
r.accessToken,
|
|
139
217
|
r.refreshToken,
|
|
@@ -146,22 +224,22 @@ export function postgresStore(sql) {
|
|
|
146
224
|
]);
|
|
147
225
|
},
|
|
148
226
|
async markReauthRequired(id, lockToken, now) {
|
|
149
|
-
|
|
227
|
+
return connection(`UPDATE bexio_connections SET status = 'reauth_required',
|
|
150
228
|
refresh_locked_until = NULL, refresh_lock_token = NULL, updated_at = $2
|
|
151
|
-
WHERE id = $1 AND refresh_lock_token = $3
|
|
229
|
+
WHERE id = $1 AND refresh_lock_token = $3
|
|
230
|
+
RETURNING ${CONNECTION_COLUMNS}`, [id, now, lockToken]);
|
|
152
231
|
},
|
|
153
232
|
async releaseRefreshLock(id, lockToken) {
|
|
154
|
-
await sql.query(`UPDATE
|
|
233
|
+
await sql.query(`UPDATE bexio_connections SET refresh_locked_until = NULL, refresh_lock_token = NULL
|
|
155
234
|
WHERE id = $1 AND refresh_lock_token = $2`, [id, lockToken]);
|
|
156
235
|
},
|
|
157
|
-
async
|
|
158
|
-
await sql.query(`
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
WHERE status = 'active' AND last_refreshed_at < $1
|
|
163
|
-
ORDER BY last_refreshed_at
|
|
236
|
+
async listIdleConnections({ refreshedBefore, limit }) {
|
|
237
|
+
const { rows } = await sql.query(`SELECT ${CONNECTION_COLUMNS} FROM bexio_connections c
|
|
238
|
+
WHERE c.status = 'active' AND c.last_refreshed_at < $1
|
|
239
|
+
AND EXISTS (SELECT 1 FROM bexio_grants g WHERE g.connection_id = c.id)
|
|
240
|
+
ORDER BY c.last_refreshed_at
|
|
164
241
|
LIMIT $2`, [refreshedBefore, limit]);
|
|
242
|
+
return rows.map(toConnection);
|
|
165
243
|
},
|
|
166
244
|
};
|
|
167
245
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"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"}
|
package/dist/store/types.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { Role } from "../permissions.js";
|
|
2
2
|
export type GrantStatus = "active" | "reauth_required";
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* A stored connection: one Bexio identity (company, user) and the tokens of
|
|
5
|
+
* its refresh token chain, shared by every owner that connected it. Token
|
|
6
|
+
* fields hold JWE ciphertext, never plaintext.
|
|
7
|
+
*/
|
|
8
|
+
export interface ConnectionRecord {
|
|
5
9
|
id: string;
|
|
6
|
-
ownerId: string;
|
|
7
10
|
companyId: string;
|
|
8
11
|
userId: string;
|
|
9
12
|
companyUserId: number | null;
|
|
@@ -18,11 +21,26 @@ export interface GrantRecord {
|
|
|
18
21
|
refreshExpiresAt: Date | null;
|
|
19
22
|
scope: string | null;
|
|
20
23
|
refreshLockedUntil: Date | null;
|
|
24
|
+
/** When Bexio issued the authorization the tokens belong to. */
|
|
25
|
+
authorizedAt: Date;
|
|
21
26
|
lastRefreshedAt: Date;
|
|
22
27
|
createdAt: Date;
|
|
23
28
|
updatedAt: Date;
|
|
24
29
|
}
|
|
25
|
-
/**
|
|
30
|
+
/** A stored grant: one owner's access to a connection. */
|
|
31
|
+
export interface GrantRecord {
|
|
32
|
+
id: string;
|
|
33
|
+
ownerId: string;
|
|
34
|
+
createdAt: Date;
|
|
35
|
+
connection: ConnectionRecord;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Upsert of the connection for (company, user) and of the grant for (owner,
|
|
39
|
+
* that connection). The grant is always linked. The connection takes the new
|
|
40
|
+
* tokens (status `active`, lock cleared) unless it already holds a newer
|
|
41
|
+
* authorization: Bexio invalidates older chains, so a slow connect that
|
|
42
|
+
* authorized earlier must not overwrite a later one.
|
|
43
|
+
*/
|
|
26
44
|
export interface GrantUpsert {
|
|
27
45
|
ownerId: string;
|
|
28
46
|
companyId: string;
|
|
@@ -37,9 +55,11 @@ export interface GrantUpsert {
|
|
|
37
55
|
accessExpiresAt: Date | null;
|
|
38
56
|
refreshExpiresAt: Date | null;
|
|
39
57
|
scope: string | null;
|
|
58
|
+
/** When Bexio issued this authorization (the access token's `iat`). */
|
|
59
|
+
authorizedAt: Date;
|
|
40
60
|
now: Date;
|
|
41
61
|
}
|
|
42
|
-
export interface
|
|
62
|
+
export interface ConnectionRefresh {
|
|
43
63
|
accessToken: string;
|
|
44
64
|
/** `null` keeps the stored refresh token (the IdP didn't rotate it). */
|
|
45
65
|
refreshToken: string | null;
|
|
@@ -53,6 +73,7 @@ export interface GrantRefresh {
|
|
|
53
73
|
now: Date;
|
|
54
74
|
}
|
|
55
75
|
export interface LockRequest {
|
|
76
|
+
/** The connection to lock. */
|
|
56
77
|
id: string;
|
|
57
78
|
now: Date;
|
|
58
79
|
lockUntil: Date;
|
|
@@ -64,42 +85,61 @@ export interface LockRequest {
|
|
|
64
85
|
lockToken: string;
|
|
65
86
|
/** Lock only if the access token expires before this instant (lazy refresh)… */
|
|
66
87
|
accessExpiresBefore?: Date;
|
|
67
|
-
/** …or only if the
|
|
88
|
+
/** …or only if the connection was last refreshed before this instant (idle sweep). */
|
|
68
89
|
refreshedBefore?: Date;
|
|
69
90
|
}
|
|
70
91
|
/**
|
|
71
|
-
* Persistence for grants. Every method must be atomic on its
|
|
72
|
-
* library never needs multi-statement transactions.
|
|
92
|
+
* Persistence for connections and grants. Every method must be atomic on its
|
|
93
|
+
* own: the library never needs multi-statement transactions.
|
|
73
94
|
*/
|
|
74
95
|
export interface GrantStore {
|
|
96
|
+
/** See `GrantUpsert`. Returns the grant with its connection. */
|
|
75
97
|
upsertGrant(input: GrantUpsert): Promise<GrantRecord>;
|
|
98
|
+
/** The owner's grants with their connections, oldest first. */
|
|
76
99
|
findGrants(query: {
|
|
77
100
|
ownerId: string;
|
|
78
101
|
companyId?: string;
|
|
79
102
|
grantId?: string;
|
|
80
103
|
}): Promise<GrantRecord[]>;
|
|
81
|
-
|
|
104
|
+
/** Deletes one grant; its connection stays. */
|
|
105
|
+
deleteGrant(id: string): Promise<void>;
|
|
106
|
+
getConnection(id: string): Promise<ConnectionRecord | null>;
|
|
82
107
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
108
|
+
* Deletes the connection if no grant references it any more, and returns
|
|
109
|
+
* the deleted row, plus whether the same Bexio user still has a connection
|
|
110
|
+
* (for another company). Returns `null` (and changes nothing) while a grant
|
|
111
|
+
* still references it, including one added concurrently.
|
|
87
112
|
*/
|
|
88
|
-
|
|
113
|
+
deleteUnusedConnection(id: string): Promise<{
|
|
114
|
+
connection: ConnectionRecord;
|
|
115
|
+
userHasOtherConnections: boolean;
|
|
116
|
+
} | null>;
|
|
117
|
+
/**
|
|
118
|
+
* Takes the refresh lock if the connection is active, not locked (or the
|
|
119
|
+
* lock expired), and still matches the `accessExpiresBefore` /
|
|
120
|
+
* `refreshedBefore` condition. Returns the locked row, or `null` if someone
|
|
121
|
+
* else holds the lock or the connection no longer needs a refresh.
|
|
122
|
+
*/
|
|
123
|
+
acquireRefreshLock(request: LockRequest): Promise<ConnectionRecord | null>;
|
|
89
124
|
/**
|
|
90
125
|
* Stores refreshed tokens, sets `lastRefreshedAt`, and releases the lock.
|
|
91
126
|
* Returns `null` (and changes nothing) unless `lockToken` still holds it.
|
|
92
127
|
*/
|
|
93
|
-
saveRefreshed(id: string, lockToken: string, refresh:
|
|
94
|
-
/**
|
|
95
|
-
|
|
128
|
+
saveRefreshed(id: string, lockToken: string, refresh: ConnectionRefresh): Promise<ConnectionRecord | null>;
|
|
129
|
+
/**
|
|
130
|
+
* Sets `status = reauth_required` and releases the lock. Returns `null`
|
|
131
|
+
* (and changes nothing) unless `lockToken` still holds it.
|
|
132
|
+
*/
|
|
133
|
+
markReauthRequired(id: string, lockToken: string, now: Date): Promise<ConnectionRecord | null>;
|
|
96
134
|
/** Releases the lock, if `lockToken` holds it. */
|
|
97
135
|
releaseRefreshLock(id: string, lockToken: string): Promise<void>;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Active connections that at least one grant references, last refreshed
|
|
138
|
+
* before `refreshedBefore`, oldest first.
|
|
139
|
+
*/
|
|
140
|
+
listIdleConnections(query: {
|
|
101
141
|
refreshedBefore: Date;
|
|
102
142
|
limit: number;
|
|
103
|
-
}): Promise<
|
|
143
|
+
}): Promise<ConnectionRecord[]>;
|
|
104
144
|
}
|
|
105
145
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,iBAAiB,CAAC;AAEvD
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { Keyring } from "./crypto.js";
|
|
2
|
+
import type { Duration } from "./duration.js";
|
|
3
|
+
import type { Role } from "./permissions.js";
|
|
4
|
+
import type { GrantStatus, GrantStore } from "./store/types.js";
|
|
5
|
+
export interface BexioOAuthConfig {
|
|
6
|
+
/** From your Bexio OAuth app. */
|
|
7
|
+
clientId: string;
|
|
8
|
+
clientSecret: string;
|
|
9
|
+
/** Must exactly match a redirect URI registered at Bexio. */
|
|
10
|
+
redirectUri: string;
|
|
11
|
+
/** Bexio API scopes your app needs; `BASE_SCOPES` are always added. */
|
|
12
|
+
scopes?: string[];
|
|
13
|
+
/** Encryption keyring (see `generateKey`). */
|
|
14
|
+
keys: Keyring;
|
|
15
|
+
store: GrantStore;
|
|
16
|
+
/** Defaults to Bexio's production issuer. Override only to point at a mock. */
|
|
17
|
+
issuer?: string;
|
|
18
|
+
/** Defaults to `https://api.bexio.com`. Override only to point at a mock. */
|
|
19
|
+
apiBaseUrl?: string;
|
|
20
|
+
/** How long one refresh may hold a connection's lock. Default `"30s"`. */
|
|
21
|
+
refreshLockTtl?: Duration;
|
|
22
|
+
/** Where `callback` redirects when the flow cookie is gone. Default `"/"`. */
|
|
23
|
+
defaultReturnTo?: string;
|
|
24
|
+
/** Custom `fetch` for every outgoing request (tests, proxies). */
|
|
25
|
+
fetch?: typeof fetch;
|
|
26
|
+
/** Clock override for tests. */
|
|
27
|
+
clock?: () => Date;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A grant as the app sees it: one owner's access to a connection, without
|
|
31
|
+
* token material. Grants of the same Bexio identity share one connection,
|
|
32
|
+
* so they share its tokens, role and status.
|
|
33
|
+
*/
|
|
34
|
+
export interface Grant {
|
|
35
|
+
id: string;
|
|
36
|
+
ownerId: string;
|
|
37
|
+
/** Shared by every grant of the same Bexio company and user. */
|
|
38
|
+
connectionId: string;
|
|
39
|
+
companyId: string;
|
|
40
|
+
companyName: string | null;
|
|
41
|
+
userId: string;
|
|
42
|
+
userName: string | null;
|
|
43
|
+
userEmail: string | null;
|
|
44
|
+
companyUserId: number | null;
|
|
45
|
+
role: Role;
|
|
46
|
+
status: GrantStatus;
|
|
47
|
+
scope: string | null;
|
|
48
|
+
accessExpiresAt: Date | null;
|
|
49
|
+
lastRefreshedAt: Date;
|
|
50
|
+
/** When this owner connected. */
|
|
51
|
+
createdAt: Date;
|
|
52
|
+
/** When the connection last changed. */
|
|
53
|
+
updatedAt: Date;
|
|
54
|
+
}
|
|
55
|
+
/** Identifies one grant of an owner. */
|
|
56
|
+
export interface GrantSelector {
|
|
57
|
+
ownerId: string;
|
|
58
|
+
companyId?: string;
|
|
59
|
+
grantId?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface AccessToken {
|
|
62
|
+
accessToken: string;
|
|
63
|
+
expiresAt: Date | null;
|
|
64
|
+
grant: Grant;
|
|
65
|
+
}
|
|
66
|
+
/** Tokens obtained by your auth framework, for `saveGrant`. */
|
|
67
|
+
export interface ExternalTokens {
|
|
68
|
+
accessToken: string;
|
|
69
|
+
refreshToken?: string | null;
|
|
70
|
+
idToken?: string | null;
|
|
71
|
+
/** Access-token expiry; defaults to the access token's `exp` claim. */
|
|
72
|
+
expiresAt?: Date | null;
|
|
73
|
+
scope?: string | null;
|
|
74
|
+
}
|
|
75
|
+
/** Counts connections, not grants: owners of one Bexio identity share one. */
|
|
76
|
+
export interface RefreshIdleResult {
|
|
77
|
+
refreshed: number;
|
|
78
|
+
/** Idle connections another process was refreshing at the same time. */
|
|
79
|
+
skipped: number;
|
|
80
|
+
reauthRequired: number;
|
|
81
|
+
failed: number;
|
|
82
|
+
/**
|
|
83
|
+
* Idle connections not attempted because `timeBudget` ran out (0 when every
|
|
84
|
+
* idle connection of this run was attempted). The next run picks them up
|
|
85
|
+
* first.
|
|
86
|
+
*/
|
|
87
|
+
remaining: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* How the callback checks the owner. Either the `ownerId` your app derives
|
|
91
|
+
* from the signed-in session (must equal the one passed to `start`), or
|
|
92
|
+
* `authorizeOwner`, which receives the owner recorded at `start` and returns
|
|
93
|
+
* whether the signed-in user may connect Bexio for it. Use the latter when
|
|
94
|
+
* one callback URL serves many owners (e.g. workspaces).
|
|
95
|
+
*/
|
|
96
|
+
export type CallbackOptions = {
|
|
97
|
+
ownerId: string;
|
|
98
|
+
} | {
|
|
99
|
+
authorizeOwner: (ownerId: string) => boolean | Promise<boolean>;
|
|
100
|
+
};
|
|
101
|
+
export interface CallbackResult {
|
|
102
|
+
grant: Grant;
|
|
103
|
+
returnTo: string;
|
|
104
|
+
/** Contains the `Set-Cookie` that deletes the flow cookie; copy it onto your response. */
|
|
105
|
+
headers: Headers;
|
|
106
|
+
}
|
|
107
|
+
/** The client returned by `createBexioOAuth`. */
|
|
108
|
+
export interface BexioOAuth {
|
|
109
|
+
/**
|
|
110
|
+
* Starts the Connect flow: returns a redirect to Bexio that sets the
|
|
111
|
+
* encrypted flow cookie. Derive `ownerId` from your authenticated session.
|
|
112
|
+
* `returnTo` must be a relative path (default: `defaultReturnTo`).
|
|
113
|
+
*/
|
|
114
|
+
start(options: {
|
|
115
|
+
ownerId: string;
|
|
116
|
+
returnTo?: string;
|
|
117
|
+
}): Promise<Response>;
|
|
118
|
+
/**
|
|
119
|
+
* Handles the redirect back from Bexio, saves the grant, and redirects to
|
|
120
|
+
* `returnTo` (or `returnTo?bexio_error=<code>` on failure). Only
|
|
121
|
+
* `invalid_config` and non-library errors are thrown.
|
|
122
|
+
*/
|
|
123
|
+
callback(request: Request, options: CallbackOptions): Promise<Response>;
|
|
124
|
+
/**
|
|
125
|
+
* Handles the redirect back from Bexio and saves the grant. Throws a
|
|
126
|
+
* `BexioOAuthError` on failure; use `callback` for a ready-made redirect.
|
|
127
|
+
*/
|
|
128
|
+
handleCallback(request: Request, options: CallbackOptions): Promise<CallbackResult>;
|
|
129
|
+
/**
|
|
130
|
+
* Saves tokens obtained by your auth framework ("Sign in with Bexio").
|
|
131
|
+
* Verifies the tokens' signatures against Bexio's keys first.
|
|
132
|
+
*/
|
|
133
|
+
saveGrant(input: {
|
|
134
|
+
ownerId: string;
|
|
135
|
+
tokens: ExternalTokens;
|
|
136
|
+
}): Promise<Grant>;
|
|
137
|
+
/**
|
|
138
|
+
* A valid access token for the owner's grant, refreshed when needed.
|
|
139
|
+
* Safe to call from background jobs and from many instances at once.
|
|
140
|
+
*/
|
|
141
|
+
getAccessToken(selector: GrantSelector): Promise<AccessToken>;
|
|
142
|
+
/** The owner's grants, without token material. */
|
|
143
|
+
listGrants(query: {
|
|
144
|
+
ownerId: string;
|
|
145
|
+
}): Promise<Grant[]>;
|
|
146
|
+
/**
|
|
147
|
+
* Deletes the owner's grant. When it was the last grant of its connection,
|
|
148
|
+
* also deletes the connection and revokes its refresh token at Bexio (best
|
|
149
|
+
* effort). Other owners of the same Bexio identity keep working.
|
|
150
|
+
*/
|
|
151
|
+
disconnect(selector: GrantSelector): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Refreshes active connections not refreshed for `idleFor`, so Bexio
|
|
154
|
+
* doesn't end their offline sessions. Run it on a schedule. `limit`
|
|
155
|
+
* (default 100) caps the connections one run looks at; `timeBudget` stops
|
|
156
|
+
* the run before the next connection once that much time has passed.
|
|
157
|
+
*/
|
|
158
|
+
refreshIdle(options: {
|
|
159
|
+
idleFor: Duration;
|
|
160
|
+
limit?: number;
|
|
161
|
+
timeBudget?: Duration;
|
|
162
|
+
}): Promise<RefreshIdleResult>;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|