Files
RenewCMS/features/get_current_user.feature

91 lines
5.8 KiB
Gherkin

Feature: A logged-in user fetches their own public profile without triggering the verified-account gate
As the maintainer of RenewCMS
I want the SPA to ask "who is currently logged in" via a single endpoint that answers from the
same jwt cookie every other protected route already trusts, distinguishes "no valid session"
from "a session for an unverified account" so it can route the user to a pending-verification
screen instead of guessing, and never competes with real login attempts for rate-limit headroom
So that the SPA can restore a session on load without going through the verification gate and
without being throttled like a login attempt
Background:
Given the public user struct and its converter, shared by the auth and user handlers from a
package under api/pkg (features/user_display_name.feature), exactly the JSON shape
POST /v1/auth/verify returns: id, username, email, is_verified, created_at, updated_at,
display_name and role
And this slice adds GET /v1/auth/me, handled by a new MeHandler (api/handlers/auth/meHandler.go)
built with NewMeHandler(container.GetUserUseCase), the same GetUserUseCase every other
protected handler already gets from api/dependecyInjection.go; no new use case or domain
method is introduced
And GET /v1/auth/me is mounted in api/router.go's NewAuthRouter inside the same group that
already carries jwtauth.Verifier(container.Token), jwtauth.Authenticator(container.Token) and
middleware.RequireCurrentToken(container.GetUserUseCase) ahead of /verify and
DELETE /register, so:
- no jwt cookie, or a jwt with a bad signature, is refused with 401 by jwtauth.Authenticator
itself before the handler runs, the same as the other routes in that group
(features/stale_token_invalidation.feature)
- a well-formed but stale token (whose token_version claim no longer matches the stored user,
per features/stale_token_invalidation.feature) is refused with 401
apperr.UnauthorizedErrorMessage by RequireCurrentToken before the handler runs
- a token minted for a user id that no longer exists (a cancelled registration,
features/cancel_pending_registration.feature) is refused with the same 401
apperr.UnauthorizedErrorMessage by RequireCurrentToken, for the same reason
And unlike NewArticleRouter's protected group, this group does not add
middleware.RequireVerifiedUser, and unlike /login and /password-reset, it does not add
middleware.NewRateLimiter, so an unverified user reaches the handler, and the route has no
per-client attempt ceiling
And MeHandler.Me resolves user_id with claims.UserId(r) (401 apperr.UnauthorizedErrorMessage if
somehow absent or malformed, though RequireCurrentToken already guarantees a valid one before
this handler is reached), loads the user with GetUserUseCase.GetUser(userId) (401
apperr.UnauthorizedErrorMessage on user.ErrUserNotFound, 500 "Internal server error" on any
other error), sets the response header Cache-Control: no-store, and responds 200 with
newPublicUser(loadedUser) as JSON, whatever that user's is_verified value is
Scenario: A verified user fetches their own profile
Given a user repository containing user id 7, username "alice", email "alice@example.com",
is_verified true, role "owner", TokenVersion 0
And a valid jwt cookie for user id 7 encoding token_version 0
When the client sends GET /v1/auth/me with that cookie
Then the response status is 200
And the response header Cache-Control is "no-store"
And the response body is the user as JSON with id 7, username "alice", email
"alice@example.com", is_verified true, role "owner"
Scenario: An unverified user still fetches their own profile, with is_verified false, not a 403
Given a user repository containing user id 8, username "bob", email "bob@example.com",
is_verified false, role "owner", TokenVersion 0
And a valid jwt cookie for user id 8 encoding token_version 0
When the client sends GET /v1/auth/me with that cookie
Then the response status is 200
And the response header Cache-Control is "no-store"
And the response body is the user as JSON with id 8, username "bob", email "bob@example.com",
is_verified false, role "owner"
Scenario: No jwt cookie at all is refused
Given a user repository containing user id 7, username "alice", is_verified true, TokenVersion 0
When the client sends GET /v1/auth/me with no jwt cookie
Then the response status is 401
Scenario: A jwt whose token_version no longer matches the stored user is refused
Given a user repository containing user id 7, username "alice", is_verified true, TokenVersion 1
And a jwt cookie for user id 7 encoding token_version 0, minted before the version advanced
When the client sends GET /v1/auth/me with that cookie
Then the response status is 401
And the response body is the apperr.UnauthorizedErrorMessage text
Scenario: A jwt minted for a since-deleted account is refused
Given a user repository containing user id 7, username "alice", is_verified false, TokenVersion 0
And a valid jwt cookie for user id 7 encoding token_version 0
When the client sends DELETE /v1/auth/register with that cookie
Then the response status is 200
When the client sends GET /v1/auth/me with the same cookie
Then the response status is 401
And the response body is the apperr.UnauthorizedErrorMessage text
Scenario: The route is never rate-limited, unlike login and password reset
Given a user repository containing user id 7, username "alice", is_verified true, TokenVersion 0
And a valid jwt cookie for user id 7 encoding token_version 0
When the client sends GET /v1/auth/me 5 times in immediate succession
Then every response status is 200
And none of the responses is 429