Skip to content

Commit 0e35ff4

Browse files
authored
Merge pull request #30 from markrai/bugfix/oidc-ui-debug
fix(web): rebuild dist for OIDC SSO button and auth status flags
2 parents 7973ec2 + 7fbd161 commit 0e35ff4

7 files changed

Lines changed: 53 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
> **Upgrades:** No breaking changes in **3.7.x** / **3.8.x** / **3.9.x** unless noted below.
44
55

6+
## [3.9.1] - 2026-04-04
7+
8+
### Fixes
9+
10+
- **OIDC auth UI (embedded `dist/`)** — Rebuilt **`internal/httpapi/web/dist/`** so the compiled bundle matches **`modules/`**: router applies **`oidcEnabled`** / **`localAuthEnabled`** from **`GET /api/auth/status`**, and the login screen shows **Continue with SSO** when OIDC is configured (previously only TypeScript sources were updated in **3.9.0**, so production builds loading **`dist/router.js`** did not surface the SSO button).
11+
12+
---
13+
614
## [3.9.0] - 2026-04-03
715

816
### Features

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<p align="center">
22
<img width="372" src="internal/httpapi/web/githublogo.png" alt="scrumboy logo" />
33
<br />
4-
<img src="https://img.shields.io/badge/version-v3.9.0-blue" alt="version" />
4+
<img src="https://img.shields.io/badge/version-v3.9.1-blue" alt="version" />
55
<a href="LICENSE">
66
<img src="https://img.shields.io/badge/license-AGPL--v3-orange" alt="license" />
77
</a>

