Skip to content

Commit b2e60d8

Browse files
aatchisonclaude
andauthored
fix(tb-dev): patch tbpro login.ftl (?no_esc) + restore clustered keycloak (#8)
Two related dev-keycloak fixes: 1. Theme loginAction escaping (the real cookie_not_found / invalid_code cause). Keycloak 26.5.7 (this image, pulled via the moving quay :26.5 tag) HTML-escapes Freemarker output, so the tbpro theme's `formAction: '${url.loginAction}'` inside window._page renders with '&amp;'. The Vue login app POSTs to that URL verbatim, so Keycloak receives 'amp;execution' (the 'execution' param is lost) and returns 400 -> the browser reports cookie_not_found / invalid_code on the cross-app OIDC login. The stage ECS keycloak runs an older 26.5.x that did not escape, so it is unaffected (until its next rebuild on the moving tag). Mount a patched login.ftl (extracted from this exact image, with ?no_esc added to the four window._page URL fields that carry query strings: loginAction, registrationUrl, loginResetCredentialsUrl, p.loginUrl) over the baked theme via a subPath volume. Stable-named configMapGenerator so the volume ref resolves. The durable fix lives in thunderbird-accounts (theme ?no_esc + pin the keycloak base image tag); this override unblocks dev validation until that image ships. 2. Restore clustered keycloak (drop the replicas: 1 override -> base replicas: 2, Infinispan DNS_PING). The single-replica workaround was a misdiagnosis of the above; Infinispan shares auth sessions across pods, so clustered login works once the theme is fixed. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 76833bf commit b2e60d8

3 files changed

Lines changed: 93 additions & 6 deletions

File tree

overlays/tb-dev/keycloak/statefulset.yaml

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@ metadata:
1313
name: keycloak-customer
1414
namespace: keycloak-customer
1515
spec:
16-
# Single replica for the dev validation (matches the working ECS keycloak,
17-
# desired_count=1). With 2 replicas and no sticky sessions, the auth session
18-
# created on one pod (GET /auth) isn't found when the login POST lands on the
19-
# other pod -> cookie_not_found / invalid_code on the cross-app OIDC login.
20-
# HA (2 replicas + sticky sessions / cookie affinity) is a follow-up for prod.
21-
replicas: 1
16+
# Clustered (base replicas: 2, Infinispan DNS_PING). The single-replica
17+
# workaround tried earlier was a misdiagnosis: the cookie_not_found / invalid_code
18+
# was NOT cross-pod session loss but the tbpro theme escaping the loginAction
19+
# ('&amp;' -> dropped 'execution' param -> 400), fixed by the login.ftl override
20+
# below. Infinispan shares auth sessions across pods, so clustered login works.
2221
template:
2322
spec:
2423
containers:
@@ -55,3 +54,20 @@ spec:
5554
value: "5432"
5655
- name: KC_DB_URL_PROPERTIES
5756
value: "?sslmode=require"
57+
# Override the baked tbpro login.ftl with a copy that adds ?no_esc to
58+
# the URL fields embedded in window._page (loginAction et al). Keycloak
59+
# 26.5.7 (this image, pulled via the moving quay :26.5 tag) HTML-escapes
60+
# Freemarker output, so '${url.loginAction}' renders with '&amp;'; the Vue
61+
# login app then POSTs to a URL with literal '&amp;' -> Keycloak drops the
62+
# 'execution' param -> 400 / cookie_not_found. The stage ECS image is an
63+
# older 26.5.x that did not escape, so it is unaffected (for now).
64+
# Real fix is in thunderbird-accounts (theme ?no_esc + pin the keycloak
65+
# base tag); this subPath override unblocks dev until that image ships.
66+
volumeMounts:
67+
- name: tbpro-login-override
68+
mountPath: /opt/keycloak/themes/tbpro/login/login.ftl
69+
subPath: login.ftl
70+
volumes:
71+
- name: tbpro-login-override
72+
configMap:
73+
name: keycloak-tbpro-login-override
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<#import "template.ftl" as layout>
2+
<#import "passkeys.ftl" as passkeys>
3+
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('username','password') displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??; section>
4+
<#if section = "js">
5+
<script>
6+
window._page['currentView'] = {
7+
formAction: '${url.loginAction?no_esc}',
8+
supportUrl: '${properties.tbproContactUrl}',
9+
clientUrl: '${client.baseUrl}',
10+
// <#if realm.password && realm.registrationAllowed && !registrationDisabled??>
11+
registerUrl: '${url.registrationUrl?no_esc}',
12+
// <#else>
13+
registerUrl: '${properties.tbproSignupUrl}',
14+
// </#if>
15+
// <#if realm.resetPasswordAllowed>
16+
forgotPasswordUrl: '${url.loginResetCredentialsUrl?no_esc}',
17+
// <#else>
18+
forgotPasswordUrl: null,
19+
// </#if>
20+
rememberMe: '${(login.rememberMe)!"off"}' === 'on',
21+
loginHint: '${login.username!''}',
22+
socialProviders: [
23+
//<#if realm.password && social?? && social.providers?has_content>
24+
//<#list social.providers as p>
25+
{
26+
name: '${p.displayName!}',
27+
alias: '${p.alias}',
28+
loginUrl: '${p.loginUrl?no_esc}',
29+
iconName: '${properties.kcCommonLogoIdP!}',
30+
className: '${properties.kcFormSocialAccountNameClass!}',
31+
},
32+
//</#list>
33+
//</#if>
34+
],
35+
firstError: '${kcSanitize(messagesPerField.getFirstError("username","password"))?no_esc}',
36+
};
37+
window._l10n = {
38+
...window._l10n,
39+
loginAccountTitle: '${msg("loginAccountTitle")}',
40+
username: '${msg("username")}',
41+
usernameOrEmail: '${msg("usernameOrEmail")}',
42+
email: '${msg("email")}',
43+
password: '${msg("password")}',
44+
showPassword: '${msg("showPassword")}',
45+
hidePassword: '${msg("hidePassword")}',
46+
rememberMe: '${msg("rememberMe")}',
47+
noAccount: '${msg("noAccount")}',
48+
doLogIn: '${msg("doLogIn")}',
49+
doRegister: '${msg("doRegister")}',
50+
doForgotPassword: '${msg("doForgotPassword")}',
51+
identityProviderLoginLabel: '${msg("identity-provider-login-label")}',
52+
goToRegister: '${msg("goToRegister")}',
53+
goToRegisterAction: '${msg("goToRegisterAction")}',
54+
needHelp: '${msg("needHelp")}',
55+
needHelpAction: '${msg("needHelpAction")}',
56+
};
57+
</script>
58+
</#if>
59+
60+
</@layout.registrationLayout>

overlays/tb-dev/kustomization.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ resources:
99
- keycloak/tailscale-ingress.yaml
1010
- keycloak/securitygrouppolicy.yaml
1111

12+
# Patched tbpro login.ftl (?no_esc on the window._page URL fields) mounted over
13+
# the baked theme via subPath in the StatefulSet patch. Stable name (no hash
14+
# suffix) so the volume reference in the patch resolves. Drop once the
15+
# thunderbird-accounts image carries the fix.
16+
configMapGenerator:
17+
- name: keycloak-tbpro-login-override
18+
files:
19+
- login.ftl=keycloak/tbpro-login.ftl
20+
generatorOptions:
21+
disableNameSuffixHash: true
22+
1223
images:
1324
- name: REPLACE_MZLA_ECR/keycloak-customer
1425
newName: 718959508124.dkr.ecr.eu-central-1.amazonaws.com/keycloak-customer

0 commit comments

Comments
 (0)