Authentication¶
Vector has no users, passwords or sessions of its own. Your application authenticates; Vector asks whether the result may enter the admin.
The vector middleware group¶
Every Vector route is in the vector middleware group. Your
authentication middleware joins it and registers a
Kinetis\Http\CurrentUserInterface on the request scope; Vector’s own
VectorAccessMiddleware, the group’s innermost member, then asks your
VectorAccessInterface:
Request |
Answer |
|---|---|
No identity registered |
|
Identity the access policy refuses |
|
Admitted |
The route runs, under the resource policies |
#[AsMiddlewareGroup('vector', priority: 50)]
#[AsMiddlewareGroup('vector-shell', priority: 50)] // hosted mode's shell page
#[AsMiddlewareGroup('mcp', priority: 50)] // the MCP tools over HTTP
final readonly class AdminAuthMiddleware implements MiddlewareInterface { /* … */ }
Register nothing when no credential is present: the API then answers
401, and the hosted shell redirects to your login page.
The mcp middleware group¶
The MCP tools over HTTP, POST /mcp, are served by kinetis/mcp in its
own mcp middleware group, which the vector group’s members do not
join on their own. Its last member, McpIdentityGuardMiddleware at
priority 0, answers 401 unless a middleware earlier in the group
registered a CurrentUserInterface. With your authentication middleware
in vector only, every caller of POST /mcp — admins included — gets
401. Add it to the mcp group as well, as the example under
The vector middleware group and the
demo’s VectorAuthMiddleware do.
Its priority in mcp must be above the guard’s 0, so it runs before
the guard; at 0 or below it is not ordered before the guard, and
requests still get 401. 50 sits between the guard and kinetis/mcp’s
McpOriginMiddleware, at 100, which checks the Origin header first.
Do not set MCP_HTTP_PUBLIC as a workaround: that opens the endpoint to
callers without an identity. See MCP tools.
The vector-shell middleware group¶
Hosted mode’s shell page, GET /vector/ui and every deep link under it,
is in the vector-shell group. Your session and authentication
middleware join it as they join vector, as above. Vector’s own
VectorShellAccessMiddleware is the group’s innermost member: a request
that reaches it with no identity registered is redirected to
VECTOR_LOGIN_URL (302, Cache-Control: no-store); any other reaches
the page. The group always exists, so the route table is valid before
your middleware joins it.
Standalone: bearer tokens¶
The standalone UI signs in with POST {apiBase}/auth/login, sending
{"email", "password"}, and expects:
{ "accessToken": "…", "tokenType": "Bearer", "expiresIn": 900,
"refreshToken": "…", "refreshExpiresIn": 43200,
"user": { "id": 1, "name": "Vector Admin", "email": "admin@example.com" } }
It then sends Authorization: Bearer <token> on every request and keeps
the tokens for the browser tab only: in memory, mirrored to
sessionStorage for reloads, never in localStorage.
refreshToken is optional. When your login answers one, the UI renews
the session itself, one renewal at a time: with under two minutes left
on the access token, and once when the API answers 401 (the
request is then repeated), it calls POST {apiBase}/auth/refresh with
{"refreshToken"} and expects the same answer as the login; Sign
out calls POST {apiBase}/auth/logout with {"refreshToken"} to revoke
it. When the renewal is refused, or there is no refresh token, the UI
signs out when the access token expires, and a 401 goes to /login.
An impersonation token is never renewed. kinetis/auth-jwt issues and
verifies access tokens, and its RefreshTokenStore keeps single-use
refresh tokens in a cache.
Protecting the sign-in¶
Throttling failed sign-ins is your application’s job — Vector never sees
a password. The demo shows the shape with the framework’s
AttemptThrottle on a Redis cache: five failures in fifteen minutes lock
one e-mail from one client address, twenty lock the address, a success
clears both, and a locked attempt answers 429 with Retry-After (the
HTML form shows a message).
Warning
Never lock an account by its e-mail alone: anyone who knows the address could then keep its owner out by typing wrong passwords. Count failures per e-mail and client address, so an attacker locks only themselves, plus per address, so one address cannot work through many accounts.
The demo’s access tokens last fifteen minutes and its refresh tokens
twelve hours, reloading the user at each renewal so a changed role
applies. Without a cache it refuses to sign anyone in (503) rather than
run unthrottled.
The UI lives on another origin, so register CorsMiddleware for it,
allowing the Authorization, Content-Type, X-CSRF-Token and
If-Match headers and exposing Content-Disposition and ETag.
How long a session lasts¶
A session does not end on its own: the UI renews the token
before it runs out and polls badges and the inbox, so an open tab would
stay signed in for as long as it is open. The layer’s app.config
vector.idleMinutes (30 by default; 0 turns it off) ends it, in both
modes, when nobody has used the admin — no pointer, key, touch or
scroll — for that long. Bearer mode revokes the refresh token and opens
the login page with a notice and the way back to the page that was
open; cookie mode posts the application’s logout form. A tab that comes
back into view after sleeping is checked at that moment too.
How long a session may last in all is your application’s rule. The demo’s refresh endpoint keeps the end the sign-in set, twelve hours later, however often the tab renewed, so a tab still signs in again by then.
One API, both modes¶
One middleware can accept both — a bearer token when one is sent, the session otherwise, requiring the CSRF token only for a session. The demo application does exactly that.
Background jobs¶
A queued job runs in a worker, with no request. Bind
ActorResolverInterface so Vector can rebuild the admin who started it
from their id:
$app->bind(ActorResolverInterface::class, AdminResolver::class);
final readonly class AdminResolver implements ActorResolverInterface
{
public function resolve(string $actorId): ?CurrentUserInterface { /* load the user */ }
}
Without it, Vector queues nothing: queued actions and imports answer
403 naming what is missing, an export above the resource’s export
limit is refused, and XLSX is not offered
(Requirements).
See also¶
Authorization — what an admitted user may do.
Deployment — choosing a mode.
Audit trail and impersonation — impersonation.