hcengineering/platform · error

Missing auth info

Error message

Missing auth info

What it means

withLoginInfoAsync rejects with 403 'Missing auth info' when the token exists but the account service returns null from getLoginInfoByToken — i.e. the token is unrecognized, expired, revoked, or the account service is unreachable/misbehaving such that no login info resolves.

Source

Thrown at services/payment/pod-payment/src/middleware.ts:62

    return
  }
  next()
}

export const withLoginInfo = (req: RequestWithAuth, res: Response, next: NextFunction): void => {
  void withLoginInfoAsync(req, res, next)
}

const withLoginInfoAsync = async (req: RequestWithAuth, res: Response, next: NextFunction): Promise<void> => {
  if (req.token === undefined || req.token == null) {
    res.status(401).json({ message: 'Token error' }).end()
    return
  }

  const accountClient = getAccountClient(req.headers.authorization?.split(' ')[1])
  const loginInfo = await accountClient.getLoginInfoByToken()
  if (loginInfo == null) {
    res.status(403).json({ message: 'Missing auth info' }).end()
    return
  }

  req.loginInfo = loginInfo
  next()
}

export const withOwner = (req: RequestWithAuth, res: Response, next: NextFunction): void => {
  void withOwnerAsync(req, res, next)
}

const withOwnerAsync = async (req: RequestWithAuth, res: Response, next: NextFunction): Promise<void> => {
  if (req.token === undefined || req.token == null) {
    res.status(401).json({ message: 'Token error' }).end()
    return
  }
  if (req.params.workspace != null && req.token.workspace !== req.params.workspace) {
    res.status(401).json({ message: 'Workspace mismatch' }).end()

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Refresh the access token (re-authenticate) and retry with a valid bearer token
  2. Check account service availability and the getAccountClient base URL configuration in this pod
  3. Confirm the token was issued by the same environment's account service (issuer/audience match)
  4. Check for clock skew between pods if tokens are rejected immediately after issuance

Example fix

// before
// client retries forever with expired token
requestWithSameToken()
// after
if (isTokenExpired(token)) token = await refreshToken()
requestWithToken(token)
Defensive patterns

Strategy: retry

Validate before calling

// client-side: check token expiry before calling
const payload = JSON.parse(atob(token.split('.')[1]))
if (payload.exp * 1000 <= Date.now()) {
  token = await refreshAccessToken() // avoids 403 Missing auth info from a dead token
}

Type guard

function isUsableToken(token: string | undefined): token is string {
  if (!token) return false
  try {
    const { exp } = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString())
    return typeof exp === 'number' && exp * 1000 > Date.now()
  } catch { return false }
}

Try / catch

try {
  const res = await callApi()
} catch (err) {
  if (err.response?.status === 403 && err.response.data?.message === 'Missing auth info') {
    await refreshAccessToken();
    // retry once, then fail; also check account-service health
  }
}

Prevention

When it happens

Trigger: Expired or revoked bearer token presented to getLoginInfoByToken; token signed for a different environment/account service; account service down or returning null due to internal error; passing a malformed-but-extractable token string.

Common situations: Long-lived clients with expired JWTs that were never refreshed; tokens from a dev account service used against staging payment pod; account service outage or wrong ACCOUNT_SERVICE_URL config; clock skew invalidating freshly issued tokens.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/59fa14818bd368f0. Report an issue: GitHub.