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
34 changes: 34 additions & 0 deletions CHANGELOG.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,40 @@
"grokify"
],
"releases": [
{
"version": "v0.30.3",
"date": "2026-11-02",
"highlights": [
{
"description": "SystemAuth's sign-in, sign-out, consent, sign-up and email pages are styled: a centered card in light and dark, with the sign-in options as buttons",
"commit": "7564e10"
}
],
"added": [
{
"description": "`/assets/systemauth.css` (`PageStylePath`): the pages' stylesheet, embedded and served from the issuer's origin, linked by every page with its content hash",
"commit": "7564e10"
}
],
"changed": [
{
"description": "The sign-in page lists its options in a `nav`, as buttons",
"commit": "7564e10"
}
],
"tests": [
{
"description": "The stylesheet is served and cached by version, every page links it, and the no-inline-code check allows only that same-origin link",
"commit": "1185a05"
}
],
"documentation": [
{
"description": "The deployment guide's security headers section describes the stylesheet",
"commit": "160ff73"
}
]
},
{
"version": "v0.30.2",
"date": "2026-11-02",
Expand Down
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro

## [Unreleased]

## [v0.30.3] - 2026-11-02

### Highlights

- SystemAuth's sign-in, sign-out, consent, sign-up and email pages are styled: a centered card in light and dark, with the sign-in options as buttons

### Added

- `/assets/systemauth.css` (`PageStylePath`): the pages' stylesheet, embedded and served from the issuer's origin, linked by every page with its content hash ([`7564e10`](https://github.com/plexusone/systemforge/commit/7564e10))

### Changed

- The sign-in page lists its options in a `nav`, as buttons ([`7564e10`](https://github.com/plexusone/systemforge/commit/7564e10))

### Documentation

- The deployment guide's security headers section describes the stylesheet ([`160ff73`](https://github.com/plexusone/systemforge/commit/160ff73))

### Tests

- The stylesheet is served and cached by version, every page links it, and the no-inline-code check allows only that same-origin link ([`1185a05`](https://github.com/plexusone/systemforge/commit/1185a05))

## [v0.30.2] - 2026-11-02

### Highlights
Expand Down Expand Up @@ -1703,7 +1725,8 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro
- GitHub Actions lint workflow with golangci-lint ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5))
- Dependabot configuration for Go modules and GitHub Actions ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5))

[unreleased]: https://github.com/plexusone/systemforge/compare/v0.30.2...HEAD
[unreleased]: https://github.com/plexusone/systemforge/compare/v0.30.3...HEAD
[v0.30.3]: https://github.com/plexusone/systemforge/compare/v0.30.2...v0.30.3
[v0.30.2]: https://github.com/plexusone/systemforge/compare/v0.30.1...v0.30.2
[v0.30.1]: https://github.com/plexusone/systemforge/compare/v0.30.0...v0.30.1
[v0.30.0]: https://github.com/plexusone/systemforge/compare/v0.29.0...v0.30.0
Expand Down
25 changes: 24 additions & 1 deletion docs/releases/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro

## [Unreleased]

## [v0.30.3] - 2026-11-02

### Highlights

- SystemAuth's sign-in, sign-out, consent, sign-up and email pages are styled: a centered card in light and dark, with the sign-in options as buttons

### Added

- `/assets/systemauth.css` (`PageStylePath`): the pages' stylesheet, embedded and served from the issuer's origin, linked by every page with its content hash ([`7564e10`](https://github.com/plexusone/systemforge/commit/7564e10))

### Changed

- The sign-in page lists its options in a `nav`, as buttons ([`7564e10`](https://github.com/plexusone/systemforge/commit/7564e10))

### Documentation

- The deployment guide's security headers section describes the stylesheet ([`160ff73`](https://github.com/plexusone/systemforge/commit/160ff73))

### Tests

- The stylesheet is served and cached by version, every page links it, and the no-inline-code check allows only that same-origin link ([`1185a05`](https://github.com/plexusone/systemforge/commit/1185a05))

## [v0.30.2] - 2026-11-02

### Highlights
Expand Down Expand Up @@ -1703,7 +1725,8 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro
- GitHub Actions lint workflow with golangci-lint ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5))
- Dependabot configuration for Go modules and GitHub Actions ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5))

[unreleased]: https://github.com/plexusone/systemforge/compare/v0.30.2...HEAD
[unreleased]: https://github.com/plexusone/systemforge/compare/v0.30.3...HEAD
[v0.30.3]: https://github.com/plexusone/systemforge/compare/v0.30.2...v0.30.3
[v0.30.2]: https://github.com/plexusone/systemforge/compare/v0.30.1...v0.30.2
[v0.30.1]: https://github.com/plexusone/systemforge/compare/v0.30.0...v0.30.1
[v0.30.0]: https://github.com/plexusone/systemforge/compare/v0.29.0...v0.30.0
Expand Down
6 changes: 5 additions & 1 deletion docs/releases/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ Release notes, one file per version, newest first. The
[changelog](changelog.md) is generated from `CHANGELOG.json` and lists every
change with its commit.

The latest release, [v0.30.2](v0.30.2.md), keeps a signed-out browser
The latest release, [v0.30.3](v0.30.3.md), styles SystemAuth's pages
with a same-origin stylesheet, in light and dark. It adds no breaking
change.

Before it, [v0.30.2](v0.30.2.md) kept a signed-out browser
signed out with a single sign-in provider: the sign-in page waits for a
click instead of redirecting. Read its upgrade notes.

Expand Down
26 changes: 26 additions & 0 deletions docs/releases/v0.30.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# v0.30.3

**Release Date:** 2026-11-02

SystemAuth's pages are styled:

- **A plain, modern look.** The sign-in, sign-out, consent, sign-up,
waitlist, age and email pages were unstyled HTML. They now link one
stylesheet: a centered card on a soft background, in light and dark
following the browser, with the sign-in options as full-width buttons,
clear form fields, and notices in color.
- **Nothing loosened.** The stylesheet is embedded in the binary and served
at `/assets/systemauth.css` from the issuer's origin, which the pages'
content security policy (`default-src 'self'`) already allows: still no
script and no inline style. The link carries the stylesheet's content
hash, so browsers cache it until it changes.

It adds no breaking change.

## Upgrade notes

1. **Pass `/assets/` through** any proxy in front of SystemAuth, as it
passes the pages themselves.

The pages are not themed per application yet. See
[Security headers](../systemauth/deployment.md#security-headers).
7 changes: 6 additions & 1 deletion docs/systemauth/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,12 @@ pages, carries:
| `Referrer-Policy` | `same-origin` |
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains`, when the issuer is https |

The pages run no script and no inline style. Browsers check `form-action`
The pages run no script and no inline style. They link one stylesheet,
`/assets/systemauth.css`, served by SystemAuth from its own origin, which
`default-src 'self'` allows: a plain card in light and dark, with the
sign-in options as buttons. The link carries the stylesheet's content hash,
so a browser caches it until it changes. A proxy in front of SystemAuth must
pass `/assets/` through. Browsers check `form-action`
on every redirect that follows a form submission, so a page whose form
ends at another origin adds it: the consent page adds the origins of the
client's redirect URIs, the logout page the `allowed_redirect_origins`,
Expand Down
181 changes: 181 additions & 0 deletions identity/systemauth/assets/page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/* SystemAuth's pages: sign-in, sign-out, consent, sign-up and email. It styles
the pages' plain elements, with no classes, in light and dark. */
:root {
color-scheme: light dark;
--bg: #f6f7f9;
--card: #ffffff;
--text: #1f2328;
--muted: #59636e;
--border: #d1d9e0;
--accent: #1f2328;
--accent-text: #ffffff;
--accent-hover: #3d444d;
--focus: #0969da;
--alert-bg: #fff1f0;
--alert-text: #a40e26;
--status-bg: #eef6ff;
--status-text: #0b4a8b;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0d1117;
--card: #161b22;
--text: #e6edf3;
--muted: #9198a1;
--border: #30363d;
--accent: #e6edf3;
--accent-text: #0d1117;
--accent-hover: #c9d1d9;
--focus: #4493f8;
--alert-bg: #3c1618;
--alert-text: #ffa198;
--status-bg: #0c2d4f;
--status-text: #a5d6ff;
}
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 24px 16px;
background: var(--bg);
color: var(--text);
font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
main {
width: 100%;
max-width: 400px;
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 32px 28px;
box-shadow: 0 1px 3px rgb(0 0 0 / 0.06), 0 8px 24px rgb(0 0 0 / 0.06);
}
main > img {
display: block;
margin: 0 auto 16px;
border-radius: 12px;
}
h1 {
margin: 0 0 8px;
font-size: 22px;
font-weight: 600;
line-height: 1.3;
text-align: center;
}
h1 + p {
margin-top: 0;
color: var(--muted);
text-align: center;
}
p {
margin: 12px 0;
}
a {
color: var(--focus);
}
ul {
margin: 16px 0;
padding-left: 20px;
}
li + li {
margin-top: 4px;
}
nav {
display: grid;
gap: 10px;
margin-top: 24px;
}
nav a,
button {
display: block;
width: 100%;
padding: 10px 16px;
border: 1px solid var(--accent);
border-radius: 8px;
background: var(--accent);
color: var(--accent-text);
font: inherit;
font-weight: 500;
text-align: center;
text-decoration: none;
cursor: pointer;
}
nav a:hover,
button:hover {
background: var(--accent-hover);
border-color: var(--accent-hover);
}
button + button {
margin-top: 10px;
}
button[value="deny"],
button[value="cancel"] {
background: transparent;
color: var(--text);
border-color: var(--border);
}
button[value="deny"]:hover,
button[value="cancel"]:hover {
background: var(--bg);
}
form {
margin-top: 20px;
}
label {
display: block;
margin: 12px 0 4px;
font-weight: 500;
}
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),
select,
textarea {
display: block;
width: 100%;
padding: 9px 12px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--card);
color: var(--text);
font: inherit;
}
input[type="checkbox"],
input[type="radio"] {
margin-right: 6px;
}
input + button,
select + button,
textarea + button,
label + button {
margin-top: 20px;
}
:focus-visible {
outline: 2px solid var(--focus);
outline-offset: 2px;
}
[role="alert"],
[role="status"] {
padding: 10px 12px;
border-radius: 8px;
}
[role="alert"] {
background: var(--alert-bg);
color: var(--alert-text);
}
[role="status"] {
background: var(--status-bg);
color: var(--status-text);
}
small,
.muted {
color: var(--muted);
}
2 changes: 1 addition & 1 deletion identity/systemauth/email_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func emailChangeCSRF(sessionToken string) string {
}

var accountEmailTemplate = template.Must(template.New("account-email").Parse(`<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">` + pageStyleLink + `
<title>{{.Title}}</title></head>
<body><main><h1>{{.Title}}</h1>
{{if .Message}}<p role="status">{{.Message}}</p>{{end}}
Expand Down
2 changes: 1 addition & 1 deletion identity/systemauth/email_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (sl *socialLogin) issueSignInToken(ctx context.Context, cookie, email strin
}

var verifyEmailTemplate = template.Must(template.New("verify-email").Parse(`<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">` + pageStyleLink + `
<title>{{.Title}}</title></head>
<body><main><h1>{{.Title}}</h1>
{{if .Message}}<p role="status">{{.Message}}</p>{{end}}
Expand Down
Loading
Loading