@@ -22,6 +22,7 @@ import (
2222 "errors"
2323 "fmt"
2424 "net/http"
25+ "net/mail"
2526 "net/url"
2627 "time"
2728
@@ -208,6 +209,24 @@ func loginHandler(svc *AuthService, w http.ResponseWriter, r *http.Request) (int
208209// Extract custom claims
209210type upstreamOIDCclaims struct {
210211 Email string `json:"email"`
212+ // This value is present in the case of Microsoft Entra IDp
213+ // It might show the user's proxy email used during login
214+ // https://learn.microsoft.com/en-us/entra/identity/authentication/howto-authentication-use-email-signin
215+ // https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference
216+ PreferredUsername string `json:"preferred_username"`
217+ }
218+
219+ // Will retrieve the email from the preferred username if it's a valid email
220+ // or fallback to the email field
221+ func (c * upstreamOIDCclaims ) preferredEmail () string {
222+ if c .PreferredUsername != "" {
223+ // validate that this is an email since according to the Entra spec this might be a phone or username
224+ if _ , err := mail .ParseAddress (c .PreferredUsername ); err == nil {
225+ return c .PreferredUsername
226+ }
227+ }
228+
229+ return c .Email
211230}
212231
213232type errorWithCode struct {
@@ -224,7 +243,7 @@ func callbackHandler(svc *AuthService, w http.ResponseWriter, r *http.Request) (
224243 }
225244
226245 // Create user if needed
227- u , err := svc .userUseCase .FindOrCreateByEmail (ctx , claims .Email )
246+ u , err := svc .userUseCase .FindOrCreateByEmail (ctx , claims .preferredEmail () )
228247 if err != nil {
229248 return http .StatusInternalServerError , sl .LogAndMaskErr (err , svc .log )
230249 }
0 commit comments