@cortexkit/common-auth 0.4.4 → 0.4.5

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 (2) hide show
  1. package/dist/store/torn.js +44 -2
  2. package/package.json +1 -1
@@ -30,6 +30,15 @@ import { buildRawRows, CREDENTIAL_STAMP_KEY, credentialDigest, dispatchDigest, e
30
30
  * store leaves the row as it is on disk (`legacy`, unbound) rather than act
31
31
  * on it; a store without the option completes it as before.
32
32
  *
33
+ * A strict store also completes a replace only when its stamp describes what
34
+ * the completed row would send: the stamp's dispatch digest must be that of
35
+ * the credential beside it, with an API key moved to the endpoint the binding
36
+ * names. The lineage digest alone covers the refresh token or API key, so
37
+ * without this check another writer that changed only the access token, the
38
+ * expiry or the endpoint would still have the strict store rewrite the row's
39
+ * epoch, identity and quota before refusing the row as unbound. A store
40
+ * without the option never compared dispatch digests here and still does not.
41
+ *
33
42
  * A write that gives a row its first identity (`recordIdentity`, or a
34
43
  * `rotate` or refresh that learns one) follows the same order: the stamp
35
44
  * naming the identity first, then the config recording it. A crash between
@@ -38,6 +47,26 @@ import { buildRawRows, CREDENTIAL_STAMP_KEY, credentialDigest, dispatchDigest, e
38
47
  * does not record; that is completed forward the same way, by recording the
39
48
  * identity.
40
49
  */
50
+ /**
51
+ * The credential a torn replace leaves once its config write lands: the
52
+ * credential loaded beside the stamp, with the endpoint the stamp's binding
53
+ * names. Only an API key's endpoint lives in the config (an OAuth credential
54
+ * is entirely in the state file, which the replace has already written). A
55
+ * replace that moves a key to a new endpoint leaves the old `baseURL` and
56
+ * `authHeader` in the config until its config write, so the binding's
57
+ * `baseURL` and `authHeader` are the ones the replacement will be sent with.
58
+ */
59
+ function projectedReplacement(credential, binding) {
60
+ if (credential.type !== 'api')
61
+ return credential;
62
+ return {
63
+ ...credential,
64
+ ...(binding.baseURL !== undefined ? { baseURL: binding.baseURL } : {}),
65
+ ...(binding.authHeader !== undefined
66
+ ? { authHeader: binding.authHeader }
67
+ : {}),
68
+ };
69
+ }
41
70
  /** Whether a well-formed stamp was written by a replace. */
42
71
  function isReplaceStamp(stamp) {
43
72
  if (!stamp.binding)
@@ -66,8 +95,21 @@ export function tornStamps(config, state, codec, options = {}) {
66
95
  if (stamp.credentialEpoch > epoch) {
67
96
  if (!isReplaceStamp(stamp))
68
97
  continue;
69
- if (options.requireCredentialStamps && stamp.dispatch === undefined)
70
- continue;
98
+ if (options.requireCredentialStamps) {
99
+ if (stamp.dispatch === undefined)
100
+ continue;
101
+ // `digest` covers only the refresh token or API key, so another
102
+ // writer may still have changed the access token, the expiry or the
103
+ // endpoint the stamp names. `dispatch` covers all of what a request
104
+ // sends; completing the replace when it disagrees would move the
105
+ // row's epoch, identity and quota on a stamp that does not describe
106
+ // the credential. So the replacement as it would be sent (the loaded
107
+ // credential with the binding's endpoint) must have the stamp's
108
+ // dispatch digest; otherwise the row stays as on disk (unbound).
109
+ if (stamp.dispatch !==
110
+ dispatchDigest(projectedReplacement(row.credential, stamp.binding)))
111
+ continue;
112
+ }
71
113
  torn.set(row.id, {
72
114
  kind: 'replace',
73
115
  stamp: { ...stamp, binding: stamp.binding },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/common-auth",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "Shared code for the CortexKit auth plugins: account pool, quota and routing, commands and auth menu, OpenCode 2 hooks, Claustrum custody, and plumbing (loopback RPC, file locks, logger, sidebar state, TUI preferences and build).",
5
5
  "license": "MIT",
6
6
  "repository": {