Skip to content

Commit

Permalink
feat: add "prefix" configuration to bearer token and OAuth2 introspec…
Browse files Browse the repository at this point in the history
…tion authenticators

This allows selecting between the two authenticators based on a prefix to the token.
  • Loading branch information
alnr committed Aug 31, 2023
1 parent b7267ad commit f1c9611
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 4 deletions.
4 changes: 3 additions & 1 deletion pipeline/authn/authenticator_bearer_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package authn
import (
"encoding/json"
"net/http"
"strings"

"github.com/pkg/errors"
"github.com/tidwall/gjson"
Expand All @@ -31,6 +32,7 @@ type AuthenticatorBearerTokenFilter struct {
type AuthenticatorBearerTokenConfiguration struct {
CheckSessionURL string `json:"check_session_url"`
BearerTokenLocation *helper.BearerTokenLocation `json:"token_from"`
Prefix string `json:"prefix"`
PreserveQuery bool `json:"preserve_query"`
PreservePath bool `json:"preserve_path"`
PreserveHost bool `json:"preserve_host"`
Expand Down Expand Up @@ -128,7 +130,7 @@ func (a *AuthenticatorBearerToken) Authenticate(r *http.Request, session *Authen
}

token := helper.BearerTokenFromRequest(r, cf.BearerTokenLocation)
if token == "" {
if token == "" || !strings.HasPrefix(token, cf.Prefix) {
return errors.WithStack(ErrAuthenticatorNotResponsible)
}

Expand Down
9 changes: 8 additions & 1 deletion pipeline/authn/authenticator_bearer_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ func TestAuthenticatorBearerToken(t *testing.T) {
expectErr: true,
expectExactErr: ErrAuthenticatorNotResponsible,
},
{
d: "should return error saying that authenticator is not responsible for validating the request, as the token does not have the specified prefix",
r: &http.Request{Header: http.Header{"Authorization": {"bearer secret_token"}}},
config: []byte(`{"prefix": "not_secret"}`),
expectErr: true,
expectExactErr: ErrAuthenticatorNotResponsible,
},
{
d: "should fail because session store returned 400",
r: &http.Request{Header: http.Header{"Authorization": {"bearer token"}}, URL: &url.URL{Path: ""}},
Expand Down Expand Up @@ -132,7 +139,7 @@ func TestAuthenticatorBearerToken(t *testing.T) {
w.WriteHeader(200)
w.Write([]byte(`{"sub": "123"}`))
},
config: []byte(`{"preserve_path": true, "force_method": "GET"}`),
config: []byte(`{"preserve_path": true, "force_method": "GET", "prefix": "zy"}`),
expectErr: false,
expectSess: &AuthenticationSession{
Subject: "123",
Expand Down
3 changes: 2 additions & 1 deletion pipeline/authn/authenticator_oauth2_introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type AuthenticatorOAuth2IntrospectionConfiguration struct {
IntrospectionURL string `json:"introspection_url"`
PreserveHost bool `json:"preserve_host"`
BearerTokenLocation *helper.BearerTokenLocation `json:"token_from"`
Prefix string `json:"prefix"`
IntrospectionRequestHeaders map[string]string `json:"introspection_request_headers"`
Retry *AuthenticatorOAuth2IntrospectionRetryConfiguration `json:"retry"`
Cache cacheConfig `json:"cache"`
Expand Down Expand Up @@ -186,7 +187,7 @@ func (a *AuthenticatorOAuth2Introspection) Authenticate(r *http.Request, session
}

token := helper.BearerTokenFromRequest(r, cf.BearerTokenLocation)
if token == "" {
if token == "" || !strings.HasPrefix(token, cf.Prefix) {
return errors.WithStack(ErrAuthenticatorNotResponsible)
}

Expand Down
9 changes: 8 additions & 1 deletion pipeline/authn/authenticator_oauth2_introspection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ func TestAuthenticatorOAuth2Introspection(t *testing.T) {
expectErr: true,
expectExactErr: ErrAuthenticatorNotResponsible,
},
{
d: "should return error saying that authenticator is not responsible for validating the request, as the token does not have the specified prefix",
r: &http.Request{Header: http.Header{"Authorization": {"bearer secret_token"}}},
config: []byte(`{"prefix": "not_secret"}`),
expectErr: true,
expectExactErr: ErrAuthenticatorNotResponsible,
},
{
d: "should return error saying that authenticator is not responsible for validating the request, as the token was not provided in a proper location (custom query parameter)",
r: &http.Request{
Expand Down Expand Up @@ -119,7 +126,7 @@ func TestAuthenticatorOAuth2Introspection(t *testing.T) {
RawQuery: "token=" + "token",
},
},
config: []byte(`{"token_from": {"query_parameter": "token"}}`),
config: []byte(`{"token_from": {"query_parameter": "token"}, "prefix": "tok"}`),
expectErr: false,
setup: func(t *testing.T, m *httprouter.Router) {
m.POST("/oauth2/introspect", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
Expand Down
10 changes: 10 additions & 0 deletions spec/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,11 @@
}
]
},
"prefix": {
"title": "Token Prefix",
"type": "string",
"description": "The token is considered only if starts with this string."
},
"preserve_query": {
"title": "Preserve Query",
"type": "boolean",
Expand Down Expand Up @@ -822,6 +827,11 @@
"type": "string"
}
},
"prefix": {
"title": "Token Prefix",
"type": "string",
"description": "The token is considered only if starts with this string."
},
"preserve_host": {
"title": "Preserve Host",
"type": "boolean",
Expand Down

0 comments on commit f1c9611

Please sign in to comment.