Skip to content

Commit 2bcdd88

Browse files
committed
Authenticator: skip if matched route doesn't have MetaAuth to true
1 parent c99bb93 commit 2bcdd88

File tree

4 files changed

+69
-0
lines changed

4 files changed

+69
-0
lines changed

auth/authenticator.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import (
1212
"goyave.dev/goyave/v5"
1313
)
1414

15+
// MetaAuth the authentication middleware will only authenticate the user
16+
// if this meta is present in the matched route or any of its parent and is equal to `true`.
17+
const MetaAuth = "goyave.require-auth"
18+
1519
// Column matches a column name with a struct field.
1620
type Column struct {
1721
Field *reflect.StructField
@@ -50,8 +54,15 @@ type Handler[T any] struct {
5054
// executing the authenticator. Blocks if the authentication is not successful.
5155
// If the authenticator implements `Unauthorizer`, `OnUnauthorized` is called,
5256
// otherwise returns a default `401 Unauthorized` error.
57+
// If the matched route doesn't contain the `MetaAuth` or if it's not equal to `true`,
58+
// the middleware is skipped.
5359
func (m *Handler[T]) Handle(next goyave.Handler) goyave.Handler {
5460
return func(response *goyave.Response, request *goyave.Request) {
61+
if requireAuth, ok := request.Route.LookupMeta(MetaAuth); !ok || requireAuth != true {
62+
next(response, request)
63+
return
64+
}
65+
5566
user := new(T)
5667
if err := m.Authenticator.Authenticate(request, user); err != nil {
5768
if unauthorizer, ok := m.Authenticator.(Unauthorizer); ok {
@@ -68,6 +79,10 @@ func (m *Handler[T]) Handle(next goyave.Handler) goyave.Handler {
6879

6980
// Middleware returns an authentication middleware which will use the given
7081
// authenticator and set the request's user according to the given model.
82+
// This middleware should be used as a global middleware, and all routes (ou routers)
83+
// that require authentication should have the meta `MetaAuth` set to `true`.
84+
// If the matched route or any of its parent doesn't have this meta or if it's not equal to
85+
// `true`, the authentication is skipped.
7186
func Middleware[T any](authenticator Authenticator) *Handler[T] {
7287
return &Handler[T]{
7388
Authenticator: authenticator,

auth/authenticator_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ func TestAuthenticator(t *testing.T) {
8888

8989
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
9090
request.Request().SetBasicAuth(user.Email, "secret")
91+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
9192
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
9293
assert.Equal(t, user.ID, request.User.(*TestUser).ID)
9394
assert.Equal(t, user.Name, request.User.(*TestUser).Name)
@@ -99,6 +100,7 @@ func TestAuthenticator(t *testing.T) {
99100

100101
request = server.NewTestRequest(http.MethodGet, "/protected", nil)
101102
request.Request().SetBasicAuth(user.Email, "incorrect password")
103+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
102104
resp = server.TestMiddleware(authenticator, request, func(response *goyave.Response, _ *goyave.Request) {
103105
response.Status(http.StatusOK)
104106
})
@@ -111,6 +113,31 @@ func TestAuthenticator(t *testing.T) {
111113
assert.Equal(t, map[string]string{"error": server.Lang.GetDefault().Get("auth.invalid-credentials")}, body)
112114
})
113115

116+
t.Run("NoAuth", func(t *testing.T) {
117+
server, user := prepareAuthenticatorTest(t)
118+
t.Cleanup(func() { server.CloseDB() })
119+
120+
authenticator := Middleware[*TestUser](&BasicAuthenticator{})
121+
122+
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
123+
request.Request().SetBasicAuth(user.Email, "secret")
124+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: false}}
125+
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
126+
assert.Nil(t, request.User)
127+
response.Status(http.StatusOK)
128+
})
129+
assert.Equal(t, http.StatusOK, resp.StatusCode)
130+
_ = resp.Body.Close()
131+
132+
request.Route = &goyave.Route{Meta: map[string]any{}}
133+
resp = server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
134+
assert.Nil(t, request.User)
135+
response.Status(http.StatusOK)
136+
})
137+
assert.Equal(t, http.StatusOK, resp.StatusCode)
138+
_ = resp.Body.Close()
139+
})
140+
114141
t.Run("MiddlewareUnauthorizer", func(t *testing.T) {
115142
server, user := prepareAuthenticatorTest(t)
116143
t.Cleanup(func() { server.CloseDB() })
@@ -119,6 +146,7 @@ func TestAuthenticator(t *testing.T) {
119146

120147
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
121148
request.Request().SetBasicAuth(user.Email, "incorrect password")
149+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
122150
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
123151
response.Status(http.StatusOK)
124152
})

auth/basic_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func TestBasicAuthenticator(t *testing.T) {
1818

1919
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
2020
request.Request().SetBasicAuth(user.Email, "secret")
21+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
2122
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
2223
assert.Equal(t, user.ID, request.User.(*TestUser).ID)
2324
assert.Equal(t, user.Name, request.User.(*TestUser).Name)
@@ -34,6 +35,7 @@ func TestBasicAuthenticator(t *testing.T) {
3435

3536
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
3637
request.Request().SetBasicAuth(user.Email, "wrong password")
38+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
3739
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
3840
assert.Fail(t, "middleware passed despite failed authentication")
3941
response.Status(http.StatusOK)
@@ -53,6 +55,7 @@ func TestBasicAuthenticator(t *testing.T) {
5355

5456
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
5557
request.Request().SetBasicAuth(user.Email, "secret")
58+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
5659
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
5760
assert.Equal(t, user.ID, request.User.(*TestUser).ID)
5861
assert.Equal(t, user.Name, request.User.(*TestUser).Name)
@@ -69,6 +72,7 @@ func TestBasicAuthenticator(t *testing.T) {
6972

7073
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
7174
request.Request().SetBasicAuth(user.Email, "wrong password")
75+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
7276
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
7377
assert.Fail(t, "middleware passed despite failed authentication")
7478
response.Status(http.StatusOK)
@@ -87,6 +91,7 @@ func TestBasicAuthenticator(t *testing.T) {
8791
authenticator := Middleware[*TestUser](&BasicAuthenticator{Optional: true})
8892

8993
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
94+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
9095
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
9196
assert.Nil(t, request.User)
9297
response.Status(http.StatusOK)
@@ -107,6 +112,7 @@ func TestBasicAuthenticator(t *testing.T) {
107112
authenticator.Init(server.Server)
108113
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
109114
request.Request().SetBasicAuth("johndoe", "secret")
115+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
110116

111117
// Panic here because table doesn't exist
112118
user := &TestUserPromoted{}
@@ -124,6 +130,7 @@ func TestBasicAuthenticator(t *testing.T) {
124130
authenticator := Middleware[*TestUser](&BasicAuthenticator{})
125131
authenticator.Init(server.Server)
126132
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
133+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
127134

128135
err := authenticator.Authenticate(request, &TestUserPromoted{})
129136
assert.Error(t, err)
@@ -140,6 +147,7 @@ func TestConfigBasicAuthenticator(t *testing.T) {
140147
server := testutil.NewTestServerWithOptions(t, goyave.Options{Config: cfg})
141148
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
142149
request.Request().SetBasicAuth("johndoe", "secret")
150+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
143151
resp := server.TestMiddleware(ConfigBasicAuth(), request, func(response *goyave.Response, request *goyave.Request) {
144152
assert.Equal(t, "johndoe", request.User.(*BasicUser).Name)
145153
response.Status(http.StatusOK)
@@ -155,6 +163,7 @@ func TestConfigBasicAuthenticator(t *testing.T) {
155163
server := testutil.NewTestServerWithOptions(t, goyave.Options{Config: cfg})
156164
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
157165
request.Request().SetBasicAuth("johndoe", "wrong_password")
166+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
158167
resp := server.TestMiddleware(ConfigBasicAuth(), request, func(response *goyave.Response, request *goyave.Request) {
159168
assert.Fail(t, "middleware passed despite failed authentication")
160169
response.Status(http.StatusOK)
@@ -174,6 +183,7 @@ func TestConfigBasicAuthenticator(t *testing.T) {
174183
cfg.Set("auth.basic.password", "secret")
175184
server := testutil.NewTestServerWithOptions(t, goyave.Options{Config: cfg})
176185
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
186+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
177187
resp := server.TestMiddleware(ConfigBasicAuth(), request, func(response *goyave.Response, request *goyave.Request) {
178188
assert.Fail(t, "middleware passed despite failed authentication")
179189
response.Status(http.StatusOK)

auth/jwt_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ func TestJWTAuthenticator(t *testing.T) {
187187

188188
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
189189
request.Request().Header.Set("Authorization", "Bearer "+token)
190+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
190191
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
191192
assert.Equal(t, user.ID, request.User.(*TestUser).ID)
192193
assert.Equal(t, user.Name, request.User.(*TestUser).Name)
@@ -215,6 +216,7 @@ func TestJWTAuthenticator(t *testing.T) {
215216

216217
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
217218
request.Request().Header.Set("Authorization", "Bearer "+token)
219+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
218220
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
219221
assert.Equal(t, user.ID, request.User.(*TestUser).ID)
220222
assert.Equal(t, user.Name, request.User.(*TestUser).Name)
@@ -243,6 +245,7 @@ func TestJWTAuthenticator(t *testing.T) {
243245

244246
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
245247
request.Request().Header.Set("Authorization", "Bearer "+token)
248+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
246249
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
247250
assert.Equal(t, user.ID, request.User.(*TestUser).ID)
248251
assert.Equal(t, user.Name, request.User.(*TestUser).Name)
@@ -261,6 +264,7 @@ func TestJWTAuthenticator(t *testing.T) {
261264

262265
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
263266
request.Request().Header.Set("Authorization", "Bearer invalidtoken")
267+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
264268
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
265269
assert.Nil(t, request.User)
266270
assert.Fail(t, "middleware passed despite failed authentication")
@@ -293,6 +297,7 @@ func TestJWTAuthenticator(t *testing.T) {
293297

294298
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
295299
request.Request().Header.Set("Authorization", "Bearer "+token)
300+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
296301
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
297302
assert.Nil(t, request.User)
298303
assert.Fail(t, "middleware passed despite failed authentication")
@@ -325,6 +330,7 @@ func TestJWTAuthenticator(t *testing.T) {
325330

326331
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
327332
request.Request().Header.Set("Authorization", "Bearer "+token)
333+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
328334
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
329335
assert.Nil(t, request.User)
330336
assert.Fail(t, "middleware passed despite failed authentication")
@@ -354,6 +360,7 @@ func TestJWTAuthenticator(t *testing.T) {
354360

355361
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
356362
request.Request().Header.Set("Authorization", "Bearer "+token)
363+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
357364
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
358365
assert.Nil(t, request.User)
359366
assert.Fail(t, "middleware passed despite failed authentication")
@@ -389,6 +396,7 @@ func TestJWTAuthenticator(t *testing.T) {
389396
authenticator.Init(server.Server)
390397
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
391398
request.Request().Header.Set("Authorization", "Bearer "+token)
399+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
392400
assert.Panics(t, func() {
393401
user := &TestUserPromoted{}
394402
_ = authenticator.Authenticate(request, &user)
@@ -411,6 +419,7 @@ func TestJWTAuthenticator(t *testing.T) {
411419

412420
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
413421
request.Request().Header.Set("Authorization", "Bearer "+token)
422+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
414423
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
415424
assert.Nil(t, request.User)
416425
assert.Fail(t, "middleware passed despite failed authentication")
@@ -439,6 +448,7 @@ func TestJWTAuthenticator(t *testing.T) {
439448

440449
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
441450
request.Request().Header.Set("Authorization", "Bearer "+token)
451+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
442452
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
443453
assert.Nil(t, request.User)
444454
assert.Fail(t, "middleware passed despite failed authentication")
@@ -467,6 +477,7 @@ func TestJWTAuthenticator(t *testing.T) {
467477

468478
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
469479
request.Request().Header.Set("Authorization", "Bearer "+token)
480+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
470481
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
471482
assert.Nil(t, request.User)
472483
assert.Fail(t, "middleware passed despite failed authentication")
@@ -495,6 +506,7 @@ func TestJWTAuthenticator(t *testing.T) {
495506

496507
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
497508
request.Request().Header.Set("Authorization", "Bearer "+token)
509+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
498510
assert.Panics(t, func() {
499511
user := &TestUser{}
500512
_ = authenticator.Authenticate(request, &user)
@@ -507,6 +519,7 @@ func TestJWTAuthenticator(t *testing.T) {
507519
authenticator := Middleware[*TestUser](&JWTAuthenticator{})
508520

509521
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
522+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
510523
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
511524
assert.Nil(t, request.User)
512525
assert.Fail(t, "middleware passed despite failed authentication")
@@ -536,6 +549,7 @@ func TestJWTAuthenticator(t *testing.T) {
536549

537550
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
538551
request.Request().Header.Set("Authorization", "Bearer "+token)
552+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
539553
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
540554
assert.Equal(t, user.ID, request.User.(*TestUser).ID)
541555
assert.Equal(t, user.Name, request.User.(*TestUser).Name)
@@ -553,6 +567,7 @@ func TestJWTAuthenticator(t *testing.T) {
553567

554568
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
555569
request.Request().Header.Set("Authorization", "Bearer invalidtoken")
570+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
556571
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
557572
assert.Nil(t, request.User)
558573
assert.Fail(t, "middleware passed despite failed authentication")
@@ -573,6 +588,7 @@ func TestJWTAuthenticator(t *testing.T) {
573588
authenticator := Middleware[*TestUser](&JWTAuthenticator{Optional: true})
574589

575590
request := server.NewTestRequest(http.MethodGet, "/protected", nil)
591+
request.Route = &goyave.Route{Meta: map[string]any{MetaAuth: true}}
576592
resp := server.TestMiddleware(authenticator, request, func(response *goyave.Response, request *goyave.Request) {
577593
assert.Nil(t, request.User)
578594
response.Status(http.StatusOK)

0 commit comments

Comments
 (0)