github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/oidc.go (about) 1 /* 2 Copyright 2020 Gravitational, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package types 18 19 import ( 20 "net/url" 21 "slices" 22 "time" 23 24 "github.com/gravitational/trace" 25 "golang.org/x/crypto/ssh" 26 27 "github.com/gravitational/teleport/api/constants" 28 "github.com/gravitational/teleport/api/defaults" 29 "github.com/gravitational/teleport/api/utils" 30 ) 31 32 // OIDCConnector specifies configuration for Open ID Connect compatible external 33 // identity provider, e.g. google in some organization 34 type OIDCConnector interface { 35 // ResourceWithSecrets provides common methods for objects 36 ResourceWithSecrets 37 ResourceWithOrigin 38 // Issuer URL is the endpoint of the provider, e.g. https://accounts.google.com 39 GetIssuerURL() string 40 // ClientID is id for authentication client (in our case it's our Auth server) 41 GetClientID() string 42 // ClientSecret is used to authenticate our client and should not 43 // be visible to end user 44 GetClientSecret() string 45 // GetRedirectURLs returns list of redirect URLs. 46 GetRedirectURLs() []string 47 // GetACR returns the Authentication Context Class Reference (ACR) value. 48 GetACR() string 49 // GetProvider returns the identity provider. 50 GetProvider() string 51 // Display - Friendly name for this provider. 52 GetDisplay() string 53 // Scope is additional scopes set by provider 54 GetScope() []string 55 // ClaimsToRoles specifies dynamic mapping from claims to roles 56 GetClaimsToRoles() []ClaimMapping 57 // GetClaims returns list of claims expected by mappings 58 GetClaims() []string 59 // GetTraitMappings converts gets all claim mappings in the 60 // generic trait mapping format. 61 GetTraitMappings() TraitMappingSet 62 // SetClientSecret sets client secret to some value 63 SetClientSecret(secret string) 64 // SetClientID sets id for authentication client (in our case it's our Auth server) 65 SetClientID(string) 66 // SetIssuerURL sets the endpoint of the provider 67 SetIssuerURL(string) 68 // SetRedirectURLs sets the list of redirectURLs 69 SetRedirectURLs([]string) 70 // SetPrompt sets OIDC prompt value 71 SetPrompt(string) 72 // GetPrompt returns OIDC prompt value, 73 GetPrompt() string 74 // SetACR sets the Authentication Context Class Reference (ACR) value. 75 SetACR(string) 76 // SetProvider sets the identity provider. 77 SetProvider(string) 78 // SetScope sets additional scopes set by provider 79 SetScope([]string) 80 // SetClaimsToRoles sets dynamic mapping from claims to roles 81 SetClaimsToRoles([]ClaimMapping) 82 // GetUsernameClaim gets the name of the claim from the OIDC connector to be used as the user's username. 83 GetUsernameClaim() string 84 // SetDisplay sets friendly name for this provider. 85 SetDisplay(string) 86 // GetGoogleServiceAccountURI returns path to google service account URI 87 GetGoogleServiceAccountURI() string 88 // GetGoogleServiceAccount returns google service account json for Google 89 GetGoogleServiceAccount() string 90 // SetGoogleServiceAccount sets the google service account json contents 91 SetGoogleServiceAccount(string) 92 // GetGoogleAdminEmail returns a google admin user email 93 // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority 94 // "Note: Although you can use service accounts in applications that run from a Google Workspace (formerly G Suite) domain, service accounts are not members of your Google Workspace account and aren’t subject to domain policies set by administrators. For example, a policy set in the Google Workspace admin console to restrict the ability of end users to share documents outside of the domain would not apply to service accounts." 95 GetGoogleAdminEmail() string 96 // GetAllowUnverifiedEmail returns true if unverified emails should be allowed in received users. 97 GetAllowUnverifiedEmail() bool 98 // GetMaxAge returns the amount of time that user logins are 99 // valid for and true if MaxAge is set. If a user logs in, but then 100 // does not login again within this time period, they will be forced 101 // to re-authenticate. 102 GetMaxAge() (time.Duration, bool) 103 } 104 105 // NewOIDCConnector returns a new OIDCConnector based off a name and OIDCConnectorSpecV3. 106 func NewOIDCConnector(name string, spec OIDCConnectorSpecV3) (OIDCConnector, error) { 107 o := &OIDCConnectorV3{ 108 Metadata: Metadata{ 109 Name: name, 110 }, 111 Spec: spec, 112 } 113 if err := o.CheckAndSetDefaults(); err != nil { 114 return nil, trace.Wrap(err) 115 } 116 return o, nil 117 } 118 119 // SetPrompt sets OIDC prompt value 120 func (o *OIDCConnectorV3) SetPrompt(p string) { 121 o.Spec.Prompt = p 122 } 123 124 // GetPrompt returns OIDC prompt value, 125 // * if not set, default to select_account for backwards compatibility 126 // * if set to none, it will be omitted 127 // * and any other non empty value, pass it as is 128 func (o *OIDCConnectorV3) GetPrompt() string { 129 if o.Spec.Prompt == "" { 130 return constants.OIDCPromptSelectAccount 131 } 132 if o.Spec.Prompt == constants.OIDCPromptNone { 133 return "" 134 } 135 return o.Spec.Prompt 136 } 137 138 // GetGoogleServiceAccountURI returns an optional path to google service account file 139 func (o *OIDCConnectorV3) GetGoogleServiceAccountURI() string { 140 return o.Spec.GoogleServiceAccountURI 141 } 142 143 // GetGoogleServiceAccount returns a string representing a Google service account 144 func (o *OIDCConnectorV3) GetGoogleServiceAccount() string { 145 return o.Spec.GoogleServiceAccount 146 } 147 148 // SetGoogleServiceAccount sets a string representing a Google service account 149 func (o *OIDCConnectorV3) SetGoogleServiceAccount(s string) { 150 o.Spec.GoogleServiceAccount = s 151 } 152 153 // GetGoogleAdminEmail returns a google admin user email 154 func (o *OIDCConnectorV3) GetGoogleAdminEmail() string { 155 return o.Spec.GoogleAdminEmail 156 } 157 158 // GetVersion returns resource version 159 func (o *OIDCConnectorV3) GetVersion() string { 160 return o.Version 161 } 162 163 // GetSubKind returns resource sub kind 164 func (o *OIDCConnectorV3) GetSubKind() string { 165 return o.SubKind 166 } 167 168 // SetSubKind sets resource subkind 169 func (o *OIDCConnectorV3) SetSubKind(s string) { 170 o.SubKind = s 171 } 172 173 // GetKind returns resource kind 174 func (o *OIDCConnectorV3) GetKind() string { 175 return o.Kind 176 } 177 178 // GetResourceID returns resource ID 179 func (o *OIDCConnectorV3) GetResourceID() int64 { 180 return o.Metadata.ID 181 } 182 183 // SetResourceID sets resource ID 184 func (o *OIDCConnectorV3) SetResourceID(id int64) { 185 o.Metadata.ID = id 186 } 187 188 // GetRevision returns the revision 189 func (o *OIDCConnectorV3) GetRevision() string { 190 return o.Metadata.GetRevision() 191 } 192 193 // SetRevision sets the revision 194 func (o *OIDCConnectorV3) SetRevision(rev string) { 195 o.Metadata.SetRevision(rev) 196 } 197 198 // WithoutSecrets returns an instance of resource without secrets. 199 func (o *OIDCConnectorV3) WithoutSecrets() Resource { 200 if o.GetClientSecret() == "" && o.GetGoogleServiceAccount() == "" { 201 return o 202 } 203 o2 := *o 204 205 o2.SetClientSecret("") 206 o2.SetGoogleServiceAccount("") 207 208 return &o2 209 } 210 211 // V3 returns V3 version of the resource 212 func (o *OIDCConnectorV3) V3() *OIDCConnectorV3 { 213 return o 214 } 215 216 // SetDisplay sets friendly name for this provider. 217 func (o *OIDCConnectorV3) SetDisplay(display string) { 218 o.Spec.Display = display 219 } 220 221 // GetMetadata returns object metadata 222 func (o *OIDCConnectorV3) GetMetadata() Metadata { 223 return o.Metadata 224 } 225 226 // Origin returns the origin value of the resource. 227 func (o *OIDCConnectorV3) Origin() string { 228 return o.Metadata.Origin() 229 } 230 231 // SetOrigin sets the origin value of the resource. 232 func (o *OIDCConnectorV3) SetOrigin(origin string) { 233 o.Metadata.SetOrigin(origin) 234 } 235 236 // SetExpiry sets expiry time for the object 237 func (o *OIDCConnectorV3) SetExpiry(expires time.Time) { 238 o.Metadata.SetExpiry(expires) 239 } 240 241 // Expiry returns object expiry setting 242 func (o *OIDCConnectorV3) Expiry() time.Time { 243 return o.Metadata.Expiry() 244 } 245 246 // GetName returns the name of the connector 247 func (o *OIDCConnectorV3) GetName() string { 248 return o.Metadata.GetName() 249 } 250 251 // SetName sets client secret to some value 252 func (o *OIDCConnectorV3) SetName(name string) { 253 o.Metadata.SetName(name) 254 } 255 256 // SetIssuerURL sets client secret to some value 257 func (o *OIDCConnectorV3) SetIssuerURL(issuerURL string) { 258 o.Spec.IssuerURL = issuerURL 259 } 260 261 // SetRedirectURLs sets the list of redirectURLs 262 func (o *OIDCConnectorV3) SetRedirectURLs(redirectURLs []string) { 263 o.Spec.RedirectURLs = redirectURLs 264 } 265 266 // SetACR sets the Authentication Context Class Reference (ACR) value. 267 func (o *OIDCConnectorV3) SetACR(acrValue string) { 268 o.Spec.ACR = acrValue 269 } 270 271 // SetProvider sets the identity provider. 272 func (o *OIDCConnectorV3) SetProvider(identityProvider string) { 273 o.Spec.Provider = identityProvider 274 } 275 276 // SetScope sets additional scopes set by provider 277 func (o *OIDCConnectorV3) SetScope(scope []string) { 278 o.Spec.Scope = scope 279 } 280 281 // SetClaimsToRoles sets dynamic mapping from claims to roles 282 func (o *OIDCConnectorV3) SetClaimsToRoles(claims []ClaimMapping) { 283 o.Spec.ClaimsToRoles = claims 284 } 285 286 // SetClientID sets id for authentication client (in our case it's our Auth server) 287 func (o *OIDCConnectorV3) SetClientID(clintID string) { 288 o.Spec.ClientID = clintID 289 } 290 291 // SetClientSecret sets client secret to some value 292 func (o *OIDCConnectorV3) SetClientSecret(secret string) { 293 o.Spec.ClientSecret = secret 294 } 295 296 // GetIssuerURL is the endpoint of the provider, e.g. https://accounts.google.com 297 func (o *OIDCConnectorV3) GetIssuerURL() string { 298 return o.Spec.IssuerURL 299 } 300 301 // GetClientID is id for authentication client (in our case it's our Auth server) 302 func (o *OIDCConnectorV3) GetClientID() string { 303 return o.Spec.ClientID 304 } 305 306 // GetClientSecret is used to authenticate our client and should not 307 // be visible to end user 308 func (o *OIDCConnectorV3) GetClientSecret() string { 309 return o.Spec.ClientSecret 310 } 311 312 // GetRedirectURLs returns a list of the connector's redirect URLs. 313 func (o *OIDCConnectorV3) GetRedirectURLs() []string { 314 return o.Spec.RedirectURLs 315 } 316 317 // GetACR returns the Authentication Context Class Reference (ACR) value. 318 func (o *OIDCConnectorV3) GetACR() string { 319 return o.Spec.ACR 320 } 321 322 // GetProvider returns the identity provider. 323 func (o *OIDCConnectorV3) GetProvider() string { 324 return o.Spec.Provider 325 } 326 327 // GetDisplay - Friendly name for this provider. 328 func (o *OIDCConnectorV3) GetDisplay() string { 329 if o.Spec.Display != "" { 330 return o.Spec.Display 331 } 332 return o.GetName() 333 } 334 335 // GetScope is additional scopes set by provider 336 func (o *OIDCConnectorV3) GetScope() []string { 337 return o.Spec.Scope 338 } 339 340 // GetUsernameClaim gets the name of the claim from the OIDC connector to be used as the user's username. 341 func (o *OIDCConnectorV3) GetUsernameClaim() string { 342 return o.Spec.UsernameClaim 343 } 344 345 // GetClaimsToRoles specifies dynamic mapping from claims to roles 346 func (o *OIDCConnectorV3) GetClaimsToRoles() []ClaimMapping { 347 return o.Spec.ClaimsToRoles 348 } 349 350 // GetClaims returns list of claims expected by mappings 351 func (o *OIDCConnectorV3) GetClaims() []string { 352 var out []string 353 for _, mapping := range o.Spec.ClaimsToRoles { 354 out = append(out, mapping.Claim) 355 } 356 return utils.Deduplicate(out) 357 } 358 359 // GetTraitMappings returns the OIDCConnector's TraitMappingSet 360 func (o *OIDCConnectorV3) GetTraitMappings() TraitMappingSet { 361 tms := make([]TraitMapping, 0, len(o.Spec.ClaimsToRoles)) 362 for _, mapping := range o.Spec.ClaimsToRoles { 363 tms = append(tms, TraitMapping{ 364 Trait: mapping.Claim, 365 Value: mapping.Value, 366 Roles: mapping.Roles, 367 }) 368 } 369 return TraitMappingSet(tms) 370 } 371 372 // setStaticFields sets static resource header and metadata fields. 373 func (o *OIDCConnectorV3) setStaticFields() { 374 o.Kind = KindOIDCConnector 375 } 376 377 // CheckAndSetDefaults checks and set default values for any missing fields. 378 func (o *OIDCConnectorV3) CheckAndSetDefaults() error { 379 o.setStaticFields() 380 381 switch o.Version { 382 case V2, V3: 383 // V2 is also supported 384 case "": 385 o.Version = V3 386 default: 387 return trace.BadParameter("Version: invalid OIDC connector version %v", o.Version) 388 } 389 390 if err := o.Metadata.CheckAndSetDefaults(); err != nil { 391 return trace.Wrap(err) 392 } 393 394 if name := o.Metadata.Name; slices.Contains(constants.SystemConnectors, name) { 395 return trace.BadParameter("ID: invalid connector name, %v is a reserved name", name) 396 } 397 398 if o.Spec.ClientID == "" { 399 return trace.BadParameter("ClientID: missing client id") 400 } 401 402 if len(o.GetClaimsToRoles()) == 0 { 403 return trace.BadParameter("claims_to_roles is empty, authorization with connector would never assign any roles") 404 } 405 for _, v := range o.Spec.ClaimsToRoles { 406 if len(v.Roles) == 0 { 407 return trace.BadParameter("add roles in claims_to_roles") 408 } 409 } 410 411 if _, err := url.Parse(o.GetIssuerURL()); err != nil { 412 return trace.BadParameter("bad IssuerURL '%v', err: %v", o.GetIssuerURL(), err) 413 } 414 415 if len(o.GetRedirectURLs()) == 0 { 416 return trace.BadParameter("RedirectURL: missing redirect_url") 417 } 418 for _, redirectURL := range o.GetRedirectURLs() { 419 if _, err := url.Parse(redirectURL); err != nil { 420 return trace.BadParameter("bad RedirectURL '%v', err: %v", redirectURL, err) 421 } 422 } 423 424 if o.GetGoogleServiceAccountURI() != "" && o.GetGoogleServiceAccount() != "" { 425 return trace.BadParameter("one of either google_service_account_uri or google_service_account is supported, not both") 426 } 427 428 if o.GetGoogleServiceAccountURI() != "" { 429 uri, err := utils.ParseSessionsURI(o.GetGoogleServiceAccountURI()) 430 if err != nil { 431 return trace.Wrap(err) 432 } 433 if uri.Scheme != "file" { 434 return trace.BadParameter("only file:// scheme is supported for google_service_account_uri") 435 } 436 if o.GetGoogleAdminEmail() == "" { 437 return trace.BadParameter("whenever google_service_account_uri is specified, google_admin_email should be set as well, read https://developers.google.com/identity/protools/OAuth2ServiceAccount#delegatingauthority for more details") 438 } 439 } 440 441 if o.GetGoogleServiceAccount() != "" { 442 if o.GetGoogleAdminEmail() == "" { 443 return trace.BadParameter("whenever google_service_account is specified, google_admin_email should be set as well, read https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority for more details") 444 } 445 } 446 447 if o.Spec.MaxAge != nil { 448 maxAge := o.Spec.MaxAge.Value.Duration() 449 if maxAge < 0 { 450 return trace.BadParameter("max_age cannot be negative") 451 } 452 if maxAge.Round(time.Second) != maxAge { 453 return trace.BadParameter("max_age must be a multiple of seconds") 454 } 455 } 456 457 return nil 458 } 459 460 // GetAllowUnverifiedEmail returns true if unverified emails should be allowed in received users. 461 func (o *OIDCConnectorV3) GetAllowUnverifiedEmail() bool { 462 return o.Spec.AllowUnverifiedEmail 463 } 464 465 // GetMaxAge returns the amount of time that user logins are 466 // valid for and true if MaxAge is set. If a user logs in, but then 467 // does not login again within this time period, they will be forced 468 // to re-authenticate. 469 func (o *OIDCConnectorV3) GetMaxAge() (time.Duration, bool) { 470 if o.Spec.MaxAge == nil { 471 return 0, false 472 } 473 return o.Spec.MaxAge.Value.Duration(), true 474 } 475 476 // Check returns nil if all parameters are great, err otherwise 477 func (i *OIDCAuthRequest) Check() error { 478 if i.ConnectorID == "" { 479 return trace.BadParameter("ConnectorID: missing value") 480 } 481 if i.StateToken == "" { 482 return trace.BadParameter("StateToken: missing value") 483 } 484 if len(i.PublicKey) != 0 { 485 _, _, _, _, err := ssh.ParseAuthorizedKey(i.PublicKey) 486 if err != nil { 487 return trace.BadParameter("PublicKey: bad key: %v", err) 488 } 489 if (i.CertTTL > defaults.MaxCertDuration) || (i.CertTTL < defaults.MinCertDuration) { 490 return trace.BadParameter("CertTTL: wrong certificate TTL") 491 } 492 } 493 494 // we could collapse these two checks into one, but the error message would become ambiguous. 495 if i.SSOTestFlow && i.ConnectorSpec == nil { 496 return trace.BadParameter("ConnectorSpec cannot be nil when SSOTestFlow is true") 497 } 498 499 if !i.SSOTestFlow && i.ConnectorSpec != nil { 500 return trace.BadParameter("ConnectorSpec must be nil when SSOTestFlow is false") 501 } 502 503 return nil 504 }