Add option to exclude URLs from the authentication.

This commit is contained in:
2024-08-29 13:13:57 +01:00
parent 448392e9bd
commit 77ead9b8a1
5 changed files with 98 additions and 7 deletions
+6 -3
View File
@@ -12,11 +12,14 @@ testData:
clientSecret: secret
callbackURL: /oauth2/callback
logoutURL: /oauth2/logout
scopes:
scopes: # If not provided, default scopes will be used (openid, email, profile)
- openid
- email
- profile
sessionEncryptionKey: potato-secret
forceHTTPS: false
logLevel: debug
rateLimit: 100
logLevel: debug # debug, info, warn, error
rateLimit: 100 # Simple rate limiter to prevent brute force attacks
excludedURLs: # Determines the list of URLs which are NOT a subject to authentication
- /login
- /my-public-data
+6 -3
View File
@@ -90,12 +90,15 @@ http:
clientSecret: secret
callbackURL: /oauth2/callback
logoutURL: /oauth2/logout
scopes:
scopes: # If not provided, default scopes will be used (openid, email, profile)
- openid
- email
- profile
sessionEncryptionKey: potato-secret
forceHTTPS: false
logLevel: info
rateLimit: 100 # 100 requests per minute
logLevel: debug # debug, info, warn, error
rateLimit: 100 # Simple rate limiter to prevent brute force attacks
excludedURLs: # Determines the list of URLs which are NOT a subject to authentication
- /login # covers /login, /login/me, /login/reminder etc.
- /my-public-data
```
+23 -1
View File
@@ -49,6 +49,7 @@ type TraefikOidc struct {
redirectURL string
tokenVerifier TokenVerifier
jwtVerifier JWTVerifier
excludedURLs map[string]struct{}
}
type ProviderMetadata struct {
@@ -178,7 +179,14 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
tokenCache: NewTokenCache(),
httpClient: httpClient,
logger: NewLogger(config.LogLevel),
redirectURL: "",
excludedURLs: func() map[string]struct{} {
m := make(map[string]struct{})
for _, url := range config.ExcludedURLs {
m[url] = struct{}{}
}
return m
}(),
redirectURL: "",
}
t.tokenVerifier = t
@@ -211,6 +219,11 @@ func discoverProviderMetadata(providerURL string, httpClient http.Client) (*Prov
}
func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if t.determineExcludedURL(req.URL.Path) {
t.next.ServeHTTP(rw, req)
return
}
t.scheme = t.determineScheme(req)
host := t.determineHost(req)
@@ -266,6 +279,15 @@ func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
t.initiateAuthentication(rw, req, session, t.redirectURL)
}
func (t *TraefikOidc) determineExcludedURL(currentRequest string) bool {
for excludedURL := range t.excludedURLs {
if strings.HasPrefix(currentRequest, excludedURL) {
return true
}
}
return false
}
func (t *TraefikOidc) determineScheme(req *http.Request) string {
if t.forceHTTPS {
return "https"
+62
View File
@@ -749,3 +749,65 @@ func (suite *TraefikOidcTestSuite) TestDiscoverProviderMetadata_InvalidURL() {
suite.Error(err)
suite.Contains(err.Error(), "failed to fetch provider metadata")
}
func (suite *TraefikOidcTestSuite) TestServeHTTP_ExcludedURLs() {
suite.oidc.excludedURLs = map[string]struct{}{
"/public": {},
"/api/health": {},
}
testCases := []struct {
name string
url string
expectedStatus int
expectedBody string
}{
{
name: "Excluded URL - public",
url: "http://example.com/public",
expectedStatus: http.StatusOK,
expectedBody: "Public content",
},
{
name: "Excluded URL - api health",
url: "http://example.com/api/health",
expectedStatus: http.StatusOK,
expectedBody: "API is healthy",
},
{
name: "Non-excluded URL",
url: "http://example.com/private",
expectedStatus: http.StatusFound, // Expect a redirect to auth
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
req := httptest.NewRequest("GET", tc.url, nil)
rw := httptest.NewRecorder()
if !strings.HasPrefix(req.URL.Path, "/public") && !strings.HasPrefix(req.URL.Path, "/api/health") {
// For non-excluded URLs, set up session mock
session := sessions.NewSession(suite.mockStore, cookieName)
suite.mockStore.On("Get", req, cookieName).Return(session, nil).Once()
suite.mockStore.On("Save", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
}
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(tc.expectedBody))
})
suite.oidc.next = nextHandler
suite.oidc.ServeHTTP(rw, req)
suite.Equal(tc.expectedStatus, rw.Code)
if tc.expectedStatus == http.StatusOK {
suite.Equal(tc.expectedBody, rw.Body.String())
}
suite.mockStore.AssertExpectations(suite.T())
})
}
}
+1
View File
@@ -23,6 +23,7 @@ type Config struct {
SessionEncryptionKey string `json:"sessionEncryptionKey"`
ForceHTTPS bool `json:"forceHTTPS"`
RateLimit int `json:"rateLimit"`
ExcludedURLs []string `json:"excludedURLs"`
}
func CreateConfig() *Config {