Skip to content

Commit 4e2d56d

Browse files
authored
Merge pull request #63 from agilezebra/logUnauthorized
add logUnauthorized option - not documented yet
2 parents b9931e5 + 1cdbd81 commit 4e2d56d

File tree

4 files changed

+50
-5
lines changed

4 files changed

+50
-5
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ experimental:
1919
plugins:
2020
jwt:
2121
moduleName: github.com/agilezebra/jwt-middleware
22-
version: v1.3.2
22+
version: v1.3.3
2323
```
2424
1b. or with command-line options:
2525
2626
```yaml
2727
command:
2828
...
2929
- "--experimental.plugins.jwt.modulename=github.com/agilezebra/jwt-middleware"
30-
- "--experimental.plugins.jwt.version=v1.3.2"
30+
- "--experimental.plugins.jwt.version=v1.3.3"
3131
```
3232
3333
2) Configure and activate the plugin as a middleware in your dynamic traefik config:

jwt.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type Config struct {
4444
RemoveMissingHeaders bool `json:"removeMissingHeaders,omitempty"`
4545
ForwardToken bool `json:"forwardToken,omitempty"`
4646
Freshness int64 `json:"freshness,omitempty"`
47+
LogUnauthorized string `json:"logUnauthorized,omitempty"`
4748
}
4849

4950
// JWTPlugin is a traefik middleware plugin that authorizes access based on JWT tokens.
@@ -70,6 +71,7 @@ type JWTPlugin struct {
7071
forwardToken bool // If true, the token is forwarded to the backend
7172
freshness int64 // The maximum age of a token in seconds
7273
environment map[string]string // Map of environment variables
74+
logUnauthorized string // If set, log the details of the failed requirements to the level specified
7375
}
7476

7577
// TemplateVariables are the per-request variables passed to Go templates for interpolation, such as the require and redirect templates.
@@ -161,6 +163,7 @@ func New(_ context.Context, next http.Handler, config *Config, name string) (htt
161163
removeMissingHeaders: config.RemoveMissingHeaders,
162164
forwardToken: config.ForwardToken,
163165
freshness: config.Freshness,
166+
logUnauthorized: strings.ToUpper(config.LogUnauthorized),
164167
environment: environment(),
165168
}
166169

@@ -610,6 +613,10 @@ func (plugin *JWTPlugin) NewTemplateVariables(request *http.Request) *TemplateVa
610613
variables["URL"] = fmt.Sprintf("%s://%s%s", variables["Scheme"], variables["Host"], variables["Path"])
611614
}
612615

616+
if plugin.logUnauthorized != "" {
617+
variables["logUnauthorized"] = plugin.logUnauthorized
618+
}
619+
613620
return &variables
614621
}
615622

jwt_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ func TestServeHTTP(tester *testing.T) {
479479
Expect: http.StatusForbidden,
480480
Config: `
481481
secret: fixed secret
482+
logUnauthorized: info
482483
require:
483484
authority: "test.company.com"`,
484485
Claims: `{"authority": "*.example.com"}`,
@@ -1367,6 +1368,18 @@ func TestServeHTTP(tester *testing.T) {
13671368
Method: jwt.SigningMethodES256,
13681369
CookieName: "Authorization",
13691370
},
1371+
{
1372+
Name: "invalid integer claim",
1373+
Expect: http.StatusForbidden,
1374+
Config: `
1375+
infoToStdout: true
1376+
logUnauthorized: info
1377+
require:
1378+
int: 0`,
1379+
ClaimsMap: jwt.MapClaims{"int": 1},
1380+
Method: jwt.SigningMethodES256,
1381+
CookieName: "Authorization",
1382+
},
13701383
{
13711384
Name: "float claim",
13721385
Expect: http.StatusOK,
@@ -1378,6 +1391,18 @@ func TestServeHTTP(tester *testing.T) {
13781391
Method: jwt.SigningMethodES256,
13791392
CookieName: "Authorization",
13801393
},
1394+
{
1395+
Name: "invalid float claim",
1396+
Expect: http.StatusForbidden,
1397+
Config: `
1398+
infoToStdout: true
1399+
logUnauthorized: info
1400+
require:
1401+
float: 0.0`,
1402+
ClaimsMap: jwt.MapClaims{"float": 1.0},
1403+
Method: jwt.SigningMethodES256,
1404+
CookieName: "Authorization",
1405+
},
13811406
{
13821407
Name: "claim with different type",
13831408
Expect: http.StatusForbidden,

validate.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"log"
99
"strings"
1010

11+
"github.com/agilezebra/jwt-middleware/logger"
1112
"github.com/danwakefield/fnmatch"
1213
)
1314

@@ -122,6 +123,7 @@ outer:
122123
// variables is required in the interface and passed on recursively but ultimately ignored by ValueRequirement
123124
// having been already interpolated by TemplateRequirement
124125
func (requirement ValueRequirement) Validate(value any, variables *TemplateVariables) error {
126+
level, verbose := (*variables)["logUnauthorized"]
125127
switch value := value.(type) {
126128
case []any:
127129
for _, value := range value {
@@ -142,22 +144,33 @@ func (requirement ValueRequirement) Validate(value any, variables *TemplateVaria
142144
case string:
143145
required, ok := requirement.value.(string)
144146
if ok {
145-
if fnmatch.Match(value, required, 0) || value == fmt.Sprintf("*.%s", required) {
147+
if wildcardMatch(value, required) {
146148
return nil
147149
}
150+
if verbose {
151+
logger.Log(level, "claim is not valid: require:%s got:%v", required, value)
152+
}
148153
}
149154
case json.Number:
150155
switch requirement.value.(type) {
151156
case int:
152157
converted, err := value.Int64()
153-
if err == nil && converted == int64(requirement.value.(int)) {
158+
required := int64(requirement.value.(int))
159+
if err == nil && converted == required {
154160
return nil
155161
}
162+
if verbose {
163+
logger.Log(level, "claim is not valid: require:%d got:%v", required, value)
164+
}
156165
case float64:
157166
converted, err := value.Float64()
158-
if err == nil && converted == requirement.value.(float64) {
167+
required := requirement.value.(float64)
168+
if err == nil && converted == required {
159169
return nil
160170
}
171+
if verbose {
172+
logger.Log(level, "claim is not valid: require:%f got:%v", required, value)
173+
}
161174
default:
162175
log.Printf("unsupported requirement type for json.Number comparison: %T %v", requirement.value, requirement.value)
163176
return fmt.Errorf("unsupported requirement type for json.Number comparison")

0 commit comments

Comments
 (0)