@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.
Files changed (79) hide show
  1. package/README.md +9 -5
  2. package/dist/claims.d.ts +3 -3
  3. package/dist/claims.d.ts.map +1 -1
  4. package/dist/claims.js +5 -5
  5. package/dist/claims.js.map +1 -1
  6. package/dist/client.d.ts +4 -137
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +18 -629
  9. package/dist/client.js.map +1 -1
  10. package/dist/config.d.ts +22 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +57 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/connect-flow.d.ts +15 -0
  15. package/dist/connect-flow.d.ts.map +1 -0
  16. package/dist/connect-flow.js +123 -0
  17. package/dist/connect-flow.js.map +1 -0
  18. package/dist/crypto.d.ts +2 -2
  19. package/dist/crypto.d.ts.map +1 -1
  20. package/dist/crypto.js +20 -15
  21. package/dist/crypto.js.map +1 -1
  22. package/dist/errors.d.ts +1 -1
  23. package/dist/errors.d.ts.map +1 -1
  24. package/dist/external-tokens.d.ts +8 -0
  25. package/dist/external-tokens.d.ts.map +1 -0
  26. package/dist/external-tokens.js +73 -0
  27. package/dist/external-tokens.js.map +1 -0
  28. package/dist/flow-cookie.d.ts +23 -0
  29. package/dist/flow-cookie.d.ts.map +1 -0
  30. package/dist/flow-cookie.js +44 -0
  31. package/dist/flow-cookie.js.map +1 -0
  32. package/dist/grants.d.ts +45 -0
  33. package/dist/grants.d.ts.map +1 -0
  34. package/dist/grants.js +143 -0
  35. package/dist/grants.js.map +1 -0
  36. package/dist/idle-sweep.d.ts +11 -0
  37. package/dist/idle-sweep.d.ts.map +1 -0
  38. package/dist/idle-sweep.js +70 -0
  39. package/dist/idle-sweep.js.map +1 -0
  40. package/dist/index.d.ts +3 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/migrations.generated.d.ts +3 -0
  45. package/dist/migrations.generated.d.ts.map +1 -0
  46. package/dist/migrations.generated.js +56 -0
  47. package/dist/migrations.generated.js.map +1 -0
  48. package/dist/oidc.d.ts +30 -0
  49. package/dist/oidc.d.ts.map +1 -0
  50. package/dist/oidc.js +67 -0
  51. package/dist/oidc.js.map +1 -0
  52. package/dist/refresh.d.ts +30 -0
  53. package/dist/refresh.d.ts.map +1 -0
  54. package/dist/refresh.js +147 -0
  55. package/dist/refresh.js.map +1 -0
  56. package/dist/return-to.d.ts +8 -0
  57. package/dist/return-to.d.ts.map +1 -0
  58. package/dist/return-to.js +31 -0
  59. package/dist/return-to.js.map +1 -0
  60. package/dist/sql.d.ts +6 -1
  61. package/dist/sql.d.ts.map +1 -1
  62. package/dist/sql.js +8 -12
  63. package/dist/sql.js.map +1 -1
  64. package/dist/store/memory.d.ts.map +1 -1
  65. package/dist/store/memory.js +82 -46
  66. package/dist/store/memory.js.map +1 -1
  67. package/dist/store/postgres.d.ts +4 -4
  68. package/dist/store/postgres.d.ts.map +1 -1
  69. package/dist/store/postgres.js +137 -59
  70. package/dist/store/postgres.js.map +1 -1
  71. package/dist/store/types.d.ts +61 -21
  72. package/dist/store/types.d.ts.map +1 -1
  73. package/dist/types.d.ts +164 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/types.js +2 -0
  76. package/dist/types.js.map +1 -0
  77. package/package.json +3 -1
  78. package/sql/0001_bexio_oauth.sql +48 -0
  79. package/sql/0001_bexio_grants.sql +0 -32
