diff --git a/README.md b/README.md index 1dc2baf6b..4cfb27940 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,10 @@ OpenID Connect is a spec for OAUTH 2.0 + identity that is implemented by many ma -cookie-secure=false -email-domain example.com +If you enable cookie-refresh, it should be set to the same duration as token lifetime +(due to a limitation in `oauth2_proxy` - see [bitly/oauth2_proxy#620](https://github.com/bitly/oauth2_proxy/pull/620)). + + ## Email Authentication To authorize by email domain use `--email-domain=yourcompany.com`. To authorize individual email addresses use `--authenticated-emails-file=/path/to/file` with one email per line. To authorize all email addresses use `--email-domain=*`. diff --git a/oauthproxy.go b/oauthproxy.go index f7bf65c4d..94a25cbed 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -259,10 +259,6 @@ func (p *OAuthProxy) redeemCode(host, code string) (s *providers.SessionState, e func (p *OAuthProxy) MakeSessionCookie(req *http.Request, value string, expiration time.Duration, now time.Time) *http.Cookie { if value != "" { value = cookie.SignedValue(p.CookieSeed, p.CookieName, value, now) - if len(value) > 4096 { - // Cookies cannot be larger than 4kb - log.Printf("WARNING - Cookie Size: %d bytes", len(value)) - } } return p.makeCookie(req, p.CookieName, value, expiration, now) } @@ -281,6 +277,11 @@ func (p *OAuthProxy) makeCookie(req *http.Request, name string, value string, ex log.Printf("Warning: request host is %q but using configured cookie domain of %q", domain, p.CookieDomain) } } + if len(value) > 3600 { + // nginx default response header limit is 4KiB, other software may have similar limits + // threshold includes margin for header name, cookie name, other cookie options + log.Printf("WARNING - %s cookie is very big: %d bytes", name, len(value)) + } return &http.Cookie{ Name: name, diff --git a/providers/oidc.go b/providers/oidc.go index 0c0fa52a9..6602eddb4 100644 --- a/providers/oidc.go +++ b/providers/oidc.go @@ -35,7 +35,58 @@ func (p *OIDCProvider) Redeem(redirectURL, code string) (s *SessionState, err er if err != nil { return nil, fmt.Errorf("token exchange: %v", err) } + s, err = p.createSessionState(token, ctx) + if err != nil { + return nil, fmt.Errorf("unable to update session: %v", err) + } + return +} + +func (p *OIDCProvider) RefreshSessionIfNeeded(s *SessionState) (bool, error) { + if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" { + return false, nil + } + + origExpiration := s.ExpiresOn + err := p.redeemRefreshToken(s) + if err != nil { + return false, fmt.Errorf("unable to redeem refresh token: %v", err) + } + + fmt.Printf("refreshed id token %s (expired on %s)\n", s, origExpiration) + return true, nil +} + +func (p *OIDCProvider) redeemRefreshToken(s *SessionState) (err error) { + c := oauth2.Config{ + ClientID: p.ClientID, + ClientSecret: p.ClientSecret, + Endpoint: oauth2.Endpoint{ + TokenURL: p.RedeemURL.String(), + }, + } + ctx := context.Background() + t := &oauth2.Token{ + RefreshToken: s.RefreshToken, + Expiry: time.Now().Add(-time.Hour), + } + token, err := c.TokenSource(ctx, t).Token() + if err != nil { + return fmt.Errorf("failed to get token: %v", err) + } + newSession, err := p.createSessionState(token, ctx) + if err != nil { + return fmt.Errorf("unable to update session: %v", err) + } + s.AccessToken = newSession.AccessToken + s.RefreshToken = newSession.RefreshToken + s.ExpiresOn = newSession.ExpiresOn + s.Email = newSession.Email + return +} + +func (p *OIDCProvider) createSessionState(token *oauth2.Token, ctx context.Context) (*SessionState, error) { rawIDToken, ok := token.Extra("id_token").(string) if !ok { return nil, fmt.Errorf("token response did not contain an id_token") @@ -63,23 +114,10 @@ func (p *OIDCProvider) Redeem(redirectURL, code string) (s *SessionState, err er return nil, fmt.Errorf("email in id_token (%s) isn't verified", claims.Email) } - s = &SessionState{ + return &SessionState{ AccessToken: token.AccessToken, RefreshToken: token.RefreshToken, ExpiresOn: token.Expiry, Email: claims.Email, - } - - return -} - -func (p *OIDCProvider) RefreshSessionIfNeeded(s *SessionState) (bool, error) { - if s == nil || s.ExpiresOn.After(time.Now()) || s.RefreshToken == "" { - return false, nil - } - - origExpiration := s.ExpiresOn - s.ExpiresOn = time.Now().Add(time.Second).Truncate(time.Second) - fmt.Printf("refreshed access token %s (expired on %s)\n", s, origExpiration) - return false, nil + }, nil }