File size: 1,682 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import { timingSafeEqual } from 'node:crypto';
import { readFileSync, statSync } from 'node:fs';

import { loadOrCreateServerToken, serverTokenPath } from './persistentToken';

export interface TokenStore {
  readonly tokenPath: string;
  getToken(): string;
  isValid(candidate: string): boolean;
  dispose(): Promise<void>;
}

export async function createTokenStore(homeDir: string): Promise<TokenStore> {
  const tokenPath = serverTokenPath(homeDir);
  const initial = await loadOrCreateServerToken(homeDir);
  const initialStat = statSync(tokenPath);
  let cache: { token: string; mtimeMs: number; ino: number } = {
    token: initial,
    mtimeMs: initialStat.mtimeMs,
    ino: initialStat.ino,
  };

  const currentToken = (): string => {
    let st: ReturnType<typeof statSync>;
    try {
      st = statSync(tokenPath);
    } catch {
      return cache.token;
    }
    if (st.mtimeMs === cache.mtimeMs && st.ino === cache.ino) {
      return cache.token;
    }
    if (process.platform !== 'win32' && (st.mode & 0o077) !== 0) {
      return cache.token;
    }
    try {
      const token = readFileSync(tokenPath, 'utf8').trim();
      if (token.length > 0) {
        cache = { token, mtimeMs: st.mtimeMs, ino: st.ino };
      }
    } catch {
    }
    return cache.token;
  };

  return {
    tokenPath,
    getToken: currentToken,
    isValid(candidate: string): boolean {
      const tokenBuf = Buffer.from(currentToken());
      const candidateBuf = Buffer.from(candidate);
      if (candidateBuf.length !== tokenBuf.length) {
        return false;
      }
      return timingSafeEqual(candidateBuf, tokenBuf);
    },
    async dispose(): Promise<void> {
    },
  };
}