Add possibility to encode the state param as UrlEncodedBase64
This commit is contained in:
parent
9473f3182a
commit
2434369974
|
|
@ -14,6 +14,7 @@
|
|||
- [#2269](https://github.com/oauth2-proxy/oauth2-proxy/pull/2269) Added Azure China (and other air gaped cloud) support (@mblaschke)
|
||||
- [#2237](https://github.com/oauth2-proxy/oauth2-proxy/pull/2237) adds an option to append CA certificates (@emsixteeen)
|
||||
- [#2128](https://github.com/oauth2-proxy/oauth2-proxy/pull/2128) Update dependencies (@vllvll)
|
||||
- [#2239](https://github.com/oauth2-proxy/oauth2-proxy/pull/2312) Add possibility to encode the state param as UrlEncodedBase64 (@brezinajn)
|
||||
- [#2274](https://github.com/oauth2-proxy/oauth2-proxy/pull/2274) Upgrade golang.org/x/net to v0.17.0 (@pierluigilenoci)
|
||||
- [#2278](https://github.com/oauth2-proxy/oauth2-proxy/pull/2278) Improve the Nginx auth_request example (@akunzai)
|
||||
- [#2282](https://github.com/oauth2-proxy/oauth2-proxy/pull/2282) Fixed checking Google Groups membership using Google Application Credentials (@kvanzuijlen)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -112,6 +113,7 @@ type OAuthProxy struct {
|
|||
redirectValidator redirect.Validator
|
||||
appDirector redirect.AppDirector
|
||||
|
||||
encodeState bool
|
||||
AuditClient *audit.Client
|
||||
}
|
||||
|
||||
|
|
@ -253,6 +255,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||
upstreamProxy: upstreamProxy,
|
||||
redirectValidator: redirectValidator,
|
||||
appDirector: appDirector,
|
||||
encodeState: opts.EncodeState,
|
||||
AuditClient: auditClient,
|
||||
}
|
||||
p.buildServeMux(opts.ProxyPrefix)
|
||||
|
|
@ -820,7 +823,7 @@ func (p *OAuthProxy) doOAuthStart(rw http.ResponseWriter, req *http.Request, ove
|
|||
callbackRedirect := p.getOAuthRedirectURI(req)
|
||||
loginURL := p.provider.GetLoginURL(
|
||||
callbackRedirect,
|
||||
encodeState(csrf.HashOAuthState(), appRedirect),
|
||||
encodeState(csrf.HashOAuthState(), appRedirect, p.encodeState),
|
||||
csrf.HashOIDCNonce(),
|
||||
extraParams,
|
||||
)
|
||||
|
|
@ -878,7 +881,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
|
|||
|
||||
csrf.ClearCookie(rw, req)
|
||||
|
||||
nonce, appRedirect, err := decodeState(req)
|
||||
nonce, appRedirect, err := decodeState(req.Form.Get("state"), p.encodeState)
|
||||
if err != nil {
|
||||
logger.Errorf("Error while parsing OAuth2 state: %v", err)
|
||||
p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
|
||||
|
|
@ -1221,18 +1224,28 @@ func checkAllowedEmails(req *http.Request, s *sessionsapi.SessionState) bool {
|
|||
|
||||
// encodedState builds the OAuth state param out of our nonce and
|
||||
// original application redirect
|
||||
func encodeState(nonce string, redirect string) string {
|
||||
return fmt.Sprintf("%v:%v", nonce, redirect)
|
||||
func encodeState(nonce string, redirect string, encode bool) string {
|
||||
rawString := fmt.Sprintf("%v:%v", nonce, redirect)
|
||||
if encode {
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(rawString))
|
||||
}
|
||||
return rawString
|
||||
}
|
||||
|
||||
// decodeState splits the reflected OAuth state response back into
|
||||
// the nonce and original application redirect
|
||||
func decodeState(req *http.Request) (string, string, error) {
|
||||
state := strings.SplitN(req.Form.Get("state"), ":", 2)
|
||||
if len(state) != 2 {
|
||||
func decodeState(state string, encode bool) (string, string, error) {
|
||||
toParse := state
|
||||
if encode {
|
||||
decoded, _ := base64.RawURLEncoding.DecodeString(state)
|
||||
toParse = string(decoded)
|
||||
}
|
||||
|
||||
parsedState := strings.SplitN(toParse, ":", 2)
|
||||
if len(parsedState) != 2 {
|
||||
return "", "", errors.New("invalid length")
|
||||
}
|
||||
return state[0], state[1], nil
|
||||
return parsedState[0], parsedState[1], nil
|
||||
}
|
||||
|
||||
// addHeadersForProxying adds the appropriate headers the request / response for proxying
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ func (patTest *PassAccessTokenTest) getCallbackEndpoint() (httpCode int, cookie
|
|||
http.MethodGet,
|
||||
fmt.Sprintf(
|
||||
"/oauth2/callback?code=callback_code&state=%s",
|
||||
encodeState(csrf.HashOAuthState(), "%2F"),
|
||||
encodeState(csrf.HashOAuthState(), "%2F", false),
|
||||
),
|
||||
strings.NewReader(""),
|
||||
)
|
||||
|
|
@ -3288,6 +3288,29 @@ func TestAuthOnlyAllowedEmailDomains(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStateEncodesCorrectly(t *testing.T) {
|
||||
state := "some_state_to_test"
|
||||
nonce := "some_nonce_to_test"
|
||||
|
||||
encodedResult := encodeState(nonce, state, true)
|
||||
assert.Equal(t, "c29tZV9ub25jZV90b190ZXN0OnNvbWVfc3RhdGVfdG9fdGVzdA", encodedResult)
|
||||
|
||||
notEncodedResult := encodeState(nonce, state, false)
|
||||
assert.Equal(t, "some_nonce_to_test:some_state_to_test", notEncodedResult)
|
||||
}
|
||||
|
||||
func TestStateDecodesCorrectly(t *testing.T) {
|
||||
nonce, redirect, _ := decodeState("c29tZV9ub25jZV90b190ZXN0OnNvbWVfc3RhdGVfdG9fdGVzdA", true)
|
||||
|
||||
assert.Equal(t, "some_nonce_to_test", nonce)
|
||||
assert.Equal(t, "some_state_to_test", redirect)
|
||||
|
||||
nonce2, redirect2, _ := decodeState("some_nonce_to_test:some_state_to_test", false)
|
||||
|
||||
assert.Equal(t, "some_nonce_to_test", nonce2)
|
||||
assert.Equal(t, "some_state_to_test", redirect2)
|
||||
}
|
||||
|
||||
func TestAuthOnlyAllowedEmails(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ type Options struct {
|
|||
SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"`
|
||||
SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"`
|
||||
ForceJSONErrors bool `flag:"force-json-errors" cfg:"force_json_errors"`
|
||||
EncodeState bool `flag:"encode-state" cfg:"encode_state"`
|
||||
AllowQuerySemicolons bool `flag:"allow-query-semicolons" cfg:"allow_query_semicolons"`
|
||||
|
||||
SignatureKey string `flag:"signature-key" cfg:"signature_key"`
|
||||
|
|
@ -135,6 +136,7 @@ func NewFlagSet() *pflag.FlagSet {
|
|||
flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS providers")
|
||||
flagSet.Bool("skip-jwt-bearer-tokens", false, "will skip requests that have verified JWT bearer tokens (default false)")
|
||||
flagSet.Bool("force-json-errors", false, "will force JSON errors instead of HTTP error pages or redirects")
|
||||
flagSet.Bool("encode-state", false, "will encode oauth state with base64")
|
||||
flagSet.Bool("allow-query-semicolons", false, "allow the use of semicolons in query args")
|
||||
flagSet.StringSlice("extra-jwt-issuers", []string{}, "if skip-jwt-bearer-tokens is set, a list of extra JWT issuer=audience pairs (where the issuer URL has a .well-known/openid-configuration or a .well-known/jwks.json)")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue