4343 ErrIdentityProvider = errors .New ("error from the identity provider" )
4444)
4545
46+ // DomainMismatchError is returned when the user tries to connect to an
47+ // instance but has an active OIDC session for a different instance.
48+ type DomainMismatchError struct {
49+ ExpectedDomain string // The instance the user is trying to access
50+ ActualDomain string // The instance from the OIDC token
51+ }
52+
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+
63+ func (e * DomainMismatchError ) Error () string {
64+ return fmt .Sprintf ("OIDC Domain Mismatch %s %s" , e .ExpectedDomain , e .ActualDomain )
65+ }
66+
4667// extractSessionID extracts the session ID (sid) from an id_token.
4768func extractSessionID (idToken string ) string {
4869 if idToken == "" {
@@ -367,7 +388,18 @@ func Login(c echo.Context) error {
367388 }
368389
369390 if err := checkDomainFromUserInfo (conf , inst , token ); err != nil {
370- 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 )
371403 }
372404 }
373405
@@ -443,7 +475,17 @@ func TwoFactor(c echo.Context) error {
443475 return renderError (c , inst , http .StatusBadRequest , "No OpenID Connect is configured." )
444476 }
445477 if err := checkDomainFromUserInfo (conf , inst , accessToken ); err != nil {
446- 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 )
447489 }
448490
449491 if inst .ValidateTwoFactorTrustedDeviceSecret (c .Request (), trustedDeviceToken ) {
@@ -1054,7 +1096,7 @@ func checkDomainFromUserInfo(conf *Config, inst *instance.Instance, token string
10541096 }
10551097 if domain != inst .Domain {
10561098 logger .WithNamespace ("oidc" ).Errorf ("Invalid domains: %s != %s" , domain , inst .Domain )
1057- return ErrAuthenticationFailed
1099+ return & DomainMismatchError { ExpectedDomain : inst . Domain , ActualDomain : domain }
10581100 }
10591101 return nil
10601102}
@@ -1259,15 +1301,15 @@ func loadKey(raw *jwKey) (interface{}, error) {
12591301 return & key , nil
12601302}
12611303
1262- 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 {
12631305 if inst == nil {
12641306 inst = & instance.Instance {
12651307 Domain : c .Request ().Host ,
12661308 ContextName : config .DefaultInstanceContext ,
12671309 Locale : consts .DefaultLocale ,
12681310 }
12691311 }
1270- return c . Render ( code , "error.html" , echo.Map {
1312+ params := echo.Map {
12711313 "Domain" : inst .ContextualDomain (),
12721314 "ContextName" : inst .ContextName ,
12731315 "Locale" : inst .Locale ,
@@ -1276,7 +1318,22 @@ func renderError(c echo.Context, inst *instance.Instance, code int, msg string)
12761318 "Illustration" : "/images/generic-error.svg" ,
12771319 "Error" : msg ,
12781320 "SupportEmail" : inst .SupportEmailAddress (),
1279- })
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
12801337}
12811338
12821339// Routes setup routing for OpenID Connect routes.
0 commit comments