Commit Graph

34 Commits

Author SHA1 Message Date
Olivier Meunier
822d78d57d TOTP authentication
This is only the first part. When a totp_secret exists for a user, the
authentication then asks for the code and carries on.

The totp lib can handle 6 or 8 letter codes and sha1, sha256 and sha512.
For maximum compatibility with Google Authenticator though, it sticks
to 6 character and sha1.
2025-12-09 07:23:13 +01:00
Olivier Meunier
3f5f50ed5d Removed deprecated API routes
- PUT /api/profile/password (was undocumented and not used)
- POST /api/auth

With the introduction of MFA, you can't authenticate with a username
and password anymore. OAuth is now the only way to obtain an access
token.

The password update through the API was not documented so quite safe
to remove.
2025-12-08 18:36:34 +01:00
Olivier Meunier
b350de82bd Minor improvement with authentication
- removed log noise (not authenticated)
- on /login, go to redirect when a session already exists
- in SessionAuthProvider, clear session when external while forwarded
  authentication is disabled
2025-12-08 17:34:06 +01:00
Olivier Meunier
6921e97741 Moved the authentication providers to internal/server
Authentication providers are very much tied to server operations and
function and having them in internal/auth made it impossible to deal
with the session cookie handler when we need it.

internal/auth depended on the request ID for logging, removed this as
well with a specific logger interface.

Introducing a server.SessionHandler function to retrieve the cookie
handler so we can create cookies from scratch when needed.
2025-12-07 12:23:06 +01:00
Olivier Meunier
88b7aa504f Oauth2 endpoints
- Dynamic Client Registration (RFC 7591)
- Dynamic Client Registration Management (RFC 7592)
- OAuth2 Authorization Code Grant (RFC 6749)
- OAuth 2.0 Token Revocation (RFC 7009)
- OAuth 2.0 Authorization Server Metadata (RFC 8414)

This introduces new routes as follow:

- GET /.well-known/oauth-authorization-server: authorization server metadata
- GET /authorize : authorization page
- POST /authorize : authorization redirect
- POST /api/oauth/client : client creation
- GET /api/oauth/client/{id} : client information
- PUT /api/oauth/client/{id} : client update
- DELETE /api/oauth/client/{id} : client removal
- POST /api/oauth/token : token retrieval
- POST /api/oauth/revoke: token revocation

Other considerations:

- The authorization flow MUST use PKCE with S256 (plain is not allowed).
- At least one scope is mandatory.
- There is no refresh token.
- There is no client_secret.

This commit also moves the API token signing to config.SigningKey
with corresponding methods. (This type, with another key) is also used
for client registration management tokens)
2025-10-24 18:23:04 +02:00
Olivier Meunier
6cc068565a Native CSRF protection
This aims to replace the token based CSRF protection with a modern
and more simple approach, sugin Fetch metadata and Origin header.

See https://words.filippo.io/csrf/
2025-08-27 21:46:30 +02:00
Olivier Meunier
2b25827ad7 Detached every possible method from server.Server.
Every handler or middleware that doesn't need to be part of server.Server
is now a function of the server module.

This leaves only Init() and AddRoute() to server.Server.
2025-06-21 14:40:54 +02:00
Olivier Meunier
3a3faad335 Probe email capability using a permission.
- Added role "/email/send"
- Grant this role to unauthenticated and user group
- Adapt permission check to account for unauthenticated users
- Use WithPermission("email", "send") for recovery routes
- Use hasPermission("email", "send") in templates and removed
  canSendEmail global variable

Upon startup, we check if a mail host is properly configured and remove
the role "/email/send" when it's not.
2025-05-10 10:57:22 +02:00
Olivier Meunier
414addbacc Improved CSRF protection.
- Removed the token masking (BREACH mitigation should be dealt with at
  the gzip middleware level, which is the case already)
- Make the token only valid for the current user's session (renew it
  upon login and logout)
- Invalidate cache when a user logs in (new session)

Thanks to @gusted for the suggestion :)
2025-03-19 19:12:52 +01:00
Olivier Meunier
4541e05870 New Key Material
This replaces the (bad) secret key interpolation with a set of keys
derived from the secret key, using HKDF.

- Use pkg/securecookie and pkg/csrf with configs.Keys
- Application password are hashed using a derived key with the user uid
  in its context.
- API Token are simplified (MAC signed using a specific derived key)
- Shared bookmark links are encrypted using blowfish + backed2b (EtM) in
  a 160-bit block

As this is a breaking change, users can now renew their application
passwords.

API Token can be retrieved on the given page. From the browser extension
a logout / login is necessary.
2025-03-19 19:12:52 +01:00
Olivier Meunier
f68f25a16f i18n on all forms.
Added a translation implementation to every form so errors are
automatically translated.
2024-03-08 20:57:39 +01:00
Olivier Meunier
7dd7e124e6 Allow login with a username or an email address.
If the login "username" field contains a "@", we retrieve the user
with its email field instead of username. It works since "@" is not
a valid username character and "email" is a unique field in the user
table.

Added more tests for web and api login.
2023-12-02 11:48:57 +01:00
Olivier Meunier
3b6a674f87 Lint all the things!
- golangci-lint configuration
- gofumpt on all go files
- added package comments
- added missing comments on exported functions
- do not check for bodyclose on http testing responses.
- check for errors on triggered tasks
- check for errors in acls
- check for errors during policy loading
- check for errors in password recovery process
- better error checking in pkg/extract/contentscripts
- nolint rules for errcheck when it's not needed
  (mostly defer calls for file reader closing)