internal/httpapi/web/dist/router.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { apiFetch } from './api.js';
22
import { renderAuth, renderResetPassword, renderProjects, renderDashboard, renderBoard, renderNotFound, stopBoardEvents } from './views/index.js';
3-
import { getAuthStatusChecked, getUser, getBootstrapAvailable, getAuthStatusAvailable, getBoard } from './state/selectors.js';
4-
import { setAuthStatusChecked, setAuthStatusAvailable, setUser, setBootstrapAvailable, setRoute, setTag, setSearch, setSlug, setProjectId, setBoard, resetUserScopedState, setTagColors, setOpenTodoSegment, hydrateDashboardTodoSortFromServer } from './state/mutations.js';
3+
import { getAuthStatusChecked, getUser, getBootstrapAvailable, getAuthStatusAvailable, getBoard, getOidcEnabled, getLocalAuthEnabled } from './state/selectors.js';
4+
import { setAuthStatusChecked, setAuthStatusAvailable, setUser, setBootstrapAvailable, setOidcEnabled, setLocalAuthEnabled, setRoute, setTag, setSearch, setSlug, setProjectId, setBoard, resetUserScopedState, setTagColors, setOpenTodoSegment, hydrateDashboardTodoSortFromServer } from './state/mutations.js';
55
import { loadUserTheme } from './theme.js';
66
let isRouting = false;
77
let rerouteRequested = false;
@@ -73,6 +73,8 @@ async function routeOnce() {
7373
}
7474
setUser(newUser);
7575
setBootstrapAvailable(!!(st && st.bootstrapAvailable));
76+
setOidcEnabled(!!(st && st.oidcEnabled));
77+
setLocalAuthEnabled(st && st.localAuthEnabled !== false);
7678
// Load full profile (including avatar) when logged in; /api/auth/status omits image to keep it lean
7779
if (newUser) {
7880
try {
@@ -152,7 +154,7 @@ async function routeOnce() {
152154
if (getUser() == null && getAuthStatusChecked() && getAuthStatusAvailable()) {
153155
if (r.name === "projects" || r.name === "dashboard" || r.name === "boardBySlug") {
154156
console.log("Router: showing auth UI (not logged in)");
155-
renderAuth({ next: window.location.pathname + window.location.search, bootstrap: getBootstrapAvailable() });
157+
renderAuth({ next: window.location.pathname + window.location.search, bootstrap: getBootstrapAvailable(), oidcEnabled: getOidcEnabled(), localAuthEnabled: getLocalAuthEnabled() });
156158
return;
157159
}
158160
}
@@ -197,7 +199,7 @@ async function routeOnce() {
197199
console.error("Router: error rendering board:", err);
198200
if (err && err.status === 401) {
199201
// Only show auth UI for 401s (entry points). Resource endpoints should generally return 404 when unauthenticated.
200-
renderAuth({ next: window.location.pathname + window.location.search, bootstrap: false });
202+
renderAuth({ next: window.location.pathname + window.location.search, bootstrap: false, oidcEnabled: getOidcEnabled(), localAuthEnabled: getLocalAuthEnabled() });
201203
return;
202204
}
203205
throw err;

internal/httpapi/web/dist/state/mutations.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ export function setAuthStatusChecked(checked) {
7272
export function setBootstrapAvailable(available) {
7373
current._bootstrapAvailable = available;
7474
}
75+
export function setOidcEnabled(enabled) {
76+
current._oidcEnabled = enabled;
77+
}
78+
export function setLocalAuthEnabled(enabled) {
79+
current._localAuthEnabled = enabled;
80+
}
7581
export function setProjectsTab(tab) {
7682
current.projectsTab = tab;
7783
}

internal/httpapi/web/dist/state/selectors.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ export function getAuthStatusChecked() {
6464
export function getBootstrapAvailable() {
6565
return current._bootstrapAvailable;
6666
}
67+
export function getOidcEnabled() {
68+
return !!current._oidcEnabled;
69+
}
70+
export function getLocalAuthEnabled() {
71+
return current._localAuthEnabled !== false;
72+
}
6773
export function getProjectsTab() {
6874
return current.projectsTab;
6975
}

internal/httpapi/web/dist/views/auth.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@ import { showToast, getAppVersion, escapeHTML, redirectAfterAuth } from '../util
44
export function renderAuth(opts = {}) {
55
const next = opts.next || (window.location.pathname + window.location.search);
66
const bootstrapAvailable = !!opts.bootstrap;
7+
const oidcEnabled = !!opts.oidcEnabled;
8+
const localAuthEnabled = opts.localAuthEnabled !== false;
79
const version = getAppVersion();
10+
const ssoButtonHTML = oidcEnabled
11+
? `<a class="btn btn--sso" href="/api/auth/oidc/login?return_to=${encodeURIComponent(next)}">Continue with SSO</a>`
12+
: '';
13+
const showLocalForm = localAuthEnabled;
14+
const dividerHTML = oidcEnabled && showLocalForm
15+
? `<div class="auth-divider"><span>or</span></div>`
16+
: '';
817
app.innerHTML = `
918
<div class="page page--auth">
1019
<div class="topbar">
@@ -21,6 +30,9 @@ export function renderAuth(opts = {}) {
2130
<div class="muted" style="margin-bottom: 12px;">
2231
Authentication is enabled for this instance. Anonymous boards remain shareable by URL; durable projects require sign-in.
2332
</div>
33+
${ssoButtonHTML}
34+
${dividerHTML}
35+
${showLocalForm ? `
2436
<form id="authForm" class="stack">
2537
${bootstrapAvailable ? `<input class="input" id="authName" placeholder="Name" maxlength="200" autocomplete="name" required />` : ``}
2638
<input class="input" id="authEmail" placeholder="Email" maxlength="200" autocomplete="email" required />
@@ -37,6 +49,7 @@ export function renderAuth(opts = {}) {
3749
: `<button class="btn" type="submit" id="loginBtn">Login</button>`}
3850
</div>
3951
</form>
52+
` : ''}
4053
</div>
4154
</div>
4255
${version ? `<div class="app-version">v${escapeHTML(version)}</div>` : ''}
@@ -58,6 +71,18 @@ export function renderAuth(opts = {}) {
5871
pwToggle.setAttribute("title", isPassword ? "Hide password" : "Show password");
5972
});
6073
}
74+
const params = new URLSearchParams(window.location.search);
75+
const oidcError = params.get('oidc_error');
76+
if (oidcError) {
77+
const msgs = {
78+
state_invalid: 'Login session expired or invalid. Please try again.',
79+
provider: 'The identity provider returned an error.',
80+
token: 'Authentication failed. Please try again.',
81+
email: 'A verified email address is required.',
82+
};
83+
showToast(msgs[oidcError] || 'Authentication failed.');
84+
window.history.replaceState({}, '', window.location.pathname);
85+
}
6186
if (bootstrapAvailable) {
6287
const bootstrapBtn = document.getElementById("bootstrapBtn");
6388
if (bootstrapBtn) {

internal/version/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package version
88
//
99
// Convention: Update when releasing (e.g., "1.0.0", "1.1.0"); match git tags
1010
// (e.g., tag "v1.0.0" should have Version = "1.0.0").
11-
const Version = "3.9.0"
11+
const Version = "3.9.1"
1212

1313
// ExportFormatVersion is the version of the backup/export data format.
1414
// Only increment this when the ExportData structure changes in a breaking way.

0 commit comments

Comments
 (0)