Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[Feature] Canopy auth 1/2: local auth — auth config, argon2id login, server-side sessions, API tokens

Đang mở
#14,159 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@hanahmily đang làm issue này rồi.

Từ ngày 23/10/2026.

  • #1431 của @hanahmily — đang mở

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
35/100
Loại issue
Tính năng
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
node.js, typescript, yaml

Hướng nghiên cứu

Start with canopy/server/src/config.ts, canopy/server/src/routes/auth.ts, canopy/server/src/plugins/session.ts, and the cited Horizon BFF files to map the existing auth flow. Then read the Canopy server tests and README. The issue defines acceptance cases for config, password verification, sessions, tokens, and compatibility; done means those cases and the stated build, license, and e2e requirements pass.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

database feature

Summary (executable leaf 1/2 of #14001)

Canopy's local authentication, following SkyWalking Horizon UI (apache/skywalking-horizon-ui main, apps/bff/src/user/{local,sessions,middleware,tokens}.ts, apps/bff/src/config/schema.ts). It has three parts:

  • Part 1: the auth config with the local backend and argon2id passwords.
  • Part 2: a server-side session store with the auth middleware.
  • Part 3: API tokens.

Nothing here needs an identity provider. SSO, break-glass and the design doc are leaf 2/2.

Current behavior (BanyanDB origin/main @ 450c73a3)

  • canopy/server/src/config.ts loadUsers reads CANOPY_USERS (a JSON/YAML file) and throws unless it is a non-empty array. CANOPY_DEV_NOAUTH=true creates admin/admin.
  • canopy/server/src/routes/auth.ts POST /auth/login verifies passwords with bcryptjs. A fixed invalid bcrypt hash keeps the timing of unknown users close to known ones.
  • Roles are admin | readonly (lib/role.ts).

canopy/server/src/plugins/session.ts registers @fastify/secure-session: the whole session (user, role, banyanVersion) is encrypted into the cookie, with httpOnly, sameSite: 'strict', secure: 'auto' and a fixed 8 h maxAge. Logout deletes the cookie but cannot revoke a copied one, and role changes in config only apply after re-login.

Part 1: auth config, local backend, argon2id

  1. Config: an auth section, loaded from CANOPY_AUTH_CONFIG (YAML/JSON file) with CANOPY_AUTH_* env overrides, using Horizon's key names: auth.backend (local | sso, default sso per #14001; this leaf implements local only, and sso fails at startup with "not yet supported" until leaf 2/2 (#14162)) and auth.local.users[] ({username, passwordHash, role}, default []). Validate it with clear startup errors.
  2. No default admin. auth.backend: local with users: [] boots cleanly, and every login is refused. CANOPY_DEV_NOAUTH keeps its current dev-only behavior and stays forbidden in production.
  3. argon2id verification with Horizon's dummy-hash timing equalization: an unknown user still runs one argon2id verify against a fixed dummy hash.
    Implementation constraint: no native module. Use a pure-JS/WASM argon2id (e.g. hash-wasm argon2Verify) that accepts the standard PHC string ($argon2id$v=19$m=…,t=…,p=…$salt$hash) that Horizon's native argon2 produces, so hashes are portable between the two UIs. This keeps the macOS-ARM build that apache/skywalking-banyandb#1389 fixed by dropping platform-pinned native binaries.
  4. Compatibility: CANOPY_USERS keeps working as an alias for auth.local.users. Existing bcrypt ($2a$/$2b$/$2y$) hashes still verify, with one startup WARN per bcrypt user naming the username and saying to rehash with argon2id. Document a one-line way to produce an argon2id hash.
  5. Map roles inside the BFF: Canopy keeps admin / readonly. Accept Horizon's viewer → readonly and maintainer/operator/admin → admin in config, and record the mapping in one place.

Part 2: server-side sessions and middleware

  1. An in-memory session store keyed by a random id (≥128 bits) in a Horizon-style cookie (session.cookieName, default canopy_sid; httpOnly, sameSite: 'strict', secure: session.cookieSecure default true outside dev, path = Canopy base path). Sliding expiry: session.ttlMinutes, default 60, extended on use. The store holds the username only.
  2. Middleware requireAuth, plus requireBrowserSession for browser-only routes. It resolves the principal in Horizon's order, of which only the cookie step exists in this leaf; keep an explicit extension point for the bearer steps of Part 3 and leaf 2/2. It re-resolves the role from the current config on every request, so removing a user or changing a role takes effect without re-login.
  3. POST /auth/logout removes the session server-side; the old cookie value then gets 401.
  4. Keep the routes' response shapes (/auth/login, /auth/session, /auth/logout) unchanged for the web app.

Part 3: API tokens

  1. auth.tokensFile (YAML/JSON): entries {id, username, secretHash, description?}, where secretHash is SHA-256 hex of the secret. Token format hzn_<id>_<secret>, kept verbatim for parity with Horizon.
  2. Authorization: Bearer hzn_… is resolved in Part 2's middleware before the cookie: parse, look up id, compare SHA-256 of the secret in constant time, then resolve the username to a user and its current role. A token names a principal, never a role set.
  3. Role re-resolution for bearers is cached 30 s on success and never cached on failure, as in Horizon. Deleting the local user revokes the token without editing the tokens file, within the cache window.
  4. Tokens never create a cookie session. Routes marked requireBrowserSession (login/logout) reject bearer-only callers.
  5. A small CLI or README recipe to mint a token (prints the token once, writes the hash entry).

Concrete oracle

Part 1
  • auth: {backend: local, local: {users: []}} → server starts; POST /auth/login {alice, x} → 401 invalid_credentials.
  • User alice with argon2id hash of s3cret, role viewer → login s3cret succeeds with role readonly; wrong → 401.
  • User bob with a bcrypt hash → login succeeds, and the log has exactly one WARN about bob.
  • Unknown user → argon2id verify still runs once (spy or counter), and the response matches a wrong-password response.
  • auth.backend: sso → startup error "sso backend not yet supported".
Part 2
  • Login → cookie canopy_sid with the attributes above. GET /auth/session with it → 200 with user and role.
  • Same cookie after logout → 401.
  • Change alice's role from admin to readonly in config (reload hook in tests) → the next write request with the existing session → 403. Remove alice → next request 401.
  • ttlMinutes: 1 with a fake clock: idle 61 s → 401; requests every 50 s keep it alive.
Part 3
  • Valid hzn_t1_<s> for alice (admin) → GET proxy route 200, POST 200. For bob (readonly) → POST 403.
  • Wrong secret, unknown id or malformed token → 401, with the same response for all three.
  • Remove alice from users → after the cache window (fake clock 31 s) → 401. A failed lookup isn't cached: re-adding her works on the next request.
  • A request with both a valid bearer and a cookie for a different user → the bearer's principal wins.

Acceptance criteria

  • Vitest tests in canopy/server/test cover every oracle line through the Fastify app (app.inject), with fake timers for TTL and cache windows and log capture where the oracle checks logs.
  • No native dependency is added. npm ls shows no node-gyp/prebuilt binary package new to the tree. Unused @fastify/secure-session and bcryptjs-only code paths are removed, and the license artifacts are regenerated (make license-dep).
  • Token secrets never appear in logs, and token comparison uses crypto.timingSafeEqual.
  • Existing Canopy server/web tests and the e2e login flow pass. Existing CANOPY_USERS + bcrypt deployments keep logging in.
  • canopy/README.md covers the auth.* and session.* keys and CANOPY_AUTH_CONFIG, the role mapping, how to make an argon2id hash, the tokens file format and token minting, and that sessions don't survive a restart.

Out of scope (leaf 2/2)

SSO (OIDC/OAuth2), break-glass, canopy/docs/auth-design.md, persistent or shared session stores, token expiry/rotation UI.

Ngôn ngữ chính
Java
Star
25k
Fork
6.6k
Merge trung bình
14 giờ 48 phút
Pull request đã merge (30 ngày)
31

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của apache/skywalking

Tất cả issue của apache/skywalking

Issue tương tự

Thêm issue về Java

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.