Commit Graph

15 Commits

Author SHA1 Message Date
3b22134292 feat: device list and revoke with the jti followed through refresh
GET /devices lists the caller's confirmed and used device sessions (PHP's
fields, device_name from the stored name or the user agent, is_current
for the session the request's token belongs to). DELETE /devices/{id}
marks the session revoked and blacklists the device token's jti for its
whole refresh window in one transaction. POST /refresh hands a used
session's jwt_jti over to the refreshed token (classes.HandOverDeviceJTI),
so a device that refreshed before the revoke is signed out too.
2026-10-26 10:19:25 +01:00
4e3e5b05b5 feat: device authorization by QR or short code
PHP's device flow on golem15_user_device_auth_sessions (PHP's columns,
the token stored as its SHA-256, plus jwt_jti): POST /devices/initiate
answers a 64-character token, an XXXX-XXXX short code, the configurable
auth_url (golem15.user.device_auth.auth_url) and the expiry; a signed-in
user authorizes it with POST /devices/authorize (short_code or token,
one guarded pending-to-confirmed UPDATE); the device's first poll of
GET /devices/status/{token} after that receives a session JWT through one
guarded confirmed-to-used UPDATE, so exactly one poll gets it. Used and
revoked sessions never turn into expired; each initiate deletes a bounded
batch of stale sessions.
2026-10-26 10:14:48 +01:00
Jakub Zych
ee5c096d0d feat: plugin-declared member token scopes (pact.HasFrontendTokenScopes)
- CompileAPITokenScopes builds the catalog from the built-ins and the
  activated plugins' declarations; a bad name, a redeclared built-in, a
  duplicate, an empty label or a missing phrase key fails boot
- POST /tokens, user:token:create and MintAPITokenFor check the
  application's own catalog (422 text unchanged); MintAPIToken keeps its
  signature with a process-wide fallback for declared scopes
- GET /_user/api/v1/tokens/scopes lists the built-ins and the declared
  scopes with translated labels
- built-in scope labels in en and pl; TestMemberTokenScopesSmoke
2026-10-23 17:00:12 +02:00
Jakub Zych
32f92591d6 fix: account link proof by auth_time, code in the URL fragment and POST show
- BackendLinkConfirm reads the session's auth_time (bouncer.VerifyAuthTime);
  a token without it, or a refreshed old session, answers 409 stale_session
- POST /_user/api/v1/backend-link/show takes the code in the body; GET
  /backend-link/{code} stays, deprecated; both answer Cache-Control no-store
- My account answers <frontend_url><page_path>#code=<code>; new key
  golem15.user.backend_links.page_path (default /link-admin)
- TestBackendLink now crafts auth_time tokens; TestBackendLinkAuthTimeSmoke
2026-10-23 16:38:55 +02:00
Jakub Zych
0f545f5c81 feat: user MCP guard provider, plugin frontend permissions and the user_me tool
- classes.FrontendPermissionCatalog: the seeded rows, then codes activated
  plugins declare through pact.HasFrontendPermissions (seeded row wins);
  both permission editors offer it
- classes.MCPUserGuard (guard "user"): reads user.api_token, user.scope:*
  and user.permission:*, allows with the route's checks (session scopes,
  group grants, every code), dispatches with an in-memory token credential,
  resolves usr_ personal tokens; registered in *pact.MCPGuards and published
  as pact.MCPPersonalTokenResolver
- GET /_user/api/v1/token/me (user.api_token, user.scope:read) and the MCP
  tool me (user_me)
- pact.MCPSubjectChanged on token revoke, Users saves and User Groups saves
  and deletes
2026-10-23 12:08:38 +02:00
Jakub Zych
7ac80c9aea feat: verified frontend account link for administrators
- golem15_user_backend_links (1:1, both sides unique) and one-shot hashed
  golem15_user_backend_link_requests (golem15.user.backend_links.request_ttl, 10m)
- classes.BackendLinks: CreateLinkRequest, ConfirmLinkRequest (row lock,
  JWT iat not before the request), SetLink, RemoveLink, LinkedBackendUser;
  published as *classes.BackendLinks and pact.MCPLinkedBackendResolver
- GET /_user/api/v1/backend-link/{code} and POST /backend-link/confirm (jwt.auth)
- My account section frontend_account with link and unlink
- BackendLinkedEvent, BackendUnlinkedEvent and pact.MCPSubjectChanged after commit;
  deleting or deactivating a frontend user removes its link
- golem15.user.frontend_url (falls back to app.url)
2026-10-23 12:03:26 +02:00
Jakub Zych
3828a65481 feat: golem15.user.password.public_routes false leaves POST /login, /forgot-password and /reset-password unregistered, so they 404 like any unknown path
The key defaults to true, so the route table is unchanged. False skips only
those three registrations: they get the router's own 404, with no custom
handler and no framework change. /register keeps allow_registration and the
turnstile guard. The key is read at boot, and a value other than true or
false fails boot like golem15.user.turnstile.
2026-10-23 00:09:38 +02:00
Jakub Zych
7b9faa3419 feat: opt-in Cloudflare Turnstile on login and register (golem15.user.turnstile)
- empty (default) adds nothing; open or closed appends turnstile:{mode} to
  POST /login and /register only; any other value fails boot naming the key
- TestTurnstileRoutesSmoke records the declared routes with a recording router
2026-10-20 14:45:22 +02:00
Jakub Zych
0fe5b91b63 feat: own shared user API tokens 2026-10-04 22:30:20 +02:00
Jakub Zych
02ba1f0666 refactor: rename module to git.golem15.com/golem15/sm-user-plugin
Prefix replacement of the old application-nested module path in go.mod
and every import. No identifier, string, table, migration ID, config key
or comment changes.
2026-10-02 10:56:06 +02:00
Jakub Zych
af976e789f refactor(10.2-01): use nested framework modules
- Rewrite application and plugin framework imports
2026-09-28 02:21:38 +02:00
Jakub Zych
63d6d049b7 feat(07-03): add avatars, mail templates, and password-change command
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 16:43:44 +02:00
Jakub Zych
5c1f857d54 feat(07-03): update profile, change password, and marketing consent
Changing a password invalidates older tokens and leaves the token that made the call signed in.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 16:32:05 +02:00
Jakub Zych
cb50d673c6 feat(07-03): add password reset and account activation
Reset and activation codes expire on a TTL, and a missing issued-at still accepts a cutover code. Forgot-password always returns the same 200 body.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 16:30:22 +02:00
Jakub Zych
6bceb8167c feat(07-02): serve login, logout, fetch, and refresh
Wire the user API session loop to the JWT blacklist and a pre-password throttle gate so a logged-out token cannot be fetched again.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 14:40:18 +02:00