@@ -0,0 +1,147 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import * as oidc from "openid-client";
3
+ import { BexioOAuthError, isBexioOAuthError } from "./errors.js";
4
+ import { bindingOf, decryptFor, issue, resolveGrant } from "./grants.js";
5
+ import { describe } from "./oidc.js";
6
+ import { fetchRole } from "./permissions.js";
7
+ /** Refresh a little before the access token really expires. */
8
+ export const EXPIRY_SKEW_MS = 30_000;
9
+ /** Extra time `getAccessToken` waits for a concurrent refresh beyond the lock TTL. */
10
+ const WAIT_GRACE_MS = 5_000;
11
+ /** Absolute expiries from a token response's `expires_in` / `refresh_expires_in`. */
12
+ export function tokenExpiry(tokens, now) {
13
+ const expiresIn = tokens.expiresIn();
14
+ const refreshExpiresIn = Number(tokens.refresh_expires_in);
15
+ return {
16
+ accessExpiresAt: expiresIn === undefined
17
+ ? null
18
+ : new Date(now.getTime() + expiresIn * 1000),
19
+ refreshExpiresAt: Number.isFinite(refreshExpiresIn) && refreshExpiresIn > 0
20
+ ? new Date(now.getTime() + refreshExpiresIn * 1000)
21
+ : null,
22
+ };
23
+ }
24
+ /** True while the access token is valid for longer than the skew. */
25
+ export function isFresh(connection, now) {
26
+ return (connection.accessExpiresAt !== null &&
27
+ connection.accessExpiresAt.getTime() - EXPIRY_SKEW_MS > now.getTime());
28
+ }
29
+ /** A new lock holder's token and lease, starting at `now`. */
30
+ export function lockWindow(ctx, now) {
31
+ return {
32
+ now,
33
+ lockUntil: new Date(now.getTime() + ctx.lockTtlMs),
34
+ lockToken: randomUUID(),
35
+ };
36
+ }
37
+ /**
38
+ * Refreshes a connection whose lock the caller holds (`lockToken`). Every
39
+ * write is fenced by the token: if the lock was lost (expired and taken over,
40
+ * or the connection was reconnected meanwhile), the write changes nothing and
41
+ * this returns `null`: the caller re-reads the connection.
42
+ */
43
+ export async function refreshLocked(ctx, connection, lockToken) {
44
+ const { store, cipher, clock } = ctx;
45
+ if (!connection.refreshToken) {
46
+ if (!(await store.markReauthRequired(connection.id, lockToken, clock())))
47
+ return null;
48
+ throw new BexioOAuthError("reauth_required", "connection has no refresh token; reconnect required");
49
+ }
50
+ let refreshToken;
51
+ try {
52
+ refreshToken = await decryptFor(cipher, connection, connection.refreshToken);
53
+ }
54
+ catch (error) {
55
+ // Usually a keyring mistake (a key removed too early): don't burn the
56
+ // connection, so restoring the key fixes it.
57
+ await store.releaseRefreshLock(connection.id, lockToken);
58
+ throw error;
59
+ }
60
+ let tokens;
61
+ try {
62
+ tokens = await oidc.refreshTokenGrant(await ctx.oidc.config(), refreshToken);
63
+ }
64
+ catch (error) {
65
+ if (error instanceof oidc.ResponseBodyError &&
66
+ error.error === "invalid_grant") {
67
+ // A reconnect during the refresh restarts the chain at Bexio, which
68
+ // rejects this (older) refresh token. If the reconnect's write already
69
+ // landed, it cleared our lock: nothing is marked and the caller reads
70
+ // the new tokens. If it lands later, it revives the connection then.
71
+ if (!(await store.markReauthRequired(connection.id, lockToken, clock())))
72
+ return null;
73
+ throw new BexioOAuthError("reauth_required", "Bexio rejected the refresh token; reconnect required", { cause: describe(error) });
74
+ }
75
+ await store.releaseRefreshLock(connection.id, lockToken);
76
+ if (isBexioOAuthError(error))
77
+ throw error;
78
+ throw new BexioOAuthError("idp_error", "token refresh failed", {
79
+ cause: describe(error),
80
+ });
81
+ }
82
+ const now = clock();
83
+ const binding = bindingOf(connection);
84
+ // null: lock lost while refreshing (someone reconnected, or our lock
85
+ // expired and another caller took over). Their write wins.
86
+ return store.saveRefreshed(connection.id, lockToken, {
87
+ accessToken: await cipher.encrypt(tokens.access_token, binding),
88
+ refreshToken: tokens.refresh_token
89
+ ? await cipher.encrypt(tokens.refresh_token, binding)
90
+ : null,
91
+ ...tokenExpiry(tokens, now),
92
+ scope: tokens.scope ?? null,
93
+ role: await fetchRole(ctx.fetchImpl, ctx.apiBaseUrl, tokens.access_token),
94
+ now,
95
+ });
96
+ }
97
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
98
+ /** `BexioOAuth.getAccessToken`. */
99
+ export async function getAccessToken(ctx, selector) {
100
+ const { store, clock } = ctx;
101
+ const grant = await resolveGrant(store, selector);
102
+ let connection = grant.connection;
103
+ const deadline = clock().getTime() + ctx.lockTtlMs + WAIT_GRACE_MS;
104
+ let wait = 50;
105
+ const reread = async () => {
106
+ const current = await store.getConnection(connection.id);
107
+ if (!current)
108
+ throw new BexioOAuthError("not_connected", "grant was disconnected");
109
+ return current;
110
+ };
111
+ for (;;) {
112
+ if (connection.status === "reauth_required") {
113
+ throw new BexioOAuthError("reauth_required", "grant needs to be reconnected");
114
+ }
115
+ const now = clock();
116
+ if (isFresh(connection, now))
117
+ return issue(ctx.cipher, grant, connection);
118
+ // Every iteration that would do more work is bounded, whether the last
119
+ // one waited for another refresher or lost its own lock.
120
+ if (now.getTime() > deadline) {
121
+ throw new BexioOAuthError("idp_error", "timed out waiting for a concurrent token refresh");
122
+ }
123
+ // Discovery outside the lock, so a cold start doesn't eat into it.
124
+ await ctx.oidc.config();
125
+ const window = lockWindow(ctx, clock());
126
+ const locked = await store.acquireRefreshLock({
127
+ id: connection.id,
128
+ ...window,
129
+ accessExpiresBefore: new Date(now.getTime() + EXPIRY_SKEW_MS),
130
+ });
131
+ if (locked) {
132
+ const refreshed = await refreshLocked(ctx, locked, window.lockToken);
133
+ if (refreshed)
134
+ return issue(ctx.cipher, grant, refreshed);
135
+ // Lost the lock to a reconnect or another refresher: look at their
136
+ // result before anything else (it may be fresh, or reauth_required).
137
+ connection = await reread();
138
+ continue;
139
+ }
140
+ // Someone else (possibly for another owner of this connection) is
141
+ // refreshing, or just did: wait for their result.
142
+ await sleep(wait);
143
+ wait = Math.min(wait * 2, 1_000);
144
+ connection = await reread();
145
+ }
146
+ }
147
+ //# sourceMappingURL=refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../src/refresh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Only same-origin relative paths: blocks open redirects via `returnTo`.
3
+ * Parsed with the URL parser (the same one the redirect will go through), so
4
+ * tricks like `/\t/evil.example` are caught, and normalized to path+query+hash.
5
+ * Returns `fallback` for an empty value; throws `invalid_config` otherwise.
6
+ */
7
+ export declare function safeReturnTo(value: string | undefined, fallback: string): string;
8
+ //# sourceMappingURL=return-to.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"return-to.d.ts","sourceRoot":"","sources":["../src/return-to.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,EAAE,MAAM,GACf,MAAM,CAqBR"}
@@ -0,0 +1,31 @@
1
+ import { BexioOAuthError } from "./errors.js";
2
+ const RETURN_TO_BASE = "https://return-to.invalid";
3
+ /**
4
+ * Only same-origin relative paths: blocks open redirects via `returnTo`.
5
+ * Parsed with the URL parser (the same one the redirect will go through), so
6
+ * tricks like `/\t/evil.example` are caught, and normalized to path+query+hash.
7
+ * Returns `fallback` for an empty value; throws `invalid_config` otherwise.
8
+ */
9
+ export function safeReturnTo(value, fallback) {
10
+ if (!value)
11
+ return fallback;
12
+ const invalid = () => new BexioOAuthError("invalid_config", "returnTo must be a relative path like /settings");
13
+ // eslint-disable-next-line no-control-regex
14
+ if (!value.startsWith("/") || /[\u0000-\u001f\\]/.test(value))
15
+ throw invalid();
16
+ let url;
17
+ try {
18
+ url = new URL(value, RETURN_TO_BASE);
19
+ }
20
+ catch {
21
+ throw invalid();
22
+ }
23
+ if (url.origin !== RETURN_TO_BASE)
24
+ throw invalid();
25
+ // Dot segments can collapse to "//host" (e.g. "/.//evil.example"), which a
26
+ // browser reads as protocol-relative: check the normalized result too.
27
+ if (url.pathname.startsWith("//"))
28
+ throw invalid();
29
+ return `${url.pathname}${url.search}${url.hash}`;
30
+ }
31
+ //# sourceMappingURL=return-to.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"return-to.js","sourceRoot":"","sources":["../src/return-to.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,cAAc,GAAG,2BAA2B,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAyB,EACzB,QAAgB;IAEhB,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC5B,MAAM,OAAO,GAAG,GAAG,EAAE,CACnB,IAAI,eAAe,CACjB,gBAAgB,EAChB,iDAAiD,CAClD,CAAC;IACJ,4CAA4C;IAC5C,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC;QAC3D,MAAM,OAAO,EAAE,CAAC;IAClB,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,cAAc;QAAE,MAAM,OAAO,EAAE,CAAC;IACnD,2EAA2E;IAC3E,uEAAuE;IACvE,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,OAAO,EAAE,CAAC;IACnD,OAAO,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;AACnD,CAAC"}
package/dist/sql.d.ts CHANGED
@@ -4,6 +4,11 @@ export interface Migration {
4
4
  name: string;
5
5
  sql: string;
6
6
  }
