@milaboratories/pl-client 3.8.1 → 3.9.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.
@@ -65,11 +65,20 @@ export function plAddressToTestConfig(address: string): PlClientConfig {
65
65
  interface AuthCache {
66
66
  /** To check if config changed */
67
67
  conf: TestConfig;
68
+ /** Backend's instance ID at the moment the JWT was issued. Restarts that
69
+ * reset state rotate this; the JWT's `iss` claim is bound to it and the
70
+ * backend rejects tokens minted by a different instance. Without this
71
+ * check, a cached JWT from a previous backend run would silently fail
72
+ * the first authenticated call after restart. */
73
+ instanceId: string;
68
74
  expiration: number;
69
75
  authInformation: AuthInformation;
70
76
  }
71
77
 
72
- function saveAuthInfoCallback(tConf: TestConfig): (authInformation: AuthInformation) => void {
78
+ function saveAuthInfoCallback(
79
+ tConf: TestConfig,
80
+ instanceId: string,
81
+ ): (authInformation: AuthInformation) => void {
73
82
  return (authInformation) => {
74
83
  const dst = getFullAuthDataFilePath();
75
84
  const tmpDst = getFullAuthDataFilePath() + randomUUID();
@@ -78,6 +87,7 @@ function saveAuthInfoCallback(tConf: TestConfig): (authInformation: AuthInformat
78
87
  Buffer.from(
79
88
  JSON.stringify({
80
89
  conf: tConf,
90
+ instanceId,
81
91
  authInformation,
82
92
  expiration: inferAuthRefreshTime(authInformation, 24 * 60 * 60),
83
93
  } as AuthCache),
@@ -99,9 +109,19 @@ const cleanAuthInfoCallback = () => {
99
109
  export async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth: AuthOps }> {
100
110
  const tConf = getTestConfig();
101
111
 
112
+ const plConf = plAddressToTestConfig(tConf.address);
113
+ const uClient = await UnauthenticatedPlClient.build(plConf);
114
+ // ll.serverInfo is populated by build()'s ping; instanceId rotates on a
115
+ // backend restart that drops the persisted state.
116
+ const instanceId = uClient.ll.serverInfo.instanceId;
117
+
102
118
  let authInformation: AuthInformation | undefined = undefined;
103
119
 
104
- // try recover from cache
120
+ // Try recover from cache. The cache is keyed by config AND backend
121
+ // instanceId — a backend restart that rotates instanceId invalidates the
122
+ // cached JWT (the token's `iss` claim no longer matches the live
123
+ // instance), so we re-login instead of carrying the dead token through
124
+ // to the first authenticated call.
105
125
  if (fs.existsSync(getFullAuthDataFilePath())) {
106
126
  try {
107
127
  const cache: AuthCache = JSON.parse(
@@ -111,7 +131,8 @@ export async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth:
111
131
  cache.conf.address === tConf.address &&
112
132
  cache.conf.test_user === tConf.test_user &&
113
133
  cache.conf.test_password === tConf.test_password &&
114
- cache.expiration > Date.now()
134
+ cache.expiration > Date.now() &&
135
+ cache.instanceId === instanceId
115
136
  )
116
137
  authInformation = cache.authInformation;
117
138
  } catch {
@@ -120,9 +141,6 @@ export async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth:
120
141
  }
121
142
  }
122
143
 
123
- const plConf = plAddressToTestConfig(tConf.address);
124
- const uClient = await UnauthenticatedPlClient.build(plConf);
125
-
126
144
  const requireAuth = await uClient.requireAuth();
127
145
 
128
146
  if (!requireAuth && (tConf.test_user !== undefined || tConf.test_password !== undefined))
@@ -141,14 +159,14 @@ export async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth:
141
159
  else authInformation = {};
142
160
 
143
161
  // saving cache
144
- saveAuthInfoCallback(tConf)(authInformation);
162
+ saveAuthInfoCallback(tConf, instanceId)(authInformation);
145
163
  }
146
164
 
147
165
  return {
148
166
  conf: plConf,
149
167
  auth: {
150
168
  authInformation,
151
- onUpdate: saveAuthInfoCallback(tConf),
169
+ onUpdate: saveAuthInfoCallback(tConf, instanceId),
152
170
  onAuthError: cleanAuthInfoCallback,
153
171
  onUpdateError: cleanAuthInfoCallback,
154
172
  },