docs: describe the user plugin
This commit is contained in:
142
README.md
Normal file
142
README.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# golem15.user
|
||||
|
||||
The shared front-end user plugin for SummerCMS applications: registration, login with JWT sessions, activation and password reset, profile and avatar endpoints.
|
||||
|
||||
```go
|
||||
import _ "git.golem15.com/golem15/sm-user-plugin"
|
||||
```
|
||||
|
||||
Module `git.golem15.com/golem15/sm-user-plugin`, package `user`, plugin ID `golem15.user`.
|
||||
|
||||
## Overview
|
||||
|
||||
The plugin is a compiled SummerCMS plugin. Its `init` registers it with the framework, so an application loads it by listing it in `summer.yaml`; `summer build` then emits the blank import above into the application's `plugins.gen.go`.
|
||||
|
||||
It owns the `users` table extensions, `user_throttle`, `jwt_blacklist` and `golem15_user_organisations`, and the JSON API under `/_user/api/v1`. It is a core plugin shared by several applications, so its routes, payloads, table names, migration IDs and config keys are a contract: change them only in a backwards-compatible way.
|
||||
|
||||
## Features
|
||||
|
||||
- **Registration and activation.** `POST /register`, `POST /activate`, `POST /activate-by-code`. Registration can be switched off and throttled; `activate_mode` is `auto`, `user` (a mailed code) or `admin`, with codes from `IssueActivationCode` and `VerifyActivationCode`.
|
||||
- **JWT sessions.** `POST /login`, `GET /fetch`, `POST /refresh`, `POST /logout`. Boot registers the `jwt` guard with the framework's `bouncer.Registry` and exposes it as the `jwt.auth` middleware for other plugins. Logged-out tokens go to the Postgres `jwt_blacklist`, which a background sweep prunes.
|
||||
- **Password reset and change.** `POST /forgot-password` answers the same body for known and unknown addresses; `POST /reset-password` consumes a code from `IssueResetCode`. `POST /change-password` requires the current password.
|
||||
- **Must-change-password lock.** The `user:require-password-change` console command flags an account. The flag rides on the authenticated principal (`bouncer.Principal.MustChangePassword`) for guards and other plugins to act on, and a successful `POST /change-password` clears it.
|
||||
- **Login throttling.** Failed logins are counted per user and IP in `user_throttle` (`CheckAndRecordLogin`, `RejectIfThrottled`) and suspend the account for a configured time.
|
||||
- **Rate limiting.** Every route runs behind the `user-api` bucket: 120 requests a minute per authenticated user, or per client IP for anonymous calls.
|
||||
- **Profile.** `POST /update`, `POST /marketing-consent`, `POST /avatar` and `POST /avatar/remove` (the avatar is stored through the framework's attachment storage).
|
||||
- **Organisations.** The `Organisation` model and the `organisation_id` and `organisation_role` user columns, as a foreign-key target for other plugins.
|
||||
- **Mail templates.** Activation, reset and reactivation mails in Polish and English (`golem15.user::mail.*`, layout `golem15.user::mail.layouts.user`); the locale follows the user (`ResolveMailLocale`).
|
||||
- **Extension events.** `RegisterEvent` fires after a user row is created; `GetApiArrayEvent` lets other plugins merge keys into the user payload.
|
||||
- **OAuth providers.** `GET /oauth-providers` lists configured social login providers (none yet: it returns an empty list).
|
||||
|
||||
## Usage
|
||||
|
||||
Mount the plugin in an application as a git submodule at `plugins/golem15/user`:
|
||||
|
||||
```sh
|
||||
git submodule add git@git.golem15.com:golem15/sm-user-plugin.git plugins/golem15/user
|
||||
```
|
||||
|
||||
Add it to the application's `go.work`:
|
||||
|
||||
```text
|
||||
use (
|
||||
.
|
||||
./plugins/golem15/user
|
||||
)
|
||||
```
|
||||
|
||||
Require it in the application's `go.mod` with a local replace:
|
||||
|
||||
```text
|
||||
require git.golem15.com/golem15/sm-user-plugin v0.0.0
|
||||
|
||||
replace git.golem15.com/golem15/sm-user-plugin => ./plugins/golem15/user
|
||||
```
|
||||
|
||||
List it in `summer.yaml` and run `summer build`:
|
||||
|
||||
```yaml
|
||||
module: git.golem15.com/golem15/acme
|
||||
binary: acme
|
||||
plugins:
|
||||
- id: golem15.user
|
||||
module: git.golem15.com/golem15/sm-user-plugin
|
||||
```
|
||||
|
||||
The plugin's own `go.mod` replaces the framework with `../../../../summercms.go`, so it expects the framework checkout next to the application checkout (`<dir>/summercms.go` and `<dir>/acme/plugins/golem15/user`).
|
||||
|
||||
Another plugin that needs the signed-in user puts its routes behind the `jwt.auth` middleware and imports `git.golem15.com/golem15/sm-user-plugin/models` or `.../classes` for the user model and helpers.
|
||||
|
||||
## API reference
|
||||
|
||||
All routes are mounted under `/_user/api/v1` with the `throttle:user-api` middleware.
|
||||
|
||||
| Method and path | Handler |
|
||||
|-----------------|---------|
|
||||
| `POST /login` | `controllers.Login` |
|
||||
| `POST /logout` | `controllers.Logout` |
|
||||
| `GET /fetch` | `controllers.Fetch` |
|
||||
| `POST /refresh` | `controllers.Refresh` |
|
||||
| `POST /register` | `controllers.Register` |
|
||||
| `GET /oauth-providers` | `controllers.OAuthProviders` |
|
||||
| `POST /forgot-password` | `controllers.ForgotPassword` |
|
||||
| `POST /reset-password` | `controllers.ResetPassword` |
|
||||
| `POST /activate` | `controllers.Activate` |
|
||||
| `POST /activate-by-code` | `controllers.ActivateByCode` |
|
||||
| `POST /update` | `controllers.Update` |
|
||||
| `POST /change-password` | `controllers.ChangePassword` |
|
||||
| `POST /marketing-consent` | `controllers.MarketingConsent` |
|
||||
| `POST /avatar` | `controllers.UploadAvatar` |
|
||||
| `POST /avatar/remove` | `controllers.RemoveAvatar` |
|
||||
|
||||
Packages:
|
||||
|
||||
- `classes`: `JWTSecret`, `GormUsers` (the `bouncer.UserProvider`), `LookupByEmail`, the activation and reset code helpers, the throttle helpers, mail locale helpers and the two events.
|
||||
- `models`: `User`, `Throttle`, `Organisation`.
|
||||
- `updates`: the gormigrate migrations, returned by `updates.All()`.
|
||||
- `console`: `RequirePasswordChange`, the `user:require-password-change` command.
|
||||
|
||||
## Configuration
|
||||
|
||||
Defaults live in `config/config.yaml` and are merged under `golem15.user.*`. Any key can be overridden by an environment variable in the framework's `SUMMER_` form.
|
||||
|
||||
| Key | Default | Meaning |
|
||||
|-----|---------|---------|
|
||||
| `golem15.user.jwt.secret` | `""` | HS256 signing secret. Required: boot fails while it is empty. Set it with `SUMMER_GOLEM15__USER__JWT__SECRET`; there is no production default. |
|
||||
| `golem15.user.jwt.ttl` | `60` | Access token lifetime in minutes. |
|
||||
| `golem15.user.jwt.refresh_ttl` | `20160` | Refresh window in minutes. |
|
||||
| `golem15.user.jwt.blacklist_grace` | `0` | Grace period in seconds for a blacklisted token. |
|
||||
| `golem15.user.jwt.leeway` | `0` | Clock leeway in seconds (declared for parity, not read yet). |
|
||||
| `golem15.user.jwt.blacklist_sweep_interval` | `10m` | How often expired blacklist rows are deleted (a Go duration). |
|
||||
| `golem15.user.activation.require_activation` | `true` | Whether a new account must be activated before login. |
|
||||
| `golem15.user.activation.activate_mode` | `auto` | `auto`, `user` (mailed code) or `admin`. |
|
||||
| `golem15.user.activation.reset_ttl_minutes` | `60` | Password reset code lifetime. |
|
||||
| `golem15.user.activation.activation_ttl_hours` | `72` | Activation code lifetime. |
|
||||
| `golem15.user.registration.allow_registration` | `true` | Whether `POST /register` accepts new accounts. |
|
||||
| `golem15.user.registration.use_register_throttle` | `true` | Whether registration is limited to three accounts per IP an hour. |
|
||||
| `golem15.user.throttle.attempt_limit` | `5` | Failed logins before suspension. |
|
||||
| `golem15.user.throttle.suspension_minutes` | `15` | Suspension length. |
|
||||
| `golem15.user.throttle.use_throttle` | `true` | Whether login throttling is on. |
|
||||
| `golem15.user.password.bcrypt_cost` | `10` | bcrypt cost for new password hashes. |
|
||||
| `golem15.user.password.min_length` | `8` | Minimum password length. |
|
||||
|
||||
## CLI commands
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `user:require-password-change <email>` | Flag an account so its next session must set a new password. |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- The SummerCMS framework, `git.golem15.com/golem15/summercms` (modules `backpack`, `bonfire`, `bouncer`, `pact`, `party`, `surf` and others).
|
||||
- gormigrate, GORM, golang-jwt and gocloud.dev, as pinned in `go.mod`.
|
||||
- testcontainers-go for the Postgres-backed tests.
|
||||
|
||||
## Testing
|
||||
|
||||
```sh
|
||||
go test -short ./... # fast run, no Docker
|
||||
go test ./... # full run, starts Postgres with testcontainers (needs Docker)
|
||||
```
|
||||
|
||||
`TestRegisterCORSPath` reads the host application's `config/http.yaml`, so run the tests inside an application checkout where the plugin is mounted at `plugins/golem15/user`.
|
||||
Reference in New Issue
Block a user