@@ -63,26 +63,32 @@ type Config struct {
6363 DomainHint string `json:"domainHint"`
6464
6565 Scopes []string `json:"scopes"` // defaults to scopeUser (user.read)
66+
67+ // PreferredUsernameField allows users to set the field to any of the
68+ // following values: "name", "email", "mailNickname" or "onPremisesSamAccountName".
69+ // If unset, the preferred_username field will remain empty.
70+ PreferredUsernameField string `json:"preferredUsernameField"`
6671}
6772
6873// Open returns a strategy for logging in through Microsoft.
6974func (c * Config ) Open (id string , logger * slog.Logger ) (connector.Connector , error ) {
7075 m := microsoftConnector {
71- apiURL : strings .TrimSuffix (c .APIURL , "/" ),
72- graphURL : strings .TrimSuffix (c .GraphURL , "/" ),
73- redirectURI : c .RedirectURI ,
74- clientID : c .ClientID ,
75- clientSecret : c .ClientSecret ,
76- tenant : c .Tenant ,
77- onlySecurityGroups : c .OnlySecurityGroups ,
78- groups : c .Groups ,
79- groupNameFormat : c .GroupNameFormat ,
80- useGroupsAsWhitelist : c .UseGroupsAsWhitelist ,
81- logger : logger .With (slog .Group ("connector" , "type" , "microsoft" , "id" , id )),
82- emailToLowercase : c .EmailToLowercase ,
83- promptType : c .PromptType ,
84- domainHint : c .DomainHint ,
85- scopes : c .Scopes ,
76+ apiURL : strings .TrimSuffix (c .APIURL , "/" ),
77+ graphURL : strings .TrimSuffix (c .GraphURL , "/" ),
78+ redirectURI : c .RedirectURI ,
79+ clientID : c .ClientID ,
80+ clientSecret : c .ClientSecret ,
81+ tenant : c .Tenant ,
82+ onlySecurityGroups : c .OnlySecurityGroups ,
83+ groups : c .Groups ,
84+ groupNameFormat : c .GroupNameFormat ,
85+ useGroupsAsWhitelist : c .UseGroupsAsWhitelist ,
86+ logger : logger .With (slog .Group ("connector" , "type" , "microsoft" , "id" , id )),
87+ emailToLowercase : c .EmailToLowercase ,
88+ promptType : c .PromptType ,
89+ domainHint : c .DomainHint ,
90+ scopes : c .Scopes ,
91+ preferredUsernameField : c .PreferredUsernameField ,
8692 }
8793
8894 if m .apiURL == "" {
@@ -123,21 +129,22 @@ var (
123129)
124130
125131type microsoftConnector struct {
126- apiURL string
127- graphURL string
128- redirectURI string
129- clientID string
130- clientSecret string
131- tenant string
132- onlySecurityGroups bool
133- groupNameFormat GroupNameFormat
134- groups []string
135- useGroupsAsWhitelist bool
136- logger * slog.Logger
137- emailToLowercase bool
138- promptType string
139- domainHint string
140- scopes []string
132+ apiURL string
133+ graphURL string
134+ redirectURI string
135+ clientID string
136+ clientSecret string
137+ tenant string
138+ onlySecurityGroups bool
139+ groupNameFormat GroupNameFormat
140+ groups []string
141+ useGroupsAsWhitelist bool
142+ logger * slog.Logger
143+ emailToLowercase bool
144+ promptType string
145+ domainHint string
146+ scopes []string
147+ preferredUsernameField string
141148}
142149
143150func (c * microsoftConnector ) isOrgTenant () bool {
@@ -223,6 +230,7 @@ func (c *microsoftConnector) HandleCallback(s connector.Scopes, connData []byte,
223230 Email : user .Email ,
224231 EmailVerified : true ,
225232 }
233+ c .setPreferredUsername (& identity , user )
226234
227235 if c .groupsRequired (s .Groups ) {
228236 groups , err := c .getGroups (ctx , client , user .ID )
@@ -314,6 +322,7 @@ func (c *microsoftConnector) Refresh(ctx context.Context, s connector.Scopes, id
314322
315323 identity .Username = user .Name
316324 identity .Email = user .Email
325+ c .setPreferredUsername (& identity , user )
317326
318327 if c .groupsRequired (s .Groups ) {
319328 groups , err := c .getGroups (ctx , client , user .ID )
@@ -326,6 +335,23 @@ func (c *microsoftConnector) Refresh(ctx context.Context, s connector.Scopes, id
326335 return identity , nil
327336}
328337
338+ func (c * microsoftConnector ) setPreferredUsername (identity * connector.Identity , u user ) {
339+ switch c .preferredUsernameField {
340+ case "name" :
341+ identity .PreferredUsername = u .Name
342+ case "email" :
343+ identity .PreferredUsername = u .Email
344+ case "mailNickname" :
345+ identity .PreferredUsername = u .MailNickname
346+ case "onPremisesSamAccountName" :
347+ identity .PreferredUsername = u .OnPremisesSamAccountName
348+ default :
349+ if c .preferredUsernameField != "" {
350+ c .logger .Warn ("preferred_username left empty. Invalid microsoft field mapped to preferred_username" , "field" , c .preferredUsernameField )
351+ }
352+ }
353+ }
354+
329355// https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/resources/user
330356// id - The unique identifier for the user. Inherited from
331357//
@@ -342,22 +368,37 @@ func (c *microsoftConnector) Refresh(ctx context.Context, s connector.Scopes, id
342368//
343369// The UPN is an Internet-style login name for the user
344370// based on the Internet standard RFC 822. By convention,
345- // this should map to the user' s email name. The general
371+ // this should map to the user’ s email name. The general
346372// format is alias@domain, where domain must be present in
347373// the tenant’s collection of verified domains. This
348374// property is required when a user is created. The
349375// verified domains for the tenant can be accessed from the
350376// verifiedDomains property of organization. Supports
351377// $filter and $orderby.
378+ //
379+ // mailNickname - The mail alias for the user.
380+ //
381+ // This property must be specified when a user is created.
382+ // Maximum length is 64 characters. Supports $filter.
383+ //
384+ // onPremisesSamAccountName - Contains the on-premises SAM account name
385+ //
386+ // synchronized from the on-premises directory.
387+ // This property is only populated for customers
388+ // who are synchronizing their on-premises directory
389+ // to Azure Active Directory via Azure AD Connect.
390+ // Read-only.
352391type user struct {
353- ID string `json:"id"`
354- Name string `json:"displayName"`
355- Email string `json:"userPrincipalName"`
392+ ID string `json:"id"`
393+ Name string `json:"displayName"`
394+ Email string `json:"userPrincipalName"`
395+ MailNickname string `json:"mailNickname"`
396+ OnPremisesSamAccountName string `json:"onPremisesSamAccountName"`
356397}
357398
358399func (c * microsoftConnector ) user (ctx context.Context , client * http.Client ) (u user , err error ) {
359400 // https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_get
360- req , err := http .NewRequest ("GET" , c .graphURL + "/v1.0/me?$select=id,displayName,userPrincipalName" , nil )
401+ req , err := http .NewRequest ("GET" , c .graphURL + "/v1.0/me?$select=id,displayName,userPrincipalName,mailNickname,onPremisesSamAccountName " , nil )
361402 if err != nil {
362403 return u , fmt .Errorf ("new req: %v" , err )
363404 }
0 commit comments