Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions connector/google/google.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,23 @@ func (c *googleConnector) Close() error {
return nil
}

// matchHostedDomain reports whether the given Google "hd" claim is allowed by
// the configured hostedDomains list. An entry of "*" acts as a wildcard that
// accepts any non-empty hd claim, allowing operators to require that users
// authenticate with a Workspace account (any domain) rather than a personal
// account, without enumerating every domain.
func matchHostedDomain(claim string, allowed []string) bool {
for _, domain := range allowed {
if domain == "*" && claim != "" {
return true
}
if claim == domain {
return true
}
}
return false
}

func (c *googleConnector) LoginURL(s connector.Scopes, callbackURL, state string) (string, []byte, error) {
if c.redirectURI != callbackURL {
return "", nil, fmt.Errorf("expected callback URL %q did not match the URL in the config %q", callbackURL, c.redirectURI)
Expand Down Expand Up @@ -256,18 +273,8 @@ func (c *googleConnector) createIdentity(ctx context.Context, identity connector
claims.Username = identity.Username
}

if len(c.hostedDomains) > 0 {
found := false
for _, domain := range c.hostedDomains {
if claims.HostedDomain == domain {
found = true
break
}
}

if !found {
return identity, fmt.Errorf("oidc: unexpected hd claim %v", claims.HostedDomain)
}
if len(c.hostedDomains) > 0 && !matchHostedDomain(claims.HostedDomain, c.hostedDomains) {
return identity, fmt.Errorf("oidc: unexpected hd claim %v", claims.HostedDomain)
}

var groups []string
Expand Down
58 changes: 58 additions & 0 deletions connector/google/google_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,61 @@ func TestPromptTypeConfig(t *testing.T) {
})
}
}

func TestMatchHostedDomain(t *testing.T) {
tests := []struct {
name string
claim string
allowed []string
want bool
}{
{
name: "exact match",
claim: "example.com",
allowed: []string{"example.com"},
want: true,
},
{
name: "exact match within multiple",
claim: "example.com",
allowed: []string{"foo.com", "example.com", "bar.com"},
want: true,
},
{
name: "no match",
claim: "ferrix.ai",
allowed: []string{"example.com"},
want: false,
},
{
name: "wildcard accepts any non-empty hd",
claim: "ferrix.ai",
allowed: []string{"*"},
want: true,
},
{
name: "wildcard alongside explicit domains",
claim: "anything.dev",
allowed: []string{"example.com", "*"},
want: true,
},
{
name: "wildcard rejects empty hd claim",
claim: "",
allowed: []string{"*"},
want: false,
},
{
name: "empty claim with explicit domains",
claim: "",
allowed: []string{"example.com"},
want: false,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.want, matchHostedDomain(tc.claim, tc.allowed))
})
}
}