7
- /** The schema migrations shipped with this version, in order. */
7
+ /**
8
+ * The schema migrations shipped with this version, in order.
9
+ *
10
+ * The SQL is embedded in the build (generated from `sql/*.sql`), not read from
11
+ * disk, so this works unchanged when a bundler inlines the library.
12
+ */
8
13
  export declare function migrations(): Promise<Migration[]>;
9
14
  //# sourceMappingURL=sql.d.ts.map
package/dist/sql.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAKD,iEAAiE;AACjE,wBAAsB,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAWvD"}
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAEvD"}
package/dist/sql.js CHANGED
@@ -1,15 +1,11 @@
1
- import { readdir, readFile } from "node:fs/promises";
2
- // `sql/` sits next to `src/` and `dist/` in the package.
3
- const SQL_DIR = new URL("../sql/", import.meta.url);
4
- /** The schema migrations shipped with this version, in order. */
1
+ import { MIGRATIONS } from "./migrations.generated.js";
2
+ /**
3
+ * The schema migrations shipped with this version, in order.
4
+ *
5
+ * The SQL is embedded in the build (generated from `sql/*.sql`), not read from
6
+ * disk, so this works unchanged when a bundler inlines the library.
7
+ */
5
8
  export async function migrations() {
6
- const files = (await readdir(SQL_DIR))
7
- .filter((f) => /^\d{4}_.+\.sql$/.test(f))
8
- .sort();
9
- return Promise.all(files.map(async (name) => ({
10
- number: Number.parseInt(name.slice(0, 4), 10),
11
- name,
12
- sql: await readFile(new URL(name, SQL_DIR), "utf8"),
13
- })));
9
+ return MIGRATIONS.map((m) => ({ ...m }));
14
10
  }
