@soulcode/bexio-oauth 0.2.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.
@@ -1,20 +1,53 @@
1
1
  import { BexioOAuthError } from "../errors.js";
2
- // `id` is a uuid column: any other value matches no row, and would make
2
+ // `id` columns are uuids: any other value matches no row, and would make
3
3
  // Postgres reject the cast instead of returning nothing.
4
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 COLUMNS = `id, owner_id, bexio_company_id, bexio_user_id, bexio_company_user_id,
5
+ const CONNECTION_COLUMNS = `id, bexio_company_id, bexio_user_id, bexio_company_user_id,
6
6
  company_name, user_name, user_email, role, status, access_token, refresh_token,
7
7
  access_expires_at, refresh_expires_at, scope, refresh_locked_until,
8
- 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";
9
43
  function date(value) {
10
44
  if (value === null)
11
45
  return null;
12
46
  return value instanceof Date ? value : new Date(value);
13
47
  }
14
- function toRecord(row) {
48
+ function toConnection(row) {
15
49
  return {
16
50
  id: row.id,
17
- ownerId: row.owner_id,
18
51
  companyId: row.bexio_company_id,
19
52
  userId: row.bexio_user_id,
20
53
  companyUserId: row.bexio_company_user_id === null
@@ -31,49 +64,49 @@ function toRecord(row) {
31
64
  refreshExpiresAt: date(row.refresh_expires_at),
32
65
  scope: row.scope,
33
66
  refreshLockedUntil: date(row.refresh_locked_until),
67
+ authorizedAt: date(row.authorized_at),
34
68
  lastRefreshedAt: date(row.last_refreshed_at),
35
69
  createdAt: date(row.created_at),
36
70
  updatedAt: date(row.updated_at),
37
71
  };
38
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
+ }
39
81
  /**
40
- * Grant store on the `bexio_grants` table (create it with
41
- * `npx @soulcode/bexio-oauth sql`). Every operation is a single statement,
42
- * 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.
43
85
  */
44
86
  export function postgresStore(sql) {
45
- const one = async (text, params) => {
46
- const { rows } = await sql.query(text, params);
47
- return rows[0] ? toRecord(rows[0]) : null;
48
- };
49
- const many = async (text, params) => {
87
+ const connection = async (text, params) => {
50
88
  const { rows } = await sql.query(text, params);
51
- return rows.map(toRecord);
89
+ return rows[0] ? toConnection(rows[0]) : null;
52
90
  };
53
91
  return {
54
92
  async upsertGrant(g) {
55
- const row = await one(`INSERT INTO bexio_grants (owner_id, bexio_company_id, bexio_user_id,
56
- bexio_company_user_id, company_name, user_name, user_email, role, status,
57
- access_token, refresh_token, access_expires_at, refresh_expires_at, scope,
58
- refresh_locked_until, last_refreshed_at, created_at, updated_at)
59
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'active', $9, $10, $11, $12, $13, NULL, $14, $14, $14)
60
- ON CONFLICT (owner_id, bexio_company_id, bexio_user_id) DO UPDATE SET
61
- bexio_company_user_id = EXCLUDED.bexio_company_user_id,
62
- company_name = EXCLUDED.company_name,
63
- user_name = EXCLUDED.user_name,
64
- user_email = EXCLUDED.user_email,
65
- role = EXCLUDED.role,
66
- status = 'active',
67
- access_token = EXCLUDED.access_token,
68
- refresh_token = EXCLUDED.refresh_token,
69
- access_expires_at = EXCLUDED.access_expires_at,
70
- refresh_expires_at = EXCLUDED.refresh_expires_at,
71
- scope = EXCLUDED.scope,
72
- refresh_locked_until = NULL,
73
- refresh_lock_token = NULL,
74
- last_refreshed_at = EXCLUDED.last_refreshed_at,
75
- updated_at = EXCLUDED.updated_at
76
- 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`, [
77
110
  g.ownerId,
78
111
  g.companyId,
79
112
  g.userId,
@@ -88,28 +121,64 @@ export function postgresStore(sql) {
88
121
  g.refreshExpiresAt,
89
122
  g.scope,
90
123
  g.now,
124
+ g.authorizedAt,
91
125
  ]);
92
- if (!row) {
126
+ if (!rows[0]) {
93
127
  throw new BexioOAuthError("invalid_config", "SqlClient returned no row for INSERT … RETURNING; check the driver wrapper");
94
128
  }
95
- return row;
129
+ return toGrant(rows[0]);
96
130
  },
97
131
  async findGrants({ ownerId, companyId, grantId }) {
98
132
  if (grantId !== undefined && !UUID.test(grantId))
99
133
  return [];
100
- return many(`SELECT ${COLUMNS} FROM bexio_grants
101
- WHERE owner_id = $1
102
- AND ($2::text IS NULL OR bexio_company_id = $2)
103
- AND ($3::uuid IS NULL OR id = $3::uuid)
104
- ORDER BY created_at, id`, [ownerId, companyId ?? null, grantId ?? null]);
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);
105
141
  },
106
- async getGrant(id) {
142
+ async deleteGrant(id) {
143
+ if (!UUID.test(id))
144
+ return;
145
+ await sql.query(`DELETE FROM bexio_grants WHERE id = $1`, [id]);
146
+ },
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) {
107
156
  if (!UUID.test(id))
108
157
  return null;
109
- return one(`SELECT ${COLUMNS} FROM bexio_grants WHERE id = $1`, [id]);
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
+ }
110
179
  },
111
180
  async acquireRefreshLock({ id, now, lockUntil, lockToken, accessExpiresBefore, refreshedBefore, }) {
112
- return one(`UPDATE bexio_grants SET refresh_locked_until = $3, refresh_lock_token = $6
181
+ return connection(`UPDATE bexio_connections SET refresh_locked_until = $3, refresh_lock_token = $6
113
182
  WHERE id = $1
114
183
  AND status = 'active'
115
184
  AND (refresh_locked_until IS NULL OR refresh_locked_until <= $2)
@@ -117,7 +186,7 @@ export function postgresStore(sql) {
117
186
  ($4::timestamptz IS NOT NULL AND (access_expires_at IS NULL OR access_expires_at <= $4))
118
187
  OR ($5::timestamptz IS NOT NULL AND last_refreshed_at < $5)
119
188
  )
120
- RETURNING ${COLUMNS}`, [
189
+ RETURNING ${CONNECTION_COLUMNS}`, [
121
190
  id,
122
191
  now,
123
192
  lockUntil,
@@ -129,7 +198,7 @@ export function postgresStore(sql) {
129
198
  // Holder writes are fenced by refresh_lock_token: a holder whose lock
130
199
  // expired or was cleared by a reconnect matches no row and changes nothing.
131
200
  async saveRefreshed(id, lockToken, r) {
132
- return one(`UPDATE bexio_grants SET
201
+ return connection(`UPDATE bexio_connections SET
133
202
  access_token = $2,
134
203
  refresh_token = COALESCE($3, refresh_token),
135
204
  access_expires_at = $4,
@@ -142,7 +211,7 @@ export function postgresStore(sql) {
142
211
  last_refreshed_at = $8,
143
212
  updated_at = $8
144
213
  WHERE id = $1 AND refresh_lock_token = $9
145
- RETURNING ${COLUMNS}`, [
214
+ RETURNING ${CONNECTION_COLUMNS}`, [
146
215
  id,
147
216
  r.accessToken,
148
217
  r.refreshToken,
@@ -155,22 +224,22 @@ export function postgresStore(sql) {
155
224
  ]);
156
225
  },
157
226
  async markReauthRequired(id, lockToken, now) {
158
- await sql.query(`UPDATE bexio_grants SET status = 'reauth_required',
227
+ return connection(`UPDATE bexio_connections SET status = 'reauth_required',
159
228
  refresh_locked_until = NULL, refresh_lock_token = NULL, updated_at = $2
160
- WHERE id = $1 AND refresh_lock_token = $3`, [id, now, lockToken]);
229
+ WHERE id = $1 AND refresh_lock_token = $3
230
+ RETURNING ${CONNECTION_COLUMNS}`, [id, now, lockToken]);
161
231
  },
162
232
  async releaseRefreshLock(id, lockToken) {
163
- await sql.query(`UPDATE bexio_grants SET refresh_locked_until = NULL, refresh_lock_token = NULL
233
+ await sql.query(`UPDATE bexio_connections SET refresh_locked_until = NULL, refresh_lock_token = NULL
164
234
  WHERE id = $1 AND refresh_lock_token = $2`, [id, lockToken]);
165
235
  },
166
- async deleteGrant(id) {
167
- await sql.query(`DELETE FROM bexio_grants WHERE id = $1`, [id]);
168
- },
169
- async listIdleGrants({ refreshedBefore, limit }) {
170
- return many(`SELECT ${COLUMNS} FROM bexio_grants
171
- WHERE status = 'active' AND last_refreshed_at < $1
172
- 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
173
241
  LIMIT $2`, [refreshedBefore, limit]);
242
+ return rows.map(toConnection);
174
243
  },
175
244
  };
176
245
  }
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,12 @@
1
1
  import type { Role } from "../permissions.js";
2
2
  export type GrantStatus = "active" | "reauth_required";
3
- /** A stored grant. Token fields hold JWE ciphertext, never plaintext. */
4
- export interface GrantRecord {
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
- /** Insert, or overwrite the tokens of, the grant for (owner, company, user). */
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 GrantRefresh {
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 grant was last refreshed before this instant (idle sweep). */
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 own: the
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
- getGrant(id: string): Promise<GrantRecord | null>;
104
+ /** Deletes one grant; its connection stays. */
105
+ deleteGrant(id: string): Promise<void>;
106
+ getConnection(id: string): Promise<ConnectionRecord | null>;
82
107
  /**
83
- * Takes the refresh lock if the grant is active, not locked (or the lock
84
- * expired), and still matches the `accessExpiresBefore` / `refreshedBefore`
85
- * condition. Returns the locked row, or `null` if someone else holds the
86
- * lock or the grant no longer needs a refresh.
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
- acquireRefreshLock(request: LockRequest): Promise<GrantRecord | null>;
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: GrantRefresh): Promise<GrantRecord | null>;
94
- /** Sets `status = reauth_required` and releases the lock, if `lockToken` holds it. */
95
- markReauthRequired(id: string, lockToken: string, now: Date): Promise<void>;
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
- deleteGrant(id: string): Promise<void>;
99
- /** Active grants last refreshed before `refreshedBefore`, oldest first. */
100
- listIdleGrants(query: {
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<GrantRecord[]>;
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":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -17,7 +17,7 @@ export interface BexioOAuthConfig {
17
17
  issuer?: string;
18
18
  /** Defaults to `https://api.bexio.com`. Override only to point at a mock. */
19
19
  apiBaseUrl?: string;
20
- /** How long one refresh may hold a grant's lock. Default `"30s"`. */
20
+ /** How long one refresh may hold a connection's lock. Default `"30s"`. */
21
21
  refreshLockTtl?: Duration;
22
22
  /** Where `callback` redirects when the flow cookie is gone. Default `"/"`. */
23
23
  defaultReturnTo?: string;
@@ -26,10 +26,16 @@ export interface BexioOAuthConfig {
26
26
  /** Clock override for tests. */
27
27
  clock?: () => Date;
28
28
  }
29
- /** A grant as the app sees it: no token material. */
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
+ */
30
34
  export interface Grant {
31
35
  id: string;
32
36
  ownerId: string;
37
+ /** Shared by every grant of the same Bexio company and user. */
38
+ connectionId: string;
33
39
  companyId: string;
34
40
  companyName: string | null;
35
41
  userId: string;
@@ -41,7 +47,9 @@ export interface Grant {
41
47
  scope: string | null;
42
48
  accessExpiresAt: Date | null;
43
49
  lastRefreshedAt: Date;
50
+ /** When this owner connected. */
44
51
  createdAt: Date;
52
+ /** When the connection last changed. */
45
53
  updatedAt: Date;
46
54
  }
47
55
  /** Identifies one grant of an owner. */
@@ -64,15 +72,17 @@ export interface ExternalTokens {
64
72
  expiresAt?: Date | null;
65
73
  scope?: string | null;
66
74
  }
75
+ /** Counts connections, not grants: owners of one Bexio identity share one. */
67
76
  export interface RefreshIdleResult {
68
77
  refreshed: number;
69
- /** Idle grants another process was refreshing at the same time. */
78
+ /** Idle connections another process was refreshing at the same time. */
70
79
  skipped: number;
71
80
  reauthRequired: number;
72
81
  failed: number;
73
82
  /**
74
- * Idle grants not attempted because `timeBudget` ran out (0 when every idle
75
- * grant of this run was attempted). The next run picks them up first.
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.
76
86
  */
77
87
  remaining: number;
78
88
  }
@@ -133,13 +143,17 @@ export interface BexioOAuth {
133
143
  listGrants(query: {
134
144
  ownerId: string;
135
145
  }): Promise<Grant[]>;
136
- /** Revokes the refresh token at Bexio (best effort) and deletes the 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
+ */
137
151
  disconnect(selector: GrantSelector): Promise<void>;
138
152
  /**
139
- * Refreshes active grants not refreshed for `idleFor`, so Bexio doesn't end
140
- * their offline sessions. Run it on a schedule. `limit` (default 100) caps
141
- * the grants one run looks at; `timeBudget` stops the run before the next
142
- * grant once that much time has passed.
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.
143
157
  */
144
158
  refreshIdle(options: {
145
159
  idleFor: Duration;
@@ -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": "@soulcode/bexio-oauth",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Bexio OAuth token custody for Node apps: connect Bexio accounts, store tokens encrypted in Postgres, get a valid access token anywhere.",
5
5
  "keywords": [
6
6
  "bexio",
@@ -47,7 +47,7 @@
47
47
  "prepack": "npm run build"
48
48
  },
49
49
  "dependencies": {
50
- "jose": "^6.2.13",
50
+ "jose": "^6.2.14",
51
51
  "openid-client": "^6.8.8"
52
52
  },
53
53
  "devDependencies": {
@@ -0,0 +1,48 @@
1
+ -- @soulcode/bexio-oauth 0001: connections and grants.
2
+ -- Token columns hold JWE ciphertext; only your server-side database role
3
+ -- should be able to read these tables.
4
+
5
+ -- One row per Bexio identity (company, user): the tokens of its one refresh
6
+ -- token chain, shared by every owner that connected this identity.
7
+ CREATE TABLE IF NOT EXISTS bexio_connections (
8
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
9
+ bexio_company_id text NOT NULL,
10
+ bexio_user_id text NOT NULL,
11
+ bexio_company_user_id integer,
12
+ company_name text,
13
+ user_name text,
14
+ user_email text,
15
+ role text NOT NULL DEFAULT 'member' CHECK (role IN ('admin', 'member')),
16
+ status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'reauth_required')),
17
+ access_token text NOT NULL,
18
+ refresh_token text,
19
+ access_expires_at timestamptz,
20
+ refresh_expires_at timestamptz,
21
+ scope text,
22
+ refresh_locked_until timestamptz,
23
+ refresh_lock_token text,
24
+ authorized_at timestamptz NOT NULL,
25
+ last_refreshed_at timestamptz NOT NULL DEFAULT now(),
26
+ created_at timestamptz NOT NULL DEFAULT now(),
27
+ updated_at timestamptz NOT NULL DEFAULT now(),
28
+ CONSTRAINT bexio_connections_company_user_key UNIQUE (bexio_company_id, bexio_user_id)
29
+ );
30
+
31
+ -- refreshIdle(): active connections by last refresh.
32
+ CREATE INDEX IF NOT EXISTS bexio_connections_idle_idx
33
+ ON bexio_connections (last_refreshed_at)
34
+ WHERE status = 'active';
35
+
36
+ -- One row per (owner, connection): an owner's access to a connection. A
37
+ -- connection is deleted only once no grant references it (RESTRICT).
38
+ CREATE TABLE IF NOT EXISTS bexio_grants (
39
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
40
+ owner_id text NOT NULL,
41
+ connection_id uuid NOT NULL REFERENCES bexio_connections (id) ON DELETE RESTRICT,
42
+ created_at timestamptz NOT NULL DEFAULT now(),
43
+ CONSTRAINT bexio_grants_owner_connection_key UNIQUE (owner_id, connection_id)
44
+ );
45
+
46
+ -- Disconnect checks whether a connection has grants left.
47
+ CREATE INDEX IF NOT EXISTS bexio_grants_connection_idx
48
+ ON bexio_grants (connection_id);