{"record":{"id":"fd6b80b845e5b67d","repo":"hcengineering/platform","slug":"unauthorized-fd6b80","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":"HttpError","httpStatus":401,"severity":"error","filePath":"pods/preview/src/middleware.ts","lineNumber":45,"sourceCode":"\nexport interface RequestWithAuth extends Request {\n  token?: Token\n}\n\nexport const keepAlive = (options: KeepAliveOptions): RequestHandler => {\n  const { timeout, max } = options\n  return (req: Request, res: Response, next: NextFunction) => {\n    res.setHeader('Connection', 'keep-alive')\n    res.setHeader('Keep-Alive', `timeout=${timeout}, max=${max}`)\n    next()\n  }\n}\n\nexport const withAdminAuthorization = (req: RequestWithAuth, res: Response, next: NextFunction): void => {\n  try {\n    const token = extractToken(req.headers)\n    if (token == null || !(token.account === systemAccountUuid || token.extra?.admin === 'true')) {\n      throw new HttpError(401, 'Unauthorized')\n    }\n    req.token = token\n\n    next()\n  } catch (err: any) {\n    next(err)\n  }\n}\n\nexport const withAuthorization = (req: RequestWithAuth, res: Response, next: NextFunction): void => {\n  try {\n    const token = extractToken(req.headers)\n    if (token == null || token.extra?.guest === 'true' || token.extra?.readonly === 'true') {\n      throw new HttpError(401, 'Unauthorized')\n    }\n    req.token = token\n\n    next()","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/pods/preview/src/middleware.ts#L27-L63","documentation":"withAdminAuthorization extracts the auth token from request headers and requires either the system account UUID or extra.admin === 'true'. If no token is present or neither admin condition holds, it throws HttpError 401 'Unauthorized'.","triggerScenarios":"Any request to an admin-protected endpoint where extractToken returns null (missing/invalid Authorization header) or the token's account is not the system account and token.extra.admin !== 'true'.","commonSituations":"Client omitted the Authorization header; expired/invalid token failed extraction; regular user token lacking admin claim calling admin endpoints; service-to-service calls not using the system account token.","solutions":["Send a valid Authorization header with a token that has extra.admin === 'true'","Use the system account token for service-to-service admin calls","Verify the token was issued with the admin claim (re-authenticate/refresh if needed)","Confirm extractToken is parsing your header scheme (Bearer vs raw token) correctly"],"exampleFix":"// before\nfetch('/admin', { method: 'POST' })\n// after\nfetch('/admin', { method: 'POST', headers: { Authorization: 'Bearer ' + adminToken } })","handlingStrategy":"try-catch","validationCode":"const token = parseToken(getAuthorizationHeader())\nconst isAdmin = token != null && (token.account === systemAccountUuid || token.extra?.admin === 'true')\nif (!isAdmin) throw new HttpError(401, 'Unauthorized')","typeGuard":"function isAdminToken (t: unknown): t is { account: string; extra?: { admin?: string } } {\n  const tok = t as any\n  return tok != null && (tok.account === systemAccountUuid || tok.extra?.admin === 'true')\n}","tryCatchPattern":"try {\n  await callAdminEndpoint()\n} catch (err) {\n  if (err.status === 401 || err.message === 'Unauthorized') {\n    await refreshAdminCredentials()\n    // retry once or surface a clear 'admin privileges required' message\n  } else throw err\n}","preventionTips":["Store admin tokens separately from user tokens and pick the right one per endpoint","Check token claims (extra.admin) client-side before calling admin routes","Handle 401 centrally in your HTTP client with token refresh logic","Never omit the Authorization header for admin operations"],"tags":["auth","http","middleware"],"backgroundTag":"unauthorized-401","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}