15
11
  //# sourceMappingURL=sql.js.map
package/dist/sql.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AASrD,yDAAyD;AACzD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEpD,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,KAAK,GAAG,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;SACnC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACxC,IAAI,EAAE,CAAC;IACV,OAAO,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACzB,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI;QACJ,GAAG,EAAE,MAAM,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC;KACpD,CAAC,CAAC,CACJ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AASvD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/store/memory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,UAAU,EAGX,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAgB,WAAW,IAAI,UAAU,CAgHxC"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/store/memory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAIV,UAAU,EAGX,MAAM,YAAY,CAAC;AASpB;;;GAGG;AACH,wBAAgB,WAAW,IAAI,UAAU,CA0JxC"}
@@ -4,42 +4,86 @@ import { randomUUID } from "node:crypto";
4
4
  * lives in this process only.
5
5
  */
6
6
  export function memoryStore() {
7
- const rows = new Map();
7
+ const connections = new Map();
8
+ const grants = new Map();
8
9
  const lockTokens = new Map();
9
10
  const copy = (row) => ({ ...row });
11
+ const joined = (grant) => ({
12
+ id: grant.id,
13
+ ownerId: grant.ownerId,
14
+ createdAt: grant.createdAt,
15
+ connection: copy(connections.get(grant.connectionId)),
16
+ });
17
+ const held = (id, lockToken) => {
18
+ const row = connections.get(id);
19
+ if (!row || lockTokens.get(id) !== lockToken)
20
+ return null;
21
+ lockTokens.delete(id);
22
+ row.refreshLockedUntil = null;
23
+ return row;
24
+ };
10
25
  return {
11
26
  async upsertGrant(input) {
12
- const existing = [...rows.values()].find((r) => r.ownerId === input.ownerId &&
13
- r.companyId === input.companyId &&
14
- r.userId === input.userId);
15
- const { now, ...fields } = input;
16
- const row = {
17
- id: existing?.id ?? randomUUID(),
18
- ...fields,
19
- status: "active",
20
- refreshLockedUntil: null,
21
- lastRefreshedAt: now,
22
- createdAt: existing?.createdAt ?? now,
23
- updatedAt: now,
24
- };
25
- rows.set(row.id, row);
26
- lockTokens.delete(row.id);
27
- return copy(row);
27
+ const { ownerId, now, ...fields } = input;
28
+ const existing = [...connections.values()].find((c) => c.companyId === input.companyId && c.userId === input.userId);
29
+ // An older authorization than the stored one only links the grant
30
+ // (see GrantUpsert).
31
+ const connection = existing && input.authorizedAt < existing.authorizedAt
32
+ ? existing
33
+ : {
34
+ id: existing?.id ?? randomUUID(),
35
+ ...fields,
36
+ status: "active",
37
+ refreshLockedUntil: null,
38
+ lastRefreshedAt: now,
39
+ createdAt: existing?.createdAt ?? now,
40
+ updatedAt: now,
41
+ };
42
+ if (connection !== existing) {
43
+ connections.set(connection.id, connection);
44
+ lockTokens.delete(connection.id);
45
+ }
46
+ let grant = [...grants.values()].find((g) => g.ownerId === ownerId && g.connectionId === connection.id);
47
+ if (!grant) {
48
+ grant = {
49
+ id: randomUUID(),
50
+ ownerId,
51
+ connectionId: connection.id,
52
+ createdAt: now,
53
+ };
54
+ grants.set(grant.id, grant);
55
+ }
56
+ return joined(grant);
28
57
  },
29
58
  async findGrants({ ownerId, companyId, grantId }) {
30
- return [...rows.values()]
31
- .filter((r) => r.ownerId === ownerId &&
32
- (companyId === undefined || r.companyId === companyId) &&
33
- (grantId === undefined || r.id === grantId))
59
+ return [...grants.values()]
60
+ .filter((g) => g.ownerId === ownerId &&
61
+ (companyId === undefined ||
62
+ connections.get(g.connectionId).companyId === companyId) &&
63
+ (grantId === undefined || g.id === grantId))
34
64
  .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
35
- .map(copy);
65
+ .map(joined);
36
66
  },
37
- async getGrant(id) {
38
- const row = rows.get(id);
67
+ async deleteGrant(id) {
68
+ grants.delete(id);
69
+ },
70
+ async getConnection(id) {
71
+ const row = connections.get(id);
39
72
  return row ? copy(row) : null;
40
73
  },
74
+ async deleteUnusedConnection(id) {
75
+ const row = connections.get(id);
76
+ if (!row || [...grants.values()].some((g) => g.connectionId === id))
77
+ return null;
78
+ connections.delete(id);
79
+ lockTokens.delete(id);
80
+ return {
81
+ connection: copy(row),
82
+ userHasOtherConnections: [...connections.values()].some((c) => c.userId === row.userId),
83
+ };
84
+ },
41
85
  async acquireRefreshLock(req) {
42
- const row = rows.get(req.id);
86
+ const row = connections.get(req.id);
43
87
  if (!row || row.status !== "active")
44
88
  return null;
45
89
  if (row.refreshLockedUntil && row.refreshLockedUntil > req.now)
@@ -56,10 +100,9 @@ export function memoryStore() {
56
100
  return copy(row);
57
101
  },
58
102
  async saveRefreshed(id, lockToken, refresh) {
59
- const row = rows.get(id);
60
- if (!row || lockTokens.get(id) !== lockToken)
103
+ const row = held(id, lockToken);
104
+ if (!row)
61
105
  return null;
62
- lockTokens.delete(id);
63
106
  row.accessToken = refresh.accessToken;
64
107
  row.refreshToken = refresh.refreshToken ?? row.refreshToken;
65
108
  row.accessExpiresAt = refresh.accessExpiresAt;
@@ -67,34 +110,27 @@ export function memoryStore() {
67
110
  row.scope = refresh.scope ?? row.scope;
68
111
  row.role = refresh.role ?? row.role;
69
112
  row.status = "active";
70
- row.refreshLockedUntil = null;
71
113
  row.lastRefreshedAt = refresh.now;
72
114
  row.updatedAt = refresh.now;
73
115
  return copy(row);
74
116
  },
75
117
  async markReauthRequired(id, lockToken, now) {
76
- const row = rows.get(id);
77
- if (!row || lockTokens.get(id) !== lockToken)
78
- return;
79
- lockTokens.delete(id);
118
+ const row = held(id, lockToken);
119
+ if (!row)
120
+ return null;
80
121
  row.status = "reauth_required";
81
- row.refreshLockedUntil = null;
82
122
  row.updatedAt = now;
123
+ return copy(row);
83
124
  },
84
125
  async releaseRefreshLock(id, lockToken) {
85
- const row = rows.get(id);
86
- if (!row || lockTokens.get(id) !== lockToken)
87
- return;
88
- lockTokens.delete(id);
89
- row.refreshLockedUntil = null;
90
- },
91
- async deleteGrant(id) {
92
- rows.delete(id);
93
- lockTokens.delete(id);
126
+ held(id, lockToken);
94
127
  },
95
- async listIdleGrants({ refreshedBefore, limit }) {
96
- return [...rows.values()]
97
- .filter((r) => r.status === "active" && r.lastRefreshedAt < refreshedBefore)
128
+ async listIdleConnections({ refreshedBefore, limit }) {
129
+ const linked = new Set([...grants.values()].map((g) => g.connectionId));
130
+ return [...connections.values()]
131
+ .filter((c) => c.status === "active" &&
132
+ c.lastRefreshedAt < refreshedBefore &&
133
+ linked.has(c.id))
98
134
  .sort((a, b) => a.lastRefreshedAt.getTime() - b.lastRefreshedAt.getTime())
99
135
  .slice(0, limit)
100
136
  .map(copy);
@@ -1 +1 @@
1
- {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/store/memory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/store/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiBzC;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,MAAM,IAAI,GAAG,CAAC,GAAqB,EAAoB,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,CAAC,KAAkB,EAAe,EAAE,CAAC,CAAC;QACnD,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAE,CAAC;KACvD,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,SAAiB,EAAE,EAAE;QAC7C,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAC1D,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtB,GAAG,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC9B,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,WAAW,CAAC,KAAkB;YAClC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,KAAK,CAAC;YAC1C,MAAM,QAAQ,GAAG,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC7C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CACpE,CAAC;YACF,kEAAkE;YAClE,qBAAqB;YACrB,MAAM,UAAU,GACd,QAAQ,IAAI,KAAK,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY;gBACpD,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC;oBACE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,UAAU,EAAE;oBAChC,GAAG,MAAM;oBACT,MAAM,EAAE,QAAQ;oBAChB,kBAAkB,EAAE,IAAI;oBACxB,eAAe,EAAE,GAAG;oBACpB,SAAS,EAAE,QAAQ,EAAE,SAAS,IAAI,GAAG;oBACrC,SAAS,EAAE,GAAG;iBACf,CAAC;YACR,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;gBAC5B,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;gBAC3C,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,YAAY,KAAK,UAAU,CAAC,EAAE,CACjE,CAAC;YACF,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,KAAK,GAAG;oBACN,EAAE,EAAE,UAAU,EAAE;oBAChB,OAAO;oBACP,YAAY,EAAE,UAAU,CAAC,EAAE;oBAC3B,SAAS,EAAE,GAAG;iBACf,CAAC;gBACF,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC9B,CAAC;YACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE;YAC9C,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;iBACxB,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,OAAO,KAAK,OAAO;gBACrB,CAAC,SAAS,KAAK,SAAS;oBACtB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAE,CAAC,SAAS,KAAK,SAAS,CAAC;gBAC3D,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAC9C;iBACA,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;iBAC7D,GAAG,CAAC,MAAM,CAAC,CAAC;QACjB,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,EAAE;YAClB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,EAAE;YACpB,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChC,CAAC;QAED,KAAK,CAAC,sBAAsB,CAAC,EAAE;YAC7B,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,EAAE,CAAC;gBACjE,OAAO,IAAI,CAAC;YACd,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACvB,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACtB,OAAO;gBACL,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC;gBACrB,uBAAuB,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CACrD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,CAC/B;aACF,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,kBAAkB,CAAC,GAAgB;YACvC,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACjD,IAAI,GAAG,CAAC,kBAAkB,IAAI,GAAG,CAAC,kBAAkB,GAAG,GAAG,CAAC,GAAG;gBAC5D,OAAO,IAAI,CAAC;YACd,MAAM,YAAY,GAChB,CAAC,GAAG,CAAC,mBAAmB,KAAK,SAAS;gBACpC,CAAC,GAAG,CAAC,eAAe,KAAK,IAAI;oBAC3B,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC;gBACpD,CAAC,GAAG,CAAC,eAAe,KAAK,SAAS;oBAChC,GAAG,CAAC,eAAe,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;YAC/C,IAAI,CAAC,YAAY;gBAAE,OAAO,IAAI,CAAC;YAC/B,GAAG,CAAC,kBAAkB,GAAG,GAAG,CAAC,SAAS,CAAC;YACvC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;YACtC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,OAA0B;YAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAChC,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtB,GAAG,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;YACtC,GAAG,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,YAAY,CAAC;YAC5D,GAAG,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;YAC9C,GAAG,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,GAAG,CAAC,gBAAgB,CAAC;YACxE,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC;YACvC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC;YACpC,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC;YACtB,GAAG,CAAC,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC;YAClC,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QAED,KAAK,CAAC,kBAAkB,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG;YACzC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAChC,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC;YACtB,GAAG,CAAC,MAAM,GAAG,iBAAiB,CAAC;YAC/B,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC;YACpB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QAED,KAAK,CAAC,kBAAkB,CAAC,EAAE,EAAE,SAAS;YACpC,IAAI,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACtB,CAAC;QAED,KAAK,CAAC,mBAAmB,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE;YAClD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;YACxE,OAAO,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC;iBAC7B,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,MAAM,KAAK,QAAQ;gBACrB,CAAC,CAAC,eAAe,GAAG,eAAe;gBACnC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACnB;iBACA,IAAI,CACH,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,eAAe,CAAC,OAAO,EAAE,CACpE;iBACA,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;iBACf,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -5,13 +5,13 @@ import type { GrantStore } from "./types.js";
5
5
  */
6
6
  export interface SqlClient {
7
7
  query(text: string, params?: unknown[]): Promise<{
8
- rows: any[];
8
+ rows: unknown[];
9
9
  }>;
10
10
  }
11
11
  /**
12
- * Grant store on the `bexio_grants` table (create it with
13
- * `npx @soulcode/bexio-oauth sql`). Every operation is a single statement,
14
- * so it works through transaction-mode poolers and HTTP drivers.
12
+ * Store on the `bexio_connections` and `bexio_grants` tables (create them
13
+ * with `npx @soulcode/bexio-oauth sql`). Every operation is a single
14
+ * statement, so it works through transaction-mode poolers and HTTP drivers.
15
15
  */
16
16
  export declare function postgresStore(sql: SqlClient): GrantStore;
17
17
  //# sourceMappingURL=postgres.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1D;;;GAGG;AACH,MAAM,WAAW,SAAS;IAExB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC,CAAC;CACnE;AAgED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,GAAG,UAAU,CAmKxD"}
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/store/postgres.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiC,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAC;CACvE;AA4HD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,GAAG,UAAU,CA0MxD"}