Skip to content

Commit 94da3d9

Browse files
committed
feat(jwtmw): WithScopes support for custom scope checker.
1 parent 4631e85 commit 94da3d9

6 files changed

+44
-28
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 0.3.0
2+
3+
[All Changes](https://github.com/crossid/crossid-go/compare/v0.2.0...v0.3.0)
4+
5+
### Major Changes
6+
7+
- jwtmw - `WithScopes` middleware support for custom scope checker.
8+
19
## 0.2.0
210

311
[All Changes](https://github.com/crossid/crossid-go/compare/v0.1.0...v0.2.0)

pkg/jwtmw/helper.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package jwtmw
22

33
import (
4+
"context"
45
"fmt"
56
"github.com/crossid/crossid-go/pkg/x/stringslice"
67
)
78

8-
func scopesCheckerOR(required, candidates []string) error {
9+
func ScopesCheckerOR(_ context.Context, required, candidates []string) error {
910
if len(required) == 0 {
1011
return nil
1112
}
@@ -18,7 +19,7 @@ func scopesCheckerOR(required, candidates []string) error {
1819
return ErrMissingClaim
1920
}
2021

21-
func scopesCheckerAND(required, candidates []string) error {
22+
func scopesCheckerAND(_ context.Context, required, candidates []string) error {
2223
for _, r := range required {
2324
f := false
2425
if stringslice.IndexOf(candidates, r) > -1 {

pkg/jwtmw/helper_test.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package jwtmw
22

3-
import "testing"
3+
import (
4+
"context"
5+
"testing"
6+
)
47

58
func TestScopesCheckerOR(t *testing.T) {
69
for k, tc := range []struct {
@@ -76,7 +79,7 @@ func TestScopesCheckerOR(t *testing.T) {
7679
v: false,
7780
},
7881
} {
79-
err := scopesCheckerOR(tc.r, tc.c)
82+
err := ScopesCheckerOR(context.Background(), tc.r, tc.c)
8083
if tc.v {
8184
if err != nil {
8285
t.Errorf("case %d=%s expected to be valid but got: %s", k, tc.name, err)
@@ -170,7 +173,7 @@ func TestScopesCheckerAND(t *testing.T) {
170173
c: []string{"f", "e", "d", "c", "b", "Z"},
171174
},
172175
} {
173-
err := scopesCheckerAND(tc.r, tc.c)
176+
err := scopesCheckerAND(context.Background(), tc.r, tc.c)
174177
if tc.v {
175178
if err != nil {
176179
t.Errorf("case %d='%s' expected to be valid but got: %s", k, tc.name, err)

pkg/jwtmw/with_scopes.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func WithScopesCustom(required []string, opt ...WithScopesOpt) func(next http.Ha
5555
return
5656
}
5757

58-
if err := opts.scopesChecker(required, cl); err != nil {
58+
if err := opts.ScopesChecker(r.Context(), required, cl); err != nil {
5959
opts.Logger(Info, "scopes errors: %s", err)
6060
opts.ErrorWriter(w, r, ErrMissingClaim)
6161
return

pkg/jwtmw/with_scopes_opts.go

+10-20
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ import (
66
"net/http"
77
)
88

9-
type ConjunctionKind int
10-
11-
const (
12-
AND ConjunctionKind = iota
13-
OR
14-
)
9+
type ScopesCheckerFunc func(ctx context.Context, required []string, candidates []string) error
1510

1611
type withScopesOpts struct {
1712
// TokenCtxKey is the context key of an authenticated token value, typically set by the JWT middleware.
@@ -21,24 +16,15 @@ type withScopesOpts struct {
2116
// default implementation is naive as r.Context().Value(TokenCtxKey).(*jwt.Token)
2217
TokenFromContext func(ctx context.Context) (*jwt.Token, error)
2318
ClaimsFromToken claimFromTokenFunc
24-
// Conjunction defines whether all scopes (AND) are required or one (OR)
25-
// of the provided scopes is sufficient for the request to be accepted.
26-
Conjunction ConjunctionKind
2719
// ErrorWriter writes an error into w
2820
ErrorWriter errorWriter
2921
// Logger logs various messages
3022
Logger logger
31-
scopesChecker func(required []string, candidates []string) error
23+
ScopesChecker ScopesCheckerFunc
3224
}
3325

3426
type WithScopesOpt func(*withScopesOpts)
3527

36-
func WithConjunction(c ConjunctionKind) WithScopesOpt {
37-
return func(o *withScopesOpts) {
38-
o.Conjunction = c
39-
}
40-
}
41-
4228
func WithErrorWriter(w errorWriter) WithScopesOpt {
4329
return func(o *withScopesOpts) {
4430
o.ErrorWriter = w
@@ -57,6 +43,12 @@ func WithClaimsFromToken(f claimFromTokenFunc) WithScopesOpt {
5743
}
5844
}
5945

46+
func WithScopesChecker(f ScopesCheckerFunc) WithScopesOpt {
47+
return func(o *withScopesOpts) {
48+
o.ScopesChecker = f
49+
}
50+
}
51+
6052
func newWithScopesOpts(opts []WithScopesOpt) *withScopesOpts {
6153
o := new(withScopesOpts)
6254
for _, oo := range opts {
@@ -89,10 +81,8 @@ func newWithScopesOpts(opts []WithScopesOpt) *withScopesOpts {
8981
o.TokenCtxKey = TokenCtxKey
9082
}
9183

92-
if o.Conjunction == OR {
93-
o.scopesChecker = scopesCheckerOR
94-
} else {
95-
o.scopesChecker = scopesCheckerAND
84+
if o.ScopesChecker == nil {
85+
o.ScopesChecker = scopesCheckerAND
9686
}
9787

9888
if o.ClaimsFromToken == nil {

pkg/jwtmw/with_scopes_test.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,18 @@ func claimsFromTok(ctx context.Context, t *jwt.Token) ([]string, error) {
1818
return t.Claims.(*claimsWithScopes).Scopes, nil
1919
}
2020

21+
type conjunctionKind int
22+
23+
const (
24+
AND conjunctionKind = iota
25+
OR
26+
)
27+
2128
func TestWithScopesCustom(t *testing.T) {
2229
for k, tc := range []struct {
2330
name string
2431
jwtopts *JwtMiddlewareOpts
25-
c ConjunctionKind
32+
c conjunctionKind
2633
r []string
2734
jwt string
2835
shouldBlock bool
@@ -106,7 +113,14 @@ func TestWithScopesCustom(t *testing.T) {
106113
}
107114

108115
jmw := NewJWT(tc.jwtopts)
109-
smw := WithScopesCustom(tc.r, WithClaimsFromToken(claimsFromTok), WithConjunction(tc.c))
116+
var scf ScopesCheckerFunc
117+
if tc.c == AND {
118+
scf = scopesCheckerAND
119+
} else {
120+
scf = ScopesCheckerOR
121+
}
122+
123+
smw := WithScopesCustom(tc.r, WithClaimsFromToken(claimsFromTok), WithScopesChecker(scf))
110124
visited := false
111125
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
112126
visited = true

0 commit comments

Comments
 (0)