feat: added organizationId/employee id as preferred username (#3237)

Signed-off-by: Drew Foehn <drew@pixelburn.net>
Signed-off-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
Drew Foehn
2025-11-07 19:44:32 +01:00
committed by GitHub
parent 110d51d1d7
commit 87827435ce
7 changed files with 185 additions and 14 deletions
+76 -7
View File
@@ -40,6 +40,8 @@ type GoogleProvider struct {
// Refresh. `Authorize` uses the results of this saved in `session.Groups`
// Since it is called on every request.
groupValidator func(*sessions.SessionState) bool
setPreferredUsername func(s *sessions.SessionState) error
}
var _ Provider = (*GoogleProvider)(nil)
@@ -100,17 +102,59 @@ func NewGoogleProvider(p *ProviderData, opts options.GoogleOptions) (*GoogleProv
groupValidator: func(*sessions.SessionState) bool {
return true
},
setPreferredUsername: func(_ *sessions.SessionState) error {
return nil
},
}
if opts.ServiceAccountJSON != "" || opts.UseApplicationDefaultCredentials {
provider.configureGroups(opts)
}
if opts.UseOrganizationID || opts.ServiceAccountJSON != "" || opts.UseApplicationDefaultCredentials {
// reuse admin service to avoid multiple calls for token
var adminService *admin.Service
if opts.UseOrganizationID {
// add user scopes to admin api
userScope := getAdminAPIUserScope(opts.AdminAPIUserScope)
for index, scope := range possibleScopesList {
possibleScopesList[index] = scope + " " + userScope
}
adminService = getAdminService(opts)
provider.setPreferredUsername = func(s *sessions.SessionState) error {
userName, err := getUserInfo(adminService, s.Email)
if err != nil {
return err
}
s.PreferredUsername = userName
return nil
}
}
if opts.ServiceAccountJSON != "" || opts.UseApplicationDefaultCredentials {
if adminService == nil {
adminService = getAdminService(opts)
}
provider.configureGroups(opts, adminService)
}
}
return provider, nil
}
func (p *GoogleProvider) configureGroups(opts options.GoogleOptions) {
adminService := getAdminService(opts)
// by default can be readonly user scope
func getAdminAPIUserScope(scope string) string {
switch scope {
case "cloud":
return admin.CloudPlatformScope
case "user":
return admin.AdminDirectoryUserScope
}
return admin.AdminDirectoryUserReadonlyScope
}
func (p *GoogleProvider) configureGroups(opts options.GoogleOptions, adminService *admin.Service) {
// Backwards compatibility with `--google-group` option
if len(opts.Groups) > 0 {
p.setAllowedGroups(opts.Groups)
@@ -204,6 +248,7 @@ func (p *GoogleProvider) Redeem(ctx context.Context, redirectURL, code, codeVeri
// EnrichSession checks the listed Google Groups configured and adds any
// that the user is a member of to session.Groups.
// if preferred username is configured to be organization ID, it sets that as well.
func (p *GoogleProvider) EnrichSession(_ context.Context, s *sessions.SessionState) error {
// TODO (@NickMeves) - Move to pure EnrichSession logic and stop
// reusing legacy `groupValidator`.
@@ -212,7 +257,7 @@ func (p *GoogleProvider) EnrichSession(_ context.Context, s *sessions.SessionSta
// populating logic.
p.groupValidator(s)
return nil
return p.setPreferredUsername(s)
}
// SetGroupRestriction configures the GoogleProvider to restrict access to the
@@ -262,7 +307,7 @@ func getOauth2TokenSource(ctx context.Context, opts options.GoogleOptions, scope
if opts.UseApplicationDefaultCredentials {
ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
TargetPrincipal: getTargetPrincipal(ctx, opts),
Scopes: []string{scope},
Scopes: strings.Split(scope, " "),
Subject: opts.AdminEmail,
})
if err != nil {
@@ -364,6 +409,30 @@ func getTargetPrincipal(ctx context.Context, opts options.GoogleOptions) (target
return targetPrincipal
}
func getUserInfo(service *admin.Service, email string) (string, error) {
req := service.Users.Get(email)
user, err := req.Do()
if err != nil {
return "", fmt.Errorf("failed to get user details for %s: %v", email, err)
}
ext, _ := user.ExternalIds.([]interface{})
for _, v := range ext {
m, _ := v.(map[string]interface{})
if m == nil {
continue
}
if t, _ := m["type"].(string); t != "organization" {
continue
}
if val, _ := m["value"].(string); val != "" {
return val, nil
}
}
return "", fmt.Errorf("failed to get organization id for %s", email)
}
// getUserGroups retrieves all groups that a user is a member of using the Google Admin Directory API
func getUserGroups(service *admin.Service, email string) ([]string, error) {
var allGroups []string