git.iko.soy/typeable/oauth2-proxy
on master help sign in
base64 cookie support
commitcdebfd643698aa6e75d0a64865dfa5442a0a1623 authorJehiah Czebotar <jehiah@gmail.com> dateMon 20 Jun 2016, 07:17 (10 years ago) parent57f82ed
9 files changed+93−18
cookie/cookies.go+2−2 cookie/cookies_test.go+20−1 main.go+1−1 oauthproxy.go+4−5 oauthproxy_test.go+1−1 options.go+35−3 options_test.go+26−1 providers/github.go+2−2 providers/session_state_test.go+2−2
cookie/cookies.go+2−2
@@ -85,8 +85,8 @@ type Cipher struct {
8585}
8686
8787// NewCipher returns a new aes Cipher for encrypting cookie values
88−func NewCipher(secret string) (*Cipher, error) {
89− c, err := aes.NewCipher([]byte(secret))
88+func NewCipher(secret []byte) (*Cipher, error) {
89+ c, err := aes.NewCipher(secret)
9090 if err != nil {
9191 return nil, err
9292 }
cookie/cookies_test.go+20−1
@@ -1,6 +1,7 @@
11package cookie
22
33import (
4+ "encoding/base64"
45 "testing"
56
67 "github.com/bmizerany/assert"
@@ -9,7 +10,25 @@ import (
910func TestEncodeAndDecodeAccessToken(t *testing.T) {
1011 const secret = "0123456789abcdefghijklmnopqrstuv"
1112 const token = "my access token"
12− c, err := NewCipher(secret)
13+ c, err := NewCipher([]byte(secret))
14+ assert.Equal(t, nil, err)
15+
16+ encoded, err := c.Encrypt(token)
17+ assert.Equal(t, nil, err)
18+
19+ decoded, err := c.Decrypt(encoded)
20+ assert.Equal(t, nil, err)
21+
22+ assert.NotEqual(t, token, encoded)
23+ assert.Equal(t, token, decoded)
24+}
25+
26+func TestEncodeAndDecodeAccessTokenB64(t *testing.T) {
27+ const secret_b64 = "A3Xbr6fu6Al0HkgrP1ztjb-mYiwmxgNPP-XbNsz1WBk="
28+ const token = "my access token"
29+
30+ secret, err := base64.URLEncoding.DecodeString(secret_b64)
31+ c, err := NewCipher([]byte(secret))
1332 assert.Equal(t, nil, err)
1433
1534 encoded, err := c.Encrypt(token)
main.go+1−1
@@ -55,7 +55,7 @@ func main() {
5555 flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in)")
5656
5757 flagSet.String("cookie-name", "_oauth2_proxy", "the name of the cookie that the oauth_proxy creates")
58− flagSet.String("cookie-secret", "", "the seed string for secure cookies")
58+ flagSet.String("cookie-secret", "", "the seed string for secure cookies (optionally base64 encoded)")
5959 flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)*")
6060 flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
6161 flagSet.Duration("cookie-refresh", time.Duration(0), "refresh the cookie after this duration; 0 to disable")
oauthproxy.go+4−5
@@ -1,7 +1,7 @@
11package main
22
33import (
4− "encoding/base64"
4+ b64 "encoding/base64"
55 "errors"
66 "fmt"
77 "html/template"
@@ -164,10 +164,9 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
164164 var cipher *cookie.Cipher
165165 if opts.PassAccessToken || (opts.CookieRefresh != time.Duration(0)) {
166166 var err error
167− cipher, err = cookie.NewCipher(opts.CookieSecret)
167+ cipher, err = cookie.NewCipher(secretBytes(opts.CookieSecret))
168168 if err != nil {
169− log.Fatal("error creating AES cipher with "+
170− "cookie-secret ", opts.CookieSecret, ": ", err)
169+ log.Fatal("cookie-secret error: ", err)
171170 }
172171 }
173172
@@ -626,7 +625,7 @@ func (p *OAuthProxy) CheckBasicAuth(req *http.Request) (*providers.SessionState,
626625 if len(s) != 2 || s[0] != "Basic" {
627626 return nil, fmt.Errorf("invalid Authorization header %s", req.Header.Get("Authorization"))
628627 }
629− b, err := base64.StdEncoding.DecodeString(s[1])
628+ b, err := b64.StdEncoding.DecodeString(s[1])
630629 if err != nil {
631630 return nil, err
632631 }
oauthproxy_test.go+1−1
@@ -427,7 +427,7 @@ func NewProcessCookieTest(opts ProcessCookieTestOpts) *ProcessCookieTest {
427427 pc_test.opts = NewOptions()
428428 pc_test.opts.ClientID = "bazquux"
429429 pc_test.opts.ClientSecret = "xyzzyplugh"
430− pc_test.opts.CookieSecret = "0123456789abcdef"
430+ pc_test.opts.CookieSecret = "0123456789abcdefabcd"
431431 // First, set the CookieRefresh option so proxy.AesCipher is created,
432432 // needed to encrypt the access_token.
433433 pc_test.opts.CookieRefresh = time.Hour
options.go+35−3
@@ -2,6 +2,7 @@ package main
22
33import (
44 "crypto"
5+ "encoding/base64"
56 "fmt"
67 "net/url"
78 "os"
@@ -156,17 +157,25 @@ func (o *Options) Validate() error {
156157 if o.PassAccessToken || (o.CookieRefresh != time.Duration(0)) {
157158 valid_cookie_secret_size := false
158159 for _, i := range []int{16, 24, 32} {
159− if len(o.CookieSecret) == i {
160+ if len(secretBytes(o.CookieSecret)) == i {
160161 valid_cookie_secret_size = true
161162 }
162163 }
164+ var decoded bool
165+ if string(secretBytes(o.CookieSecret)) != o.CookieSecret {
166+ decoded = true
167+ }
163168 if valid_cookie_secret_size == false {
169+ var suffix string
170+ if decoded {
171+ suffix = fmt.Sprintf(" note: cookie secret was base64 decoded from %q", o.CookieSecret)
172+ }
164173 msgs = append(msgs, fmt.Sprintf(
165174 "cookie_secret must be 16, 24, or 32 bytes "+
166175 "to create an AES cipher when "+
167176 "pass_access_token == true or "+
168− "cookie_refresh != 0, but is %d bytes",
169− len(o.CookieSecret)))
177+ "cookie_refresh != 0, but is %d bytes.%s",
178+ len(secretBytes(o.CookieSecret)), suffix))
170179 }
171180 }
172181
@@ -251,3 +260,26 @@ func parseSignatureKey(o *Options, msgs []string) []string {
251260 }
252261 return msgs
253262}
263+
264+func addPadding(secret string) string {
265+ padding := len(secret) % 4
266+ switch padding {
267+ case 1:
268+ return secret + "==="
269+ case 2:
270+ return secret + "=="
271+ case 3:
272+ return secret + "="
273+ default:
274+ return secret
275+ }
276+}
277+
278+// secretBytes attempts to base64 decode the secret, if that fails it treats the secret as binary
279+func secretBytes(secret string) []byte {
280+ b, err := base64.URLEncoding.DecodeString(addPadding(secret))
281+ if err == nil {
282+ return []byte(addPadding(string(b)))
283+ }
284+ return []byte(secret)
285+}
options_test.go+26−1
@@ -160,7 +160,7 @@ func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
160160 o := testOptions()
161161 assert.Equal(t, nil, o.Validate())
162162
163− o.CookieSecret = "0123456789abcdef"
163+ o.CookieSecret = "0123456789abcdefabcd"
164164 o.CookieRefresh = o.CookieExpire
165165 assert.NotEqual(t, nil, o.Validate())
166166
@@ -168,6 +168,31 @@ func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
168168 assert.Equal(t, nil, o.Validate())
169169}
170170
171+func TestBase64CookieSecret(t *testing.T) {
172+ o := testOptions()
173+ assert.Equal(t, nil, o.Validate())
174+
175+ // 32 byte, base64 (urlsafe) encoded key
176+ o.CookieSecret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ="
177+ assert.Equal(t, nil, o.Validate())
178+
179+ // 32 byte, base64 (urlsafe) encoded key, w/o padding
180+ o.CookieSecret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ"
181+ assert.Equal(t, nil, o.Validate())
182+
183+ // 24 byte, base64 (urlsafe) encoded key
184+ o.CookieSecret = "Kp33Gj-GQmYtz4zZUyUDdqQKx5_Hgkv3"
185+ assert.Equal(t, nil, o.Validate())
186+
187+ // 16 byte, base64 (urlsafe) encoded key
188+ o.CookieSecret = "LFEqZYvYUwKwzn0tEuTpLA=="
189+ assert.Equal(t, nil, o.Validate())
190+
191+ // 16 byte, base64 (urlsafe) encoded key, w/o padding
192+ o.CookieSecret = "LFEqZYvYUwKwzn0tEuTpLA"
193+ assert.Equal(t, nil, o.Validate())
194+}
195+
171196func TestValidateSignatureKey(t *testing.T) {
172197 o := testOptions()
173198 o.SignatureKey = "sha1:secret"
providers/github.go+2−2
@@ -3,11 +3,11 @@ package providers
33import (
44 "encoding/json"
55 "fmt"
6− "strings"
76 "io/ioutil"
87 "log"
98 "net/http"
109 "net/url"
10+ "strings"
1111)
1212
1313type GitHubProvider struct {
@@ -64,7 +64,7 @@ func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
6464 "limit": {"100"},
6565 }
6666
67− endpoint := p.ValidateURL.Scheme + "://" + p.ValidateURL.Host + "/user/orgs?" + params.Encode()
67+ endpoint := p.ValidateURL.Scheme + "://" + p.ValidateURL.Host + "/user/orgs?" + params.Encode()
6868 req, _ := http.NewRequest("GET", endpoint, nil)
6969 req.Header.Set("Accept", "application/vnd.github.v3+json")
7070 resp, err := http.DefaultClient.Do(req)
providers/session_state_test.go+2−2
@@ -13,9 +13,9 @@ const secret = "0123456789abcdefghijklmnopqrstuv"
1313const altSecret = "0000000000abcdefghijklmnopqrstuv"
1414
1515func TestSessionStateSerialization(t *testing.T) {
16− c, err := cookie.NewCipher(secret)
16+ c, err := cookie.NewCipher([]byte(secret))
1717 assert.Equal(t, nil, err)
18− c2, err := cookie.NewCipher(altSecret)
18+ c2, err := cookie.NewCipher([]byte(altSecret))
1919 assert.Equal(t, nil, err)
2020 s := &SessionState{
2121 Email: "user@domain.com",