Skip to main content

Login & JWT (Auth)

Overview

The auth module is the front door for all of cms-web, and it serves two purposes at once.

  1. It provides the endpoints for login, LINE OA selection, token refresh, and token revocation.
  2. It provides the global JWT guard that the router wraps around the authed route group of every module.

The defining characteristic of this system is that login happens in two steps. The first step yields a token not yet bound to any LINE OA; in the second step the user picks an OA and receives a token carrying lineOaId in its claims. That is what lets every tenant-scoped route insist the user has chosen an OA first.

Business Flow

  1. POST /api/auth/login — verifies email and password with bcrypt, then issues an access token and a refresh token. At this stage the token has no lineOaId claim.
  2. cms-web shows the list of LINE OAs the user can access, and the user picks one.
  3. POST /api/auth/login-with-line-oa runs behind the JwtLoginAuth guard, which accepts tokens without lineOaId, and issues a fresh token pair carrying lineOaId, lineOaHash, organizationId, and roleId. If that OA has no lineOaHash yet, the system generates one and writes it back to the line_oa table as a backfill.
  4. Every issued token is cached in the Redis hash h_session:<userId>, storing only the first 15 characters of SHA256(token) in the accessToken and refreshToken fields.
  5. Every request hitting the authed group passes through JwtAuth, which verifies the signature, requires lineOaId, populates CLS, and compares the hash against Redis. A mismatch — because the token was revoked or a newer login superseded it — returns 401 with code APP_001.
  6. POST /api/auth/refresh-access-token runs behind RefreshTokenAuth. It requires a refresh token still present in Redis and an nbf claim before issuing a new access token.
  7. POST /api/auth/revoke-token deletes the h_session:<userId> key, invalidating every token belonging to that user immediately.

Key Files & Functions

The code lives in internal/modules/auth/, comprising controller.go, service.go, guards.go, jwt.go, hash.go, lineoa.go, oaaccess.go, and module.go.

These routes are registered directly from cmd/api/main.go rather than through featureModules, because the module also has to supply the router's shared guards.

MethodRouteHandlerGuard
POST/api/auth/loginm.loginnone (public)
POST/api/auth/login-with-line-oam.loginWithLineOaJwtLoginAuth
POST/api/auth/refresh-access-tokenm.refreshAccessTokenRefreshTokenAuth
POST/api/auth/revoke-tokenm.revokeTokennone (a legacy TODO notes the admin role check is still missing)

Guards exported for other modules (internal/modules/auth/guards.go):

  • (*Module).JwtAuth() — the global guard on the authed group; requires lineOaId
  • (*Module).JwtLoginAuth() — guard for routes reachable before an OA is chosen
  • (*Module).RefreshTokenAuth() — guard for the refresh flow
  • auth.SuperAdmin() — requires roleId to equal 1
  • auth.InternalApiKey() — checks the X-Internal-Key header against the INTERNAL_API_KEY environment variable

Other exported helpers include auth.GenerateLineOaHash, auth.GenerateShortHash, and the app.AuthTokenService interface (IsAllowToken, RevokeToken).

Connections to Other Services

  • Permission — the routes themselves need no permission since they are public, but this module populates the CLS values that the entire permission system depends on.
  • Tablesuser, line_oa, user_line_oa (per-user OA access), and system_role.
  • Redis — the h_session:<userId> hash holding token hashes, namespaced by REDIS_NAMESPACE.
  • Key environment variablesJWT_SECRET (missing it prevents boot) and INTERNAL_API_KEY.
  • Error codes — 401 returns APP_001; a failed refresh returns 400 with APP_002 or APP_003.
  • Related pages — User Permissions & Module Gate, and User Management.