Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 0 additions & 51 deletions .github/dependabot.yml

This file was deleted.

21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ Read before upgrading. No configuration is required; the defaults are safe.
3. **AD outage default is `grace` (10 hours).** Previously an AD outage had no effect on already-signed-in users. Now a user not confirmed active in AD within the last 10 hours is denied until AD is reachable again. Right after upgrading, nobody has been confirmed yet: the first successful login, refresh, or SSO after the upgrade records it. If AD goes down before that, those users are denied. Set the policy to `allow` temporarily if you expect AD maintenance right after upgrading.
4. **One extra LDAP search per token refresh and per SSO-cookie reuse** for AD users (about 4 per hour per active session with the default 15-minute access token).
5. **`/api/auth/refresh` now stamps the app's *current* audience** (`aud`) instead of the audience of the original login. If you changed an app's audience, existing sessions switch to the new value at their next refresh. ([SECURITY-AUDIT.md](https://github.com/bodaay/SimpleAuth/blob/master/SECURITY-AUDIT.md) **M40**)
6. **Refresh responses can now be `403 {"error":"account disabled"}`** (`/api/auth/refresh`) or `401 {"error":"invalid_grant","error_description":"account disabled"}` (OIDC) for AD users. Apps must treat these like an expired session and send the user back to login.
6. **Building from source now requires Go 1.26** (`go.mod` is `go 1.26.8`; the Docker image builds with `golang:1.26-alpine`). Prebuilt binaries and the Docker image are unaffected.
7. **Refresh responses can now be `403 {"error":"account disabled"}`** (`/api/auth/refresh`) or `401 {"error":"invalid_grant","error_description":"account disabled"}` (OIDC) for AD users. Apps must treat these like an expired session and send the user back to login.

### Security fixes

Expand All @@ -51,6 +52,24 @@ Read before upgrading. No configuration is required; the defaults are safe.
- **L23 (LOW)**: after the SSO cookie moved from path `/` to the base path, an old `/` cookie survived logout and could silently sign the user back in. Logout now clears both.
- **L24 (LOW)**: creating an app-local user (`POST /api/app/users`) ignored store errors and could crash mid-way, leaving a user with no username mapping or roles. It now fails cleanly and rolls back.

### Admin console

- **Reworked admin UI.** Pages now have their own URLs (`#/users`, `#/apps`, …), so refresh, deep links, and Back/Forward work. The sidebar is grouped into Access / Directory / System.
- **Structured per-app authorization editor**, replacing the raw JSON textarea. Assignments can only use defined roles.
- **Accessible dialogs**: Escape closes them, focus is trapped and restored. Rotating a secret, deleting an app, and enabling require-assignment now ask for confirmation with a warning.
- **Users page**: Edit Profile and Unlock actions, and a "Locked" badge.
- If the admin session expires, the console now returns to the sign-in screen instead of showing a stream of errors.
- New **AD Outage Behavior** card on the Settings page (see the highlight above).

### Changed

- **Go 1.26.8** (was 1.25.12). The Go 1.25.12 standard library had 6 vulnerabilities the code reaches (net/url, html/template, crypto/tls, net/http, encoding/xml, encoding/asn1), plus GO-2026-5026 in `golang.org/x/net`. All are fixed.
- Dependencies updated: `golang.org/x/crypto` 0.57.0, `golang.org/x/net` 0.58.0, `github.com/go-ldap/ldap/v3` 3.4.14, `go.etcd.io/bbolt` 1.5.0, `github.com/jackc/pgx/v5` 5.11.0.
- SDK dev dependencies updated: TypeScript 7, `@types/node` 26, and newer Python minimums for `cryptography`, `fastapi`, `starlette`, `django`, and `setuptools`.
- GitHub Actions updated: `actions/checkout` v7, `actions/setup-go` v7, `github/codeql-action` v4.38.2, `softprops/action-gh-release` v3.0.3.
- **Security scan fixed.** The `govulncheck` CI job had been failing to install since `govulncheck` v1.8.0 started requiring a newer Go version, so no scan was actually running. It is now pinned to a working version.
- **Dependabot removed.** Dependencies are now reviewed by a maintainer once a month, following [docs/MAINTENANCE.md](https://github.com/bodaay/SimpleAuth/blob/master/docs/MAINTENANCE.md). `govulncheck` still runs on every push and pull request, and weekly.

### Added

- Runtime settings `directory_outage_policy` (`grace` | `block` | `allow`) and `directory_outage_grace_hours` (1–168, default 10), in `GET/PUT /api/admin/settings` and the admin UI.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ Your app and its auth server, one process, one deploy. See [docs/ARCHITECTURE.md
| [SDK Guide](docs/SDK-GUIDE.md) | Client SDK usage |
| [Security Audit Log](SECURITY-AUDIT.md) | Every review, finding, and fix |
| [Changelog](CHANGELOG.md) | What changed in each release, upgrade notes |
| [Maintenance](docs/MAINTENANCE.md) | Monthly dependency + toolchain checklist (no bots) |

## License

Expand Down
190 changes: 190 additions & 0 deletions docs/MAINTENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Monthly Maintenance Checklist

SimpleAuth does **not** use Dependabot or any other update bot. Dependencies are reviewed and updated by a maintainer **once a month**, using this checklist. Do the steps in order. Every command is run from the repository root unless a step says otherwise.

**What you need:** Go (the version in `go.mod`), Node.js 20+, Python 3.11+, Docker, the GitHub CLI (`gh`, logged in), and optionally the .NET 8 SDK.

The `govulncheck` job in CI (`.github/workflows/security.yml`) still runs on every push and pull request and **every Monday at 06:00 UTC**. It fails the build if the Go code reaches a known vulnerability, so an urgent Go security fix will show up there between monthly checks.

---

## 1. Start from an up-to-date `master` on a new branch

```bash
git checkout master
git pull --ff-only
git checkout -b chore/monthly-maintenance-$(date +%Y-%m)
```

## 2. Go toolchain

The Go version appears in **three places**, and they must always move together:

| File | What to change | Example |
|---|---|---|
| `go.mod` | the `go` line | `go 1.26.8` |
| `Dockerfile` | the builder image | `FROM golang:1.26-alpine AS builder` |
| `.github/workflows/security.yml` | the `govulncheck` version, if a newer one supports your Go version | `go install golang.org/x/vuln/cmd/govulncheck@v1.8.0` |

1. Find the newest **patch** release of your current Go minor version (always use the latest patch — `.0` releases usually have known vulnerabilities):

```bash
curl -s 'https://go.dev/dl/?mode=json' | python3 -c "import json,sys; print([r['version'] for r in json.load(sys.stdin)])"
```

2. Set it in `go.mod` (replace `1.26.8` with the version you found):

```bash
go mod edit -go=1.26.8
```

3. Moving to a new **minor** version (e.g. 1.26 → 1.27) is a bigger change: also update the `Dockerfile` builder tag, and add an upgrade note to `CHANGELOG.md` ("building from source now requires Go 1.27").

4. Check whether a newer `govulncheck` exists and which Go version it needs:

```bash
curl -s https://proxy.golang.org/golang.org/x/vuln/@latest
curl -s https://proxy.golang.org/golang.org/x/vuln/@v/<VERSION>.mod | grep '^go '
```

Only move the pin in `security.yml` if that `go` line is **not newer** than the one in `go.mod`.

## 3. Go server dependencies

```bash
go list -m -u all | grep '\[' # lists modules that have a newer version in [brackets]
go get -u ./... # update direct + indirect deps to the latest minor/patch
go mod tidy
git diff go.mod # review every version change
```

- If `go get` changes the `go` line in `go.mod`, a dependency now needs a newer Go. Do step 2 for that version instead of accepting whatever `.0` it picked.
- Do not take a new **major** version (a `/v2`, `/v3` … import path) without reading its release notes.

Run the vulnerability scanner (use the same version as `security.yml`):

```bash
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
```

Exit code `0` means no reachable vulnerabilities. Anything else: update the module named in the output (or Go itself, if it says "Standard library").

## 4. Go SDK (`sdk/go`)

```bash
cd sdk/go
go list -m -u all | grep '\['
go get -u ./... && go mod tidy
go build ./... && go vet ./...
cd ../..
```

## 5. JavaScript / TypeScript SDK (`sdk/js`)

```bash
cd sdk/js
npm outdated # shows current vs latest for typescript and @types/node
npm install --save-dev typescript@latest @types/node@latest
npm run typecheck # must pass
cd ../..
```

## 6. Python SDK (`sdk/python`)

The minimum versions live in `sdk/python/pyproject.toml`: `dependencies` (`requests`, `cryptography`), `[project.optional-dependencies]` (`fastapi`, `starlette`, `flask`, `django`), and `[build-system] requires` (`setuptools`). For each package, check the latest release:

```bash
python3 -m pip index versions requests
python3 -m pip index versions cryptography
# …repeat for fastapi, starlette, flask, django, setuptools
```

Raise a `>=` lower bound only when the newer version fixes a security issue or is needed. Then confirm the package still installs and imports:

```bash
python3 -m venv /tmp/sa-venv && /tmp/sa-venv/bin/pip install ./sdk/python \
&& /tmp/sa-venv/bin/python -c "import simpleauth; print('ok')"
```

## 7. .NET SDK (`sdk/dotnet`)

The .NET SDK currently has no NuGet package references. Only check that it still builds (needs the .NET 8 SDK; CI also does this):

```bash
dotnet build sdk/dotnet/SimpleAuth.csproj -c Release
```

## 8. GitHub Actions

Every action in `.github/workflows/*.yml` is pinned to a **full commit SHA** with the version in a comment, e.g.:

```yaml
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
```

1. List what is in use:

```bash
grep -rhoE 'uses: [^ ]+@[^ ]+ # [^ ]+' .github/workflows | sort -u
```

2. For each action, find the latest release tag (replace `actions/checkout`):

```bash
gh release view --repo actions/checkout --json tagName -q .tagName
```

For `github/codeql-action`, `gh release view` returns a `codeql-bundle-…` tag, which is **not** the action version. Use this instead:

```bash
gh api 'repos/github/codeql-action/tags?per_page=100' --jq '.[].name' | grep -E '^v4\.[0-9]+\.[0-9]+$' | sort -V | tail -1
```

3. Get the commit SHA for that tag and replace **both** the SHA and the `# vX.Y.Z` comment on every line that uses the action:

```bash
gh api repos/actions/checkout/commits/v7.0.1 --jq .sha
```

`github/codeql-action/init`, `/autobuild`, and `/analyze` must always use the **same** SHA.

## 9. Test everything

All of these must pass before you open the pull request:

```bash
go vet ./...
go test -race -count=1 ./...
make -C test/integration test # full Docker integration suite (real LDAP, central + standalone); tears itself down
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
(cd sdk/go && go build ./... && go vet ./...)
(cd sdk/js && npm install && npm run typecheck)
docker build -t simpleauth:maintenance-check .
```

The integration suite is **not** run by CI, so running it here is the only place it runs.

## 10. Open the pull request

```bash
git add -A
git commit -m "chore: monthly maintenance $(date +%Y-%m)"
git push -u origin HEAD
gh pr create --base master --title "chore: monthly maintenance $(date +%Y-%m)" \
--body "Monthly dependency + toolchain update. All steps of docs/MAINTENANCE.md done; tests pass."
```

Merge after CI is green. In `CHANGELOG.md`, add a line under the next version's `### Changed` (or `### Security`, if you fixed a vulnerability) listing what was updated.

## 11. Releasing (only when you want a new version)

1. Make sure `CHANGELOG.md` has a section `## vX.Y.Z` for the new version. The release workflow publishes that section as the top of the GitHub Release notes, so write it for operators: highlights, upgrade notes, security fixes.
2. Set the same version in the `VERSION` file (no `v`, e.g. `2.3.0`), commit, and merge to `master`.
3. Tag and push the tag from an up-to-date `master`:

```bash
git checkout master && git pull --ff-only
make tag # creates and pushes tag v$(cat VERSION)
```

4. Watch the release run: `gh run watch` (or the repository's **Actions** tab). It builds the binaries and Docker image and creates the GitHub Release.
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/jackc/pgx/v5 v5.11.0
github.com/jcmturner/gokrb5/v8 v8.4.4
go.etcd.io/bbolt v1.5.0
golang.org/x/crypto v0.54.0
golang.org/x/crypto v0.57.0
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -27,8 +27,8 @@ require (
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sync v0.23.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/text v0.42.0 // indirect
)
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,38 @@ go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
Expand Down
Loading