Skip to content

Commit ef68298

Browse files
committed
fix: Added a "Disconnect" button to an OIDC mismatch error page
1 parent 220eb43 commit ef68298

File tree

8 files changed

+10594
-10519
lines changed

8 files changed

+10594
-10519
lines changed

assets/locales/en.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,9 @@ msgstr "The FranceConnect authentication has failed"
485485
msgid "OIDC Domain Mismatch %s %s"
486486
msgstr "To connect to %s, please disconnect first from %s"
487487

488+
msgid "Disconnect"
489+
msgstr "Disconnect"
490+
488491
msgid "Instance Blocked Login"
489492
msgstr "The Twake was blocked because of too many login attempts"
490493

assets/locales/fr.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,9 @@ msgstr "Le compte FranceConnect utilisé ne correspond pas à votre compte Twake
553553
msgid "OIDC Domain Mismatch %s %s"
554554
msgstr "Pour vous connecter à %s, veuillez d'abord vous déconnecter de %s"
555555

556+
msgid "Disconnect"
557+
msgstr "Déconnexion"
558+
556559
msgid "Instance Blocked Login"
557560
msgstr "Le Twake a été bloqué à cause de trop nombreux essais de connexion"
558561

assets/locales/ru.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,9 @@ msgstr "Используемый аккаунт FranceConnect не соотве
536536
msgid "OIDC Domain Mismatch %s %s"
537537
msgstr "Чтобы подключиться к %s, сначала отключитесь от %s"
538538

539+
msgid "Disconnect"
540+
msgstr "Отключиться"
541+
539542
msgid "Instance Blocked Login"
540543
msgstr "Twake был заблокирован из-за слишком многих попыток входа"
541544

assets/locales/vi.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ msgstr "Xác thực bằng FranceConnect không thành công"
487487
msgid "OIDC Domain Mismatch %s %s"
488488
msgstr "Để kết nối với %s, vui lòng đăng xuất khỏi %s trước"
489489

490+
msgid "Disconnect"
491+
msgstr "Ngắt kết nối"
492+
490493
msgid "Instance Blocked Login"
491494
msgstr "Twake đã bị khóa do có quá nhiều lần đăng nhập không thành công"
492495

assets/templates/error.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@
2828
{{if .Illustration}}<img src="{{asset .Domain .Illustration}}" alt="" class="illustration mb-3" />{{end}}
2929
<h1 class="h4 h2-md mb-3 text-center">{{if .ErrorTitle}}{{t .ErrorTitle}}{{else}}{{t "Error Title"}}{{end}}</h1>
3030
<div class="mb-2 mb-md-3">
31-
{{$err := t .Error}}
31+
{{$err := ""}}
32+
{{if .ErrorArgs}}
33+
{{$err = tArgs .Error .ErrorArgs}}
34+
{{else}}
35+
{{$err = t .Error}}
36+
{{end}}
3237
{{$arr := split $err "\n"}}
3338
{{range $i, $p := $arr}}
3439
{{if ne $p ""}}<p class="text-center mb-2">{{$p}}</p>{{end}}

web/oidc/oidc.go

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ type DomainMismatchError struct {
5050
ActualDomain string // The instance from the OIDC token
5151
}
5252

53+
// TranslationKey returns the i18n key for translating this error.
54+
func (e *DomainMismatchError) TranslationKey() string {
55+
return "OIDC Domain Mismatch %s %s"
56+
}
57+
58+
// TranslationArgs returns the arguments for the translation.
59+
func (e *DomainMismatchError) TranslationArgs() []interface{} {
60+
return []interface{}{e.ExpectedDomain, e.ActualDomain}
61+
}
62+
5363
func (e *DomainMismatchError) Error() string {
5464
return fmt.Sprintf("OIDC Domain Mismatch %s %s", e.ExpectedDomain, e.ActualDomain)
5565
}
@@ -378,7 +388,18 @@ func Login(c echo.Context) error {
378388
}
379389

380390
if err := checkDomainFromUserInfo(conf, inst, token); err != nil {
381-
return renderError(c, inst, http.StatusBadRequest, err.Error())
391+
extras := map[string]interface{}{}
392+
errMsg := err.Error()
393+
var dmErr *DomainMismatchError
394+
if errors.As(err, &dmErr) {
395+
extras["ErrorArgs"] = dmErr.TranslationArgs()
396+
errMsg = dmErr.TranslationKey()
397+
if logoutURL := getOIDCLogoutURL(inst.ContextName); logoutURL != "" {
398+
extras["Button"] = "Disconnect"
399+
extras["ButtonURL"] = logoutURL
400+
}
401+
}
402+
return renderError(c, inst, http.StatusBadRequest, errMsg, extras)
382403
}
383404
}
384405

