-
Notifications
You must be signed in to change notification settings - Fork 189
Description
Describe the bug
This proxy supports a variety of mechanisms to authorize access to a host:
Lines 48 to 59 in 290f27e
| validators := []options.Validator{} | |
| if len(upstreamConfig.AllowedEmailAddresses) != 0 { | |
| validators = append(validators, options.NewEmailAddressValidator(upstreamConfig.AllowedEmailAddresses)) | |
| } | |
| if len(upstreamConfig.AllowedEmailDomains) != 0 { | |
| validators = append(validators, options.NewEmailDomainValidator(upstreamConfig.AllowedEmailDomains)) | |
| } | |
| if len(upstreamConfig.AllowedGroups) != 0 { | |
| validators = append(validators, options.NewEmailGroupValidator(provider, upstreamConfig.AllowedGroups)) | |
| } |
When responding to the Oauth callback (that is, when a user first authenticates and initiates a session), the proxy requires any of its validators be satisfied before proceeding:
sso/internal/proxy/oauthproxy.go
Lines 481 to 482 in 290f27e
| errors := options.RunValidators(p.Validators, session) | |
| if len(errors) == len(p.Validators) { |
However, when the proxy is refreshing a session sometime later, it requires that a user satisfy "groups" validation, exclusively:
sso/internal/proxy/oauthproxy.go
Lines 660 to 663 in 290f27e
| } else if session.RefreshPeriodExpired() { | |
| // Refresh period is the period in which the access token is valid. This is ultimately | |
| // controlled by the upstream provider and tends to be around 1 hour. | |
| ok, err := p.provider.RefreshSession(session, allowedGroups) |
sso/internal/proxy/providers/sso.go
Lines 274 to 289 in 290f27e
| inGroups, validGroup, err := p.ValidateGroup(s.Email, allowedGroups, newToken) | |
| if err != nil { | |
| // When we detect that the auth provider is not explicitly denying | |
| // authentication, and is merely unavailable, we refresh and continue | |
| // as normal during the "grace period" | |
| if err == ErrAuthProviderUnavailable && p.withinGracePeriod(s) { | |
| tags := []string{"action:refresh_session", "error:user_groups_failed"} | |
| p.StatsdClient.Incr("provider_error_fallback", tags, 1.0) | |
| s.RefreshDeadline = extendDeadline(p.SessionValidTTL) | |
| return true, nil | |
| } | |
| return false, err | |
| } | |
| if !validGroup { | |
| return false, errors.New("Group membership revoked") | |
| } |
sso/internal/proxy/providers/sso.go
Lines 188 to 201 in 290f27e
| userGroups, err := p.UserGroups(email, allowedGroups, accessToken) | |
| if err != nil { | |
| return nil, false, err | |
| } | |
| allowed := false | |
| for _, userGroup := range userGroups { | |
| for _, allowedGroup := range allowedGroups { | |
| if userGroup == allowedGroup { | |
| inGroups = append(inGroups, userGroup) | |
| allowed = true | |
| } | |
| } | |
| } |
I might be reading things wrong, but I have produced this with a live instance, and it seems like strange behavior to me.
The consequence of this is a user who satisfies email domain validation but fails group validation will be able to authenticate and briefly see the service, until the refresh period expires (or validation period, for that matter), where they'll be logged out.
To Reproduce
upstream_configs.yml
Create a group named nobody@corp.com with 0 people (or as close to it as the provider allows). Register a backend that uses this group in the allowed_groups field:
- service: my-service
default:
from: my-service.int.corp.com
to: http://my-service.corp-internal.com
options:
allowed_groups:
# an empty google group
- nobody@corp.comrun the service, ensuring that DEFAULT_ALLOWED_EMAIL_DOMAINS=corp.com. Then, as a user not in nobody@corp.com, attempt to access my-service.int.corp.com. You will succeed, but be logged out as soon as the session refreshes.
Expected behavior
The user should not be logged out when the session is refreshed.