Files
HIPCTF2/docs/guides/landing-page.md
T

6.0 KiB
Raw Blame History

type, title, description, tags, timestamp
type title description tags timestamp
guide Landing Page How the public landing page renders, how the login + registration modal is opened, and what each form does.
guide
landing
login
register
ui
2026-07-21T18:28:00Z

What you see

LandingComponent (frontend/src/app/features/landing/landing.component.{ts,html,css}) is the route mounted at /login (the path is historic — the page is now the public landing page, not a bare login form).

On a fresh visit the page shows, in order:

  1. Logo<img data-testid="landing-logo"> rendered from BootstrapService.payload().logo (empty string → the <img> is not rendered).
  2. Page title<h1 data-testid="landing-title"> rendered from BootstrapService.payload().pageTitle (defaults to HIPCTF).
  3. Welcome card<article data-testid="landing-welcome"> whose innerHTML is the sanitized render of BootstrapService.payload().welcomeMarkdown (via MarkdownService.rendermarked + DOMPurify).
  4. Login button<button data-testid="landing-open-login">.
  5. Blog list<section data-testid="landing-blog"> listing every row returned by GET /api/v1/blog/posts (only status='published' rows), or the text No announcements yet. when the list is empty.

Guard

/login is gated by landingGuard (frontend/src/app/core/guards/landing.guard.ts):

initialized isAuthenticated Result
false any Redirect to /bootstrap (first-admin modal).
true true Redirect to / (already signed in).
true false Render the landing page.

Login modal

Clicking the Login button sets mode = 'login', which renders an overlay containing:

Field Selector (data-testid) Notes
Username login-username autocomplete="username"
Password login-password autocomplete="current-password"
Field error login-field-error Inline required message.
Server error login-server-error Mapped via buildLoginFailureMessage.
Submit login-submit Disabled while submitting() is true.
Switch login-switch-btn Visible only when registrationsEnabled is true; opens the registration modal.

Submission flow:

  1. submitLogin()AuthService.login({ username, password }).
  2. On ok: true, AuthService.setSession(...) is called and the user is navigated to /.
  3. On ok: false, the modal renders the mapped failure text and a (Ns remaining) hint when code === 'RATE_LIMITED' and the server message contains a wait-seconds value.

Registration modal

When bootstrap.payload().registrationsEnabled === true, the Login modal shows a "Register here" link (login-switch-btn) that swaps the form to mode = 'register'. The registration form contains:

Field Selector (data-testid) Notes
Username register-username 332 chars, regex ^[a-zA-Z0-9_.-]+$.
Password register-password Argon2id policy enforced server-side.
Confirm password register-confirm Must match (group-level passwordMatchValidator).
Field error register-field-error Inline message covering required / minLength / maxLength / pattern / mismatch.
Server error register-server-error Warn-styled when code is RATE_LIMITED or REGISTRATIONS_DISABLED.
Submit register-submit Disabled while submitting.
Switch register-switch-btn Goes back to the login form.

Submission calls AuthService.register(...) which posts to POST /api/v1/auth/register. On ok: true the user is signed in immediately and navigated to /.

The modal is dismissible via the × button (landing-modal-close), clicking the overlay backdrop, or pressing Escape. Submit-in-flight dismissal is suppressed.

Server error code → user copy

buildLoginFailureMessage (frontend/src/app/features/landing/login-modal.service.ts) maps the API error envelope into modal copy:

Code Rendered text
INVALID_CREDENTIALS Invalid username or password.
RATE_LIMITED Please wait N seconds before trying again. + (Ns remaining) hint when N is found in the server message.
CSRF_INVALID Session expired. Please reload the page and try again.
USERNAME_TAKEN Username already exists.
WEAK_PASSWORD Password does not meet the security policy.
REGISTRATIONS_DISABLED Registrations are currently disabled.
VALIDATION_FAILED Please check the form fields and try again.
other / default Raw message (or Something went wrong. Please try again. if empty).

CSRF

AuthService.ensureCsrf() (frontend/src/app/core/services/auth.service.ts) calls GET /api/v1/auth/csrf before both login() and register() to materialise the csrf cookie if it isn't already set. The call swallows errors because the CSRF middleware also mints the cookie on the response to the unsafe call itself.

See also