@@ -454,7 +475,17 @@ func TwoFactor(c echo.Context) error {
454475
return renderError(c, inst, http.StatusBadRequest, "No OpenID Connect is configured.")
455476
}
456477
if err := checkDomainFromUserInfo(conf, inst, accessToken); err != nil {
457-
return renderError(c, inst, http.StatusBadRequest, err.Error())
478+
extras := map[string]interface{}{}
479+
errMsg := err.Error()
480+
if dmErr, ok := err.(*DomainMismatchError); ok {
481+
extras["ErrorArgs"] = dmErr.TranslationArgs()
482+
errMsg = dmErr.TranslationKey()
483+
if logoutURL := getOIDCLogoutURL(inst.ContextName); logoutURL != "" {
484+
extras["Button"] = "Disconnect"
485+
extras["ButtonURL"] = logoutURL
486+
}
487+
}
488+
return renderError(c, inst, http.StatusBadRequest, errMsg, extras)
458489
}
459490

460491
if inst.ValidateTwoFactorTrustedDeviceSecret(c.Request(), trustedDeviceToken) {
@@ -1270,15 +1301,15 @@ func loadKey(raw *jwKey) (interface{}, error) {
12701301
return &key, nil
12711302
}
12721303

1273-
func renderError(c echo.Context, inst *instance.Instance, code int, msg string) error {
1304+
func renderError(c echo.Context, inst *instance.Instance, code int, msg string, extras ...map[string]interface{}) error {
12741305
if inst == nil {
12751306
inst = &instance.Instance{
12761307
Domain: c.Request().Host,
12771308
ContextName: config.DefaultInstanceContext,
12781309
Locale: consts.DefaultLocale,
12791310
}
12801311
}
1281-
return c.Render(code, "error.html", echo.Map{
1312+
params := echo.Map{
12821313
"Domain": inst.ContextualDomain(),
12831314
"ContextName": inst.ContextName,
12841315
"Locale": inst.Locale,
@@ -1287,7 +1318,22 @@ func renderError(c echo.Context, inst *instance.Instance, code int, msg string)
12871318
"Illustration": "/images/generic-error.svg",
12881319
"Error": msg,
12891320
"SupportEmail": inst.SupportEmailAddress(),
1290-
})
1321+
}
1322+
// Merge any extra params (Button, ButtonURL, etc.)
1323+
for _, extra := range extras {
1324+
for k, v := range extra {
1325+
params[k] = v
1326+
}
1327+
}
1328+
return c.Render(code, "error.html", params)
1329+
}
1330+
1331+
func getOIDCLogoutURL(contextName string) string {
1332+
a := config.GetConfig().Authentication
1333+
delegated, _ := a[contextName].(map[string]interface{})
1334+
oidc, _ := delegated["oidc"].(map[string]interface{})
1335+
u, _ := oidc["logout_url"].(string)
1336+
return u
12911337
}
12921338

12931339
// Routes setup routing for OpenID Connect routes.

web/statik/handler.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ func NewDirRenderer(assetsPath string) (AssetRenderer, error) {
106106
middlewares.FuncsMap = template.FuncMap{
107107
"t": fmt.Sprintf,
108108
"tHTML": fmt.Sprintf,
109+
"tArgs": func(key string, args interface{}) string { return fmt.Sprintf(key, args.([]interface{})...) },
109110
"split": strings.Split,
110111
"replace": strings.Replace,
111112
"hasSuffix": strings.HasSuffix,
@@ -132,6 +133,7 @@ func NewRenderer() (AssetRenderer, error) {
132133
middlewares.FuncsMap = template.FuncMap{
133134
"t": fmt.Sprintf,
134135
"tHTML": fmt.Sprintf,
136+
"tArgs": func(key string, args interface{}) string { return fmt.Sprintf(key, args.([]interface{})...) },
135137
"split": strings.Split,
136138
"replace": strings.Replace,
137139
"hasSuffix": strings.HasSuffix,
@@ -171,12 +173,18 @@ func (r *renderer) Render(w io.Writer, name string, data interface{}, c echo.Con
171173
funcMap = template.FuncMap{
172174
"t": i.Translate,
173175
"tHTML": i18n.TranslatorHTML(i.Locale, i.ContextName),
176+
"tArgs": func(key string, args []interface{}) string {
177+
return i18n.Translate(key, i.Locale, i.ContextName, args...)
178+
},
174179
}
175180
} else {
176181
lang := GetLanguageFromHeader(c.Request().Header)
177182
funcMap = template.FuncMap{
178183
"t": i18n.Translator(lang, ""),
179184
"tHTML": i18n.TranslatorHTML(lang, ""),
185+
"tArgs": func(key string, args []interface{}) string {
186+
return i18n.Translate(key, lang, "", args...)
187+
},
180188
}
181189
}
182190
var t *template.Template

0 commit comments

Comments
 (0)