2023-11-28 20:26:05 +01:00
Olivier Meunier
70b9f4678e Moved everything in src/* back to the root folder.
This was a mistake to use go.work since it might be needed on a
specific dev setup (with different "use" directives).
It results in a codebase that really matches the main readeck
module fqdn.
2023-09-12 20:12:02 +02:00
Olivier Meunier
dda91020d3 Moved all the source files to ./src 2023-08-13 23:37:48 +02:00
Olivier Meunier
8a23cbad74 License prefix in all go files 2023-08-13 20:29:43 +02:00
Olivier Meunier
8a9b35d18f Allow redirection to requested page after login.
When unauthorized, pass the current page as a query parameter to the
login page. It will be forwarded to the login process, which will
send a redirection to the requested page upon successful authentication.
2023-07-22 16:12:03 +02:00
Olivier Meunier
6bc8d43ce8 Set Server.WithRedirectLogin to web views 2023-07-22 14:53:13 +02:00
Olivier Meunier
170d7e076a Moved auth, admin and profile to new forms 2021-10-24 21:37:47 +02:00
Olivier Meunier
d5ca2003c9 Refactored pkg/form.
Fields and Errors properties are now private and exposed through
Fields() and Errors() methods. This will let us extend a specialized
form instance if needs be.

Also added a Form.Get(string) as a helper to retrieve a form field
instance.
2021-09-27 23:25:49 +02:00
Olivier Meunier
14e8282538 Cookie and CSRF protection on logout.
Removed CSRF protection on logout route (since it's a POST, we don't
need it for this operation).

Changed the same-site value of the CSRF cookie to "lax".
2021-07-01 21:09:52 +02:00
Olivier Meunier
796e5a687c Roll our own session engine.
The internal/session package is heavily based on gorilla session but
provides a structured payload that is serialized to JSON.

This provides a mean to load and read the session with other tools
(provided they use the same algorithm and know the keys).
2021-07-01 21:06:42 +02:00
Olivier Meunier
0382e0e145 Route tests (signin, profile and admin so far)
- New email sender interface.
  The email sender is initialized by the app prelaunch and can be
  overridden later if need be. (We'll need it during tests)

- Prepared the app and server so we can run tests.
  - Split init and running parts in app, server and configuration
  - Properly close and delete the database connection on db.Close
  - Don't panic if bookmarks.StartWorkerPool is called more than once

- Testing tools.
  internal/testing provides an api for tests.
  - TestApp: prepares the application so it's ready to accept requests
  - Client: HTTP client to perform queries
  - Response: A wrapper around http.Response with some utilities
  - RunRequestSequence: to perform a series of HTTP requests

- internal/auth/signin
- internal/profile
- internal/admin tests
2021-06-07 21:26:31 +02:00
Olivier Meunier
37547ea6fd Password recovery procedure.
If a user forgets their password, we provide a form where they can
enter their account's email address. Regardless of the mail being
present in the db, we send an email with further instructions.
If the email is indeed linked to an account, the message contains a
link (valid for 2 hours) where the user can set a new password.
When successful, the user can then go to their login page.

The link for recovery (and the HTTP routes) are not available if the
mail configuration is not set.

Note: we use a very naive in-memory k/v store to keep the recovery link
codes and expire them. While it works, we might have to move to
something distributed if one day there's a need to have multiple
readeck instances running behind a load balancer. The same applies for
the cancel processes that exist elsewhere.
2021-05-08 12:46:45 +02:00
Olivier Meunier
e11047d041 Moving to github.
Although, it'll never be the upstream repository, it will be the public
one.
2021-05-02 18:58:39 +02:00
Olivier Meunier
3ab45393ca Sync internal session ttl to the cookie's max age option. 2021-04-27 23:29:48 +02:00
Olivier Meunier
094f44d68f Cookie only session.
The cookie is signed and encrypted, and contains the user ID and its
seed value.
2021-04-27 22:21:15 +02:00
Olivier Meunier
839b983a3c New template engine (Jet)
In order to have reusable components (with template content inside)
we had to move to a suitable template engine. No hackish {{ extends }}
anymore, blocks with arguments, includes, etc.
2021-04-21 23:46:07 +02:00
Olivier Meunier
e0917a2187 Simplified the form field validation.
Added a Validate() method to form.Field that directly uses the
validation.Rule interfaces so we can pass it any validation rule
or roll our owns.
2021-03-31 06:14:34 +02:00
Olivier Meunier
07fa3c1beb Linting #1 2021-03-20 23:15:06 +01:00
Olivier Meunier
3c492df297 Renamed the module to codeberg.org/readeck/readeck
This project was never on github in the first place, so there's no
need to have yet another github namespace.
2021-03-20 14:53:56 +01:00
Olivier Meunier
6e1a81b9d3 Improved session management
- Configurable cookie name and session duration
- Switched to sessions.FilesystemStore
- Store a check code based on username, email and password.
  Changing any of these invalidates all the user sessions.
- Change the check code for the current user session so they don't
  get kicked out on password change or profile update.
- Resend the session cookie on each request so its duration increases.
  It make the cookie expires only if the user is not active during the
  cookie max age.
2021-03-14 15:16:24 +01:00
Olivier Meunier
ec220ee5a7 JWT authentication method.
- Added a token table. A token has a non sequential unique ID and is
  attached to a user. It can be disabled and have an optional expiry
  date.
- A new token can be created with a basic API using the user's
  crendentials.
- The TokenAuthProvider authenticates the user when a request has
  an "Authorization: Bearer {token}" header.

The JWT token is never saved in db, only the token ID. The JWT is
signed with configs.Config.Keys.JwtSk that is derived from the
configured secret key.
2021-03-13 12:28:07 +01:00
Olivier Meunier
867f883eff Moved auth related packages into internal/auth
- internal/users -> internal/auth/users
- internal/logon -> internal/auth/signin
2021-03-13 08:57:05 +01:00