@@ -90,7 +90,7 @@ func (rh *RouteHandler) SetupRoutes() {
9090 rp .CodeExchangeHandler (rh .GithubCodeExchangeCallback (), relyingParty ))
9191 } else if config .IsOpenIDSupported (provider ) {
9292 rh .c .Router .HandleFunc (constants .CallbackBasePath + "/" + provider ,
93- rp .CodeExchangeHandler (rp .UserinfoCallback (rh .OpenIDCodeExchangeCallback ( )), relyingParty ))
93+ rp .CodeExchangeHandler (rp .UserinfoCallback (rh .OpenIDCodeExchangeCallbackWithProvider ( provider )), relyingParty ))
9494 }
9595 }
9696 }
@@ -1998,17 +1998,73 @@ func (rh *RouteHandler) GithubCodeExchangeCallback() rp.CodeExchangeCallback[*oi
19981998 }
19991999}
20002000
2001- // Openid CodeExchange callback.
2001+ // Openid CodeExchange callback (legacy, kept for compatibility) .
20022002func (rh * RouteHandler ) OpenIDCodeExchangeCallback () rp.CodeExchangeUserinfoCallback [
20032003 * oidc.IDTokenClaims ,
20042004 * oidc.UserInfo ,
2005+ ] {
2006+ return rh .OpenIDCodeExchangeCallbackWithProvider ("" )
2007+ }
2008+
2009+ // OpenIDCodeExchangeCallbackWithProvider is the OIDC CodeExchange callback that supports configurable claim mapping.
2010+ // The providerName parameter is used to lookup provider-specific claim mapping configuration.
2011+ // This differs from the legacy version by allowing per-provider claim mapping based on the providerName.
2012+ func (rh * RouteHandler ) OpenIDCodeExchangeCallbackWithProvider (providerName string ) rp.CodeExchangeUserinfoCallback [
2013+ * oidc.IDTokenClaims ,
2014+ * oidc.UserInfo ,
20052015] {
20062016 return func (w http.ResponseWriter , r * http.Request , tokens * oidc.Tokens [* oidc.IDTokenClaims ], state string ,
20072017 relyingParty rp.RelyingParty , info * oidc.UserInfo ,
20082018 ) {
2009- email := info .UserInfoEmail .Email
2010- if email == "" {
2011- rh .c .Log .Error ().Msg ("failed to set user record for empty email value" )
2019+ // Extract username based on claim mapping configuration
2020+ var username string
2021+ authConfig := rh .c .Config .CopyAuthConfig ()
2022+
2023+ if authConfig != nil && authConfig .OpenID != nil && providerName != "" {
2024+ if providerConfig , ok := authConfig .OpenID .Providers [providerName ]; ok {
2025+ // Check if claim mapping is configured
2026+ if providerConfig .ClaimMapping != nil && providerConfig .ClaimMapping .Username != "" {
2027+ claimName := providerConfig .ClaimMapping .Username
2028+
2029+ // Use the configured claim
2030+ switch claimName {
2031+ case "preferred_username" :
2032+ username = info .PreferredUsername
2033+ case "email" :
2034+ username = info .UserInfoEmail .Email
2035+ case "sub" :
2036+ username = info .Subject
2037+ case "name" :
2038+ username = info .Name
2039+ default :
2040+ // Try to get from custom claims in UserInfo
2041+ if val , ok := info .Claims [claimName ].(string ); ok {
2042+ username = val
2043+ }
2044+ }
2045+
2046+ if username != "" {
2047+ rh .c .Log .Debug ().
2048+ Str ("provider" , providerName ).
2049+ Str ("claim" , claimName ).
2050+ Str ("username" , username ).
2051+ Msg ("extracted username from configured claim" )
2052+ }
2053+ }
2054+ }
2055+ }
2056+
2057+ // Fallback to email if no username was extracted
2058+ if username == "" {
2059+ username = info .UserInfoEmail .Email
2060+ rh .c .Log .Debug ().
2061+ Str ("provider" , providerName ).
2062+ Str ("username" , username ).
2063+ Msg ("using email as username (fallback)" )
2064+ }
2065+
2066+ if username == "" {
2067+ rh .c .Log .Error ().Msg ("failed to set user record for empty username value" )
20122068 w .WriteHeader (http .StatusUnauthorized )
20132069
20142070 return
@@ -2018,7 +2074,7 @@ func (rh *RouteHandler) OpenIDCodeExchangeCallback() rp.CodeExchangeUserinfoCall
20182074
20192075 val , ok := info .Claims ["groups" ].([]interface {})
20202076 if ! ok {
2021- rh .c .Log .Info ().Msgf ("failed to find any 'groups' claim for user %s in UserInfo" , email )
2077+ rh .c .Log .Info ().Msgf ("failed to find any 'groups' claim for user %s in UserInfo" , username )
20222078 }
20232079
20242080 for _ , group := range val {
@@ -2027,7 +2083,7 @@ func (rh *RouteHandler) OpenIDCodeExchangeCallback() rp.CodeExchangeUserinfoCall
20272083
20282084 val , ok = tokens .IDTokenClaims .Claims ["groups" ].([]interface {})
20292085 if ! ok {
2030- rh .c .Log .Info ().Msgf ("failed to find any 'groups' claim for user %s in IDTokenClaimsToken" , email )
2086+ rh .c .Log .Info ().Msgf ("failed to find any 'groups' claim for user %s in IDTokenClaimsToken" , username )
20312087 }
20322088
20332089 for _ , group := range val {
@@ -2037,7 +2093,7 @@ func (rh *RouteHandler) OpenIDCodeExchangeCallback() rp.CodeExchangeUserinfoCall
20372093 slices .Sort (groups )
20382094 groups = slices .Compact (groups )
20392095
2040- callbackUI , err := OAuth2Callback (rh .c , w , r , state , email , groups )
2096+ callbackUI , err := OAuth2Callback (rh .c , w , r , state , username , groups )
20412097 if err != nil {
20422098 if errors .Is (err , zerr .ErrInvalidStateCookie ) {
20432099 w .WriteHeader (http .StatusUnauthorized )
0 commit comments