From 4c86a4d574952ffa829dbe1f5a32c48b3f2e1ddc Mon Sep 17 00:00:00 2001 From: Kevin Kreitner Date: Tue, 12 Aug 2025 17:41:45 +0200 Subject: [PATCH] feat: add Cidaas provider (#2273) * Add sensible logging flag to default setup for logger * Fix default value flag for sensitive logging * Remove sensitive logging changes * Add Cidaas provider * Update CHANGELOG.md * Add required groups scope to defaults * Fix tests * Remove if block with protected resource * Fix linting * Adjust provider sorting, fixes * Directly handle error return Co-authored-by: Jan Larwig * Use less deep nesting Co-authored-by: Jan Larwig * Directly handle returned error Co-authored-by: Jan Larwig * Pass provider options to Cidaas provider Co-authored-by: Jan Larwig * Add import for provider options * Fix tests * Fix linting * Add Cidaas doc page * Add Cidaas provider doc page to overview * Fix link in docs * Fix link in docs * Add link to Cidaas * fix provider order in docs and changelog position Signed-off-by: Jan Larwig --------- Signed-off-by: Jan Larwig Co-authored-by: Teko012 <112829523+Teko012@users.noreply.github.com> Co-authored-by: Jan Larwig Co-authored-by: Kevin Kreitner --- CHANGELOG.md | 2 + docs/docs/configuration/providers/cidaas.md | 37 ++ docs/docs/configuration/providers/index.md | 1 + pkg/apis/options/providers.go | 3 + providers/cidaas.go | 144 ++++++ providers/cidaas_test.go | 493 ++++++++++++++++++++ providers/providers.go | 5 +- 7 files changed, 684 insertions(+), 1 deletion(-) create mode 100644 docs/docs/configuration/providers/cidaas.md create mode 100644 providers/cidaas.go create mode 100644 providers/cidaas_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index c8301170..a6909163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ## Changes since v7.11.0 +- [#2273](https://github.com/oauth2-proxy/oauth2-proxy/pull/2273) feat: add Cidaas provider (@Bibob7, @Teko012) + # V7.11.0 ## Release Highlights diff --git a/docs/docs/configuration/providers/cidaas.md b/docs/docs/configuration/providers/cidaas.md new file mode 100644 index 00000000..7a987018 --- /dev/null +++ b/docs/docs/configuration/providers/cidaas.md @@ -0,0 +1,37 @@ +--- +id: cidaas +title: Cidaas +--- + +[Cidaas](https://www.cidaas.com/) is an Identity as a Service (IDaaS) solution that provides authentication and authorization services. +It supports various protocols including OpenID Connect, OAuth 2.0, and SAML. + +However, Cidaas provides groups and their roles as hierarchical claims, which are not supported by oauth2-proxy yet. +The Cidaas provider transforms the hierarchical claims into a flat list of groups, which can be used by oauth2-proxy. + +Example of groups and roles in Cidaas: + +```json +{ + "groups": [ + { + "groupId": "group1", + "roles": ["role1", "role2"] + }, + { + "groupId": "group2", + "roles": ["role3"] + } + ] +} +``` + +This will be transformed into a flat list of groups: + +```json +{ + "groups": ["group1:role1", "group2:role2", "group2:role3"] +} +``` + +Apart from that the Cidaas provider inherits all the features of the [OpenID Connect provider](openid_connect.md). \ No newline at end of file diff --git a/docs/docs/configuration/providers/index.md b/docs/docs/configuration/providers/index.md index b947c09b..713b5cb9 100644 --- a/docs/docs/configuration/providers/index.md +++ b/docs/docs/configuration/providers/index.md @@ -10,6 +10,7 @@ Valid providers are : - [ADFS](adfs.md) - [Bitbucket](bitbucket.md) +- [Cidaas](cidaas.md) - [DigitalOcean](digitalocean.md) - [Facebook](facebook.md) - [Gitea](gitea.md) diff --git a/pkg/apis/options/providers.go b/pkg/apis/options/providers.go index ac5652ca..0f254575 100644 --- a/pkg/apis/options/providers.go +++ b/pkg/apis/options/providers.go @@ -115,6 +115,9 @@ const ( // BitbucketProvider is the provider type for Bitbucket BitbucketProvider ProviderType = "bitbucket" + // CidaasProvider is the provider type for Cidaas IDP + CidaasProvider ProviderType = "cidaas" + // DigitalOceanProvider is the provider type for DigitalOcean DigitalOceanProvider ProviderType = "digitalocean" diff --git a/providers/cidaas.go b/providers/cidaas.go new file mode 100644 index 00000000..1d526dfc --- /dev/null +++ b/providers/cidaas.go @@ -0,0 +1,144 @@ +package providers + +import ( + "context" + "encoding/json" + "errors" + "fmt" + + "github.com/bitly/go-simplejson" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" +) + +type GroupsClaimList []GroupClaimEntry + +type GroupClaimEntry struct { + GroupID string `json:"groupId"` + Roles []string `json:"roles"` +} + +// CIDAASProvider represents an CIDAAS based Identity Provider +type CIDAASProvider struct { + *OIDCProvider +} + +var _ Provider = (*CIDAASProvider)(nil) + +const ( + CidaasProviderName = "CIDAAS" + CidaasGroupName = "cidaas" + CidaasDefaultScope = "openid email profile roles groups" +) + +// NewCIDAASProvider initiates a new CIDAASProvider +func NewCIDAASProvider(p *ProviderData, opts options.Provider) *CIDAASProvider { + p.setProviderDefaults(providerDefaults{ + name: CidaasProviderName, + scope: CidaasDefaultScope, + }) + + return &CIDAASProvider{ + OIDCProvider: NewOIDCProvider(p, opts.OIDCConfig), + } +} + +// RefreshSession uses the RefreshToken to fetch new Access and ID Tokens +func (p *CIDAASProvider) RefreshSession(ctx context.Context, s *sessions.SessionState) (bool, error) { + if s == nil || s.RefreshToken == "" { + return false, nil + } + + if err := p.redeemRefreshToken(ctx, s); err != nil { + return false, fmt.Errorf("unable to redeem refresh token: %w", err) + } + + if err := p.EnrichSession(ctx, s); err != nil { + return false, fmt.Errorf("unable to enrich session data after refresh: %w %v", err, s) + } + + return true, nil +} + +// EnrichSession data to add email an groups +func (p *CIDAASProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error { + if p.ProfileURL.String() == "" && s.Email == "" { + return errors.New("id_token did not contain an email and profileURL is not defined") + } else if p.ProfileURL.String() == "" { + return nil + } + + // Try to get missing emails or groups from a profileURL + if err := p.enrichFromUserinfoEndpoint(ctx, s); err != nil { + logger.Errorf("Warning: Profile URL request failed: %s", err) + } + + // If a mandatory email wasn't set, error at this point. + if s.Email == "" { + return errors.New("neither the id_token nor the profileURL set an email") + } + return nil +} + +// enrichFromUserinfoEndpoint enriches a session's Email & Groups via the JSON response of +// an OIDC profile URL +func (p *CIDAASProvider) enrichFromUserinfoEndpoint(ctx context.Context, s *sessions.SessionState) error { + // profile url is userinfo url in case of Cidaas + respJSON, err := requests.New(p.ProfileURL.String()). + WithContext(ctx). + WithHeaders(makeOIDCHeader(s.AccessToken)). + Do(). + UnmarshalSimpleJSON() + if err != nil { + return err + } + + email, err := respJSON.Get(p.EmailClaim).String() + if err == nil && s.Email == "" { + s.Email = email + } + + groups, err := p.extractGroups(respJSON) + if err != nil { + return fmt.Errorf("extracting groups failed: %w", err) + } + + s.Groups = groups + return nil +} + +func (p *CIDAASProvider) extractGroups(respJSON *simplejson.Json) ([]string, error) { + rawGroupsClaim, err := respJSON.Get(p.GroupsClaim).MarshalJSON() + if err != nil { + return nil, err + } + + var groupsClaimList GroupsClaimList + err = json.Unmarshal(rawGroupsClaim, &groupsClaimList) + if err != nil { + return nil, err + } + + var groups []string + for _, group := range groupsClaimList { + for _, role := range group.Roles { + groups = append(groups, fmt.Sprintf("%s:%s", group.GroupID, role)) + } + } + + // Cidaas specific roles + if rolesVal, rolesClaimExists := respJSON.CheckGet("roles"); rolesClaimExists { + cidaasRoles, err := rolesVal.StringArray() + if err != nil { + return nil, fmt.Errorf("unmarshal roles failed: %w", err) + } + + for _, role := range cidaasRoles { + groups = append(groups, fmt.Sprintf("%s:%s", CidaasGroupName, role)) + } + } + + return groups, nil +} diff --git a/providers/cidaas_test.go b/providers/cidaas_test.go new file mode 100644 index 00000000..a9ee4e97 --- /dev/null +++ b/providers/cidaas_test.go @@ -0,0 +1,493 @@ +package providers + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/stretchr/testify/assert" + + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" +) + +func newCidaasProvider(serverURL *url.URL) *CIDAASProvider { + providerData := &ProviderData{ + ProviderName: "cidaas", + ClientID: oidcClientID, + ClientSecret: oidcSecret, + LoginURL: &url.URL{ + Scheme: serverURL.Scheme, + Host: serverURL.Host, + Path: "/login/oauth/authorize"}, + RedeemURL: &url.URL{ + Scheme: serverURL.Scheme, + Host: serverURL.Host, + Path: "/login/oauth/access_token"}, + ProfileURL: &url.URL{ + Scheme: serverURL.Scheme, + Host: serverURL.Host, + Path: "/profile"}, + ValidateURL: &url.URL{ + Scheme: serverURL.Scheme, + Host: serverURL.Host, + Path: "/api"}, + Scope: "openid profile offline_access roles groups", + EmailClaim: "email", + GroupsClaim: "groups", + Verifier: oidc.NewVerifier( + oidcIssuer, + mockJWKS{}, + &oidc.Config{ClientID: oidcClientID}, + ), + } + cfg := options.Provider{ + Type: options.CidaasProvider, + } + + p := NewCIDAASProvider(providerData, cfg) + + return p +} + +func newCidaasServer(pathBodyMap map[string][]byte) (*url.URL, *httptest.Server) { + s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + body, ok := pathBodyMap[r.URL.Path] + if !ok { + rw.WriteHeader(404) + return + } + rw.Header().Add("content-type", "application/json") + _, _ = rw.Write(body) + })) + u, _ := url.Parse(s.URL) + return u, s +} + +func newTestCidaasSetup(pathToBodyMap map[string][]byte) (*httptest.Server, *CIDAASProvider) { + redeemURL, server := newCidaasServer(pathToBodyMap) + provider := newCidaasProvider(redeemURL) + return server, provider +} + +func TestCidaasProvider_EnrichSession(t *testing.T) { + testCases := map[string]struct { + ExistingSession *sessions.SessionState + EmailClaim string + GroupsClaim string + ProfileJSON map[string]interface{} + ExpectedError error + ExpectedSession *sessions.SessionState + }{ + "Missing Email Only in Profile URL": { + ExistingSession: &sessions.SessionState{ + User: "missing.email", + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups", + ProfileJSON: map[string]interface{}{ + "email": "found@email.com", + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "missing.email", + Email: "found@email.com", + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Missing Email with Custom Claim": { + ExistingSession: &sessions.SessionState{ + User: "missing.email", + Groups: []string{"already", "populated"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "weird", + GroupsClaim: "groups", + ProfileJSON: map[string]interface{}{ + "weird": "weird@claim.com", + "groups": []map[string]interface{}{ + { + "groupId": "CIDAAS_USERS", + "roles": []string{"USER"}, + }, + }, + "roles": []string{"USER"}, + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "missing.email", + Email: "weird@claim.com", + Groups: []string{"CIDAAS_USERS:USER", "cidaas:USER"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Missing Email not in Profile URL": { + ExistingSession: &sessions.SessionState{ + User: "missing.email", + Groups: []string{"already", "populated"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups", + ProfileJSON: map[string]interface{}{ + "groups": []map[string]interface{}{ + { + "groupId": "CIDAAS_USERS", + "roles": []string{"USER"}, + }, + }, + "roles": []string{"USER"}, + }, + ExpectedError: errors.New("neither the id_token nor the profileURL set an email"), + ExpectedSession: &sessions.SessionState{ + User: "missing.email", + Groups: []string{"CIDAAS_USERS:USER", "cidaas:USER"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Missing Groups": { + ExistingSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: nil, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups", + ProfileJSON: map[string]interface{}{ + "email": "new@thing.com", + "groups": []map[string]interface{}{ + { + "groupId": "CIDAAS_USERS", + "roles": []string{"USER"}, + }, + }, + "roles": []string{"USER"}, + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: []string{"CIDAAS_USERS:USER", "cidaas:USER"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Empty Groups Claims": { + ExistingSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: []string{}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups", + ProfileJSON: map[string]interface{}{ + "email": "new@thing.com", + "groups": []map[string]interface{}{ + { + "groupId": "CIDAAS_USERS", + "roles": []string{"USER"}, + }, + }, + "roles": []string{"USER"}, + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: []string{"CIDAAS_USERS:USER", "cidaas:USER"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Missing Groups with Custom Claim": { + ExistingSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: nil, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups2", + ProfileJSON: map[string]interface{}{ + "email": "already@populated.com", + "groups2": []map[string]interface{}{ + { + "sub": "aa5181ea-0841-4ea7-b67f-81882f153d40", + "groupId": "CIDAAS_ADMINS", + "path": "/CIDAAS_ADMINS/", + "roles": []string{"ADMIN"}, + }, + { + "sub": "aa5181ea-0841-4ea7-b67f-81882f153d39", + "groupId": "customers", + "groupType": "Customers", + "path": "/customers/", + "roles": []string{ + "CUSTOMER_ACCOUNT_LOGIN", + "GROUP_ADMIN", + }, + }, + { + "groupId": "CIDAAS_USERS", + "roles": []string{"USER"}, + }, + }, + "roles": []string{"USER"}, + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: []string{"CIDAAS_ADMINS:ADMIN", "customers:CUSTOMER_ACCOUNT_LOGIN", "customers:GROUP_ADMIN", "CIDAAS_USERS:USER", "cidaas:USER"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Just format Groups": { + ExistingSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: nil, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups2", + ProfileJSON: map[string]interface{}{ + "email": "already@populated.com", + "groups2": []map[string]interface{}{ + { + "sub": "aa5181ea-0841-4ea7-b67f-81882f153d39", + "groupId": "customers", + "groupType": "Customers", + "path": "/customers/", + "roles": []string{ + "CUSTOMER_ACCOUNT_LOGIN", + "GROUP_ADMIN", + }, + }, + { + "groupId": "CIDAAS_USERS", + "roles": []string{"USER"}, + }, + }, + "roles": []string{"USER"}, + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: []string{"customers:CUSTOMER_ACCOUNT_LOGIN", "customers:GROUP_ADMIN", "CIDAAS_USERS:USER", "cidaas:USER"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Missing Groups String Profile URL Response": { + ExistingSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: nil, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups", + ProfileJSON: map[string]interface{}{ + "groups": []map[string]interface{}{ + { + "sub": "aa5181ea-0841-4ea7-b67f-81882f153d40", + "groupId": "CIDAAS_ADMINS", + "path": "/CIDAAS_ADMINS/", + "roles": []string{"ADMIN"}, + }, + { + "sub": "aa5181ea-0841-4ea7-b67f-81882f153d39", + "groupId": "customers", + "groupType": "Customers", + "path": "/customers/", + "roles": []string{ + "CUSTOMER_ACCOUNT_LOGIN", + "GROUP_ADMIN", + }, + }, + { + "groupId": "CIDAAS_USERS", + "roles": []string{"USER"}, + }, + }, + "roles": []string{"USER"}, + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + Groups: []string{"CIDAAS_ADMINS:ADMIN", "customers:CUSTOMER_ACCOUNT_LOGIN", "customers:GROUP_ADMIN", "CIDAAS_USERS:USER", "cidaas:USER"}, + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + "Missing Groups in both Claims and Profile URL": { + ExistingSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + EmailClaim: "email", + GroupsClaim: "groups", + ProfileJSON: map[string]interface{}{ + "email": "new@thing.com", + }, + ExpectedError: nil, + ExpectedSession: &sessions.SessionState{ + User: "already", + Email: "already@populated.com", + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + }, + } + for testName, tc := range testCases { + t.Run(testName, func(t *testing.T) { + path := "/userinfo/" + jsonResp, err := json.Marshal(tc.ProfileJSON) + assert.NoError(t, err) + + server, provider := newTestCidaasSetup(map[string][]byte{path: jsonResp}) + provider.ProfileURL, err = url.Parse(fmt.Sprintf("%s%s", server.URL, path)) + assert.NoError(t, err) + + provider.EmailClaim = tc.EmailClaim + provider.GroupsClaim = tc.GroupsClaim + defer server.Close() + + err = provider.EnrichSession(context.Background(), tc.ExistingSession) + if tc.ExpectedError != nil { + assert.EqualError(t, err, tc.ExpectedError.Error()) + } else { + assert.NoError(t, err) + } + assert.Equal(t, *tc.ExpectedSession, *tc.ExistingSession) + }) + } +} + +func TestCidaasProvider_RefreshSession(t *testing.T) { + testCases := map[string]struct { + ExistingSession *sessions.SessionState + EmailClaim string + GroupsClaim string + ProfileJSON map[string]interface{} + RedeemJSON redeemTokenResponse + ExpectedRefreshed bool + ExpectedError error + ExpectedEmail string + ExpectedUser string + }{ + "Refresh session successfully": { + ExistingSession: &sessions.SessionState{ + User: "session.is.not.locked", + Email: "found@email.com", + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + RedeemJSON: redeemTokenResponse{ + AccessToken: accessToken, + ExpiresIn: 10, + TokenType: "Bearer", + RefreshToken: refreshToken, + }, + ExpectedRefreshed: true, + ExpectedError: nil, + ExpectedEmail: defaultIDToken.Email, + ExpectedUser: defaultIDToken.Subject, + }, + "Unable to refresh session": { + ExistingSession: &sessions.SessionState{ + User: "session.is.unable.to.refresh", + Email: "found@email.com", + IDToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }, + ExpectedRefreshed: false, + ExpectedError: fmt.Errorf("unable to redeem refresh token: failed to get token: oauth2: server response missing access_token"), + ExpectedUser: "session.is.unable.to.refresh", + ExpectedEmail: "found@email.com", + }, + } + for testName, tc := range testCases { + t.Run(testName, func(t *testing.T) { + idToken, _ := newSignedTestIDToken(defaultIDToken) + tc.RedeemJSON.IDToken = idToken + redeemPath := "/token/" + redeemJSONResp, err := json.Marshal(tc.RedeemJSON) + assert.NoError(t, err) + + serverURL, server := newCidaasServer( + map[string][]byte{ + redeemPath: redeemJSONResp, + }) + provider := newCidaasProvider(serverURL) + + // Disable session enrichment, because we want to focus on refreshing logic + provider.ProfileURL, err = url.Parse("") + assert.NoError(t, err) + provider.RedeemURL, err = url.Parse(fmt.Sprintf("%s%s", server.URL, redeemPath)) + assert.NoError(t, err) + + provider.GroupsClaim = tc.GroupsClaim + defer server.Close() + + var refreshed bool + refreshed, err = provider.RefreshSession(context.Background(), tc.ExistingSession) + + if tc.ExpectedError != nil { + assert.EqualError(t, err, tc.ExpectedError.Error()) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tc.ExpectedRefreshed, refreshed) + assert.Equal(t, tc.ExpectedEmail, tc.ExistingSession.Email) + assert.Equal(t, tc.ExpectedUser, tc.ExistingSession.User) + }) + } +} diff --git a/providers/providers.go b/providers/providers.go index 8bc5ff88..ec00f412 100644 --- a/providers/providers.go +++ b/providers/providers.go @@ -45,6 +45,8 @@ func NewProvider(providerConfig options.Provider) (Provider, error) { return NewMicrosoftEntraIDProvider(providerData, providerConfig), nil case options.BitbucketProvider: return NewBitbucketProvider(providerData, providerConfig.BitbucketConfig), nil + case options.CidaasProvider: + return NewCIDAASProvider(providerData, providerConfig), nil case options.DigitalOceanProvider: return NewDigitalOceanProvider(providerData), nil case options.FacebookProvider: @@ -188,7 +190,8 @@ func providerRequiresOIDCProviderVerifier(providerType options.ProviderType) (bo options.GoogleProvider, options.KeycloakProvider, options.LinkedInProvider, options.LoginGovProvider, options.NextCloudProvider, options.SourceHutProvider: return false, nil - case options.ADFSProvider, options.AzureProvider, options.GitLabProvider, options.KeycloakOIDCProvider, options.OIDCProvider, options.MicrosoftEntraIDProvider: + case options.OIDCProvider, options.ADFSProvider, options.AzureProvider, options.CidaasProvider, + options.GitLabProvider, options.KeycloakOIDCProvider, options.MicrosoftEntraIDProvider: return true, nil default: return false, fmt.Errorf("unknown provider type: %s", providerType)