Skip to content
Closed
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
11 changes: 10 additions & 1 deletion connector/oidc/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ type Config struct {

// PromptType will be used fot the prompt parameter (when offline_access, by default prompt=consent)
PromptType string `json:"promptType"`

// Configurable key which contains the groups claims
GroupsKey string `json:"groupsKey"` // defaults to "groups"
}

// Domains that don't support basic auth. golang.org/x/oauth2 has an internal
Expand Down Expand Up @@ -144,6 +147,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
userIDKey: c.UserIDKey,
userNameKey: c.UserNameKey,
promptType: c.PromptType,
groupsKey: c.GroupsKey,
}, nil
}

Expand All @@ -166,6 +170,7 @@ type oidcConnector struct {
userIDKey string
userNameKey string
promptType string
groupsKey string
}

func (c *oidcConnector) Close() error {
Expand Down Expand Up @@ -336,7 +341,11 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I
}

if c.insecureEnableGroups {
vs, ok := claims["groups"].([]interface{})
if c.groupsKey == "" {
c.groupsKey = "groups"
}

vs, ok := claims[c.groupsKey].([]interface{})
if ok {
for _, v := range vs {
if s, ok := v.(string); ok {
Expand Down
91 changes: 91 additions & 0 deletions connector/oidc/oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"sort"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -42,17 +44,87 @@ func TestKnownBrokenAuthHeaderProvider(t *testing.T) {
}
}

func TestOpen(t *testing.T) {
testServer, err := setupServer(make(map[string]interface{}))
if err != nil {
t.Fatal("failed to setup test server", err)
}
defer testServer.Close()

conn, err := newConnector(Config{
Issuer: testServer.URL,
ClientID: "clientID",
ClientSecret: "clientSecret",
Scopes: []string{"groups"},
RedirectURI: fmt.Sprintf("%s/callback", testServer.URL),
})

if err != nil {
t.Fatal("failed to create new connector", err)
}

sort.Strings(conn.oauth2Config.Scopes)

expectEquals(t, conn.oauth2Config.ClientID, "clientID")
expectEquals(t, conn.oauth2Config.ClientSecret, "clientSecret")
expectEquals(t, conn.oauth2Config.RedirectURL, testServer.URL+"/callback")
expectEquals(t, conn.oauth2Config.Endpoint.TokenURL, testServer.URL+"/token")
expectEquals(t, conn.oauth2Config.Endpoint.AuthURL, testServer.URL+"/authorize")
expectEquals(t, len(conn.oauth2Config.Scopes), 2)
expectEquals(t, conn.oauth2Config.Scopes[0], "groups")
expectEquals(t, conn.oauth2Config.Scopes[1], "openid")
}

func TestLoginURL(t *testing.T) {
testServer, err := setupServer(make(map[string]interface{}))
if err != nil {
t.Fatal("failed to setup test server", err)
}
defer testServer.Close()

conn, err := newConnector(Config{
Issuer: testServer.URL,
ClientID: "clientID",
ClientSecret: "clientSecret",
Scopes: []string{"groups"},
RedirectURI: fmt.Sprintf("%s/callback", testServer.URL),
})

if err != nil {
t.Fatal("failed to create new connector", err)
}

loginURL, err := conn.LoginURL(connector.Scopes{}, conn.redirectURI, "some-state")
expectEquals(t, err, nil)

expectedURL, err := url.Parse(testServer.URL + "/authorize")
expectEquals(t, err, nil)

values := url.Values{}
values.Add("client_id", "clientID")
values.Add("redirect_uri", conn.redirectURI)
values.Add("response_type", "code")
values.Add("scope", "openid groups")
values.Add("state", "some-state")
expectedURL.RawQuery = values.Encode()

expectEquals(t, loginURL, expectedURL.String())
}

func TestHandleCallback(t *testing.T) {
t.Helper()

tests := []struct {
name string
userIDKey string
userNameKey string
groupsKey string
insecureSkipEmailVerified bool
insecureEnableGroups bool
scopes []string
expectUserID string
expectUserName string
expectGroups []string
expectedEmailField string
token map[string]interface{}
}{
Expand Down Expand Up @@ -108,6 +180,22 @@ func TestHandleCallback(t *testing.T) {
"email_verified": true,
},
},
{
name: "withGroupsKey",
insecureEnableGroups: true,
groupsKey: "groups_key",
expectUserID: "subvalue",
expectUserName: "namevalue",
expectGroups: []string{"group"},
expectedEmailField: "emailvalue",
token: map[string]interface{}{
"sub": "subvalue",
"name": "namevalue",
"groups_key": []string{"group"},
"email": "emailvalue",
"email_verified": true,
},
},
{
name: "emptyEmailScope",
expectUserID: "subvalue",
Expand Down Expand Up @@ -161,7 +249,9 @@ func TestHandleCallback(t *testing.T) {
RedirectURI: fmt.Sprintf("%s/callback", serverURL),
UserIDKey: tc.userIDKey,
UserNameKey: tc.userNameKey,
GroupsKey: tc.groupsKey,
InsecureSkipEmailVerified: tc.insecureSkipEmailVerified,
InsecureEnableGroups: tc.insecureEnableGroups,
BasicAuthUnsupported: &basicAuth,
}

Expand All @@ -182,6 +272,7 @@ func TestHandleCallback(t *testing.T) {

expectEquals(t, identity.UserID, tc.expectUserID)
expectEquals(t, identity.Username, tc.expectUserName)
expectEquals(t, identity.Groups, tc.expectGroups)
expectEquals(t, identity.Email, tc.expectedEmailField)
expectEquals(t, identity.EmailVerified, true)
})
Expand Down