Skip to content

Commit bd4e710

Browse files
committed
implement basic role-based access control with auth0
1 parent 5c76f4b commit bd4e710

4 files changed

Lines changed: 58 additions & 6 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# Standard Library/Golang: API Basic Access Control Code Sample
1+
# Standard Library/Golang: API Basic Role-Based Access Control (RBAC) Code Sample
22

3-
This Golang code sample demonstrates **how to implement authorization** in Standard Library API servers using Auth0.
3+
This Golang code sample demonstrates **how to implement Role-Based Access Control (RBAC)** in Standard Library API servers using Auth0.
44

55
This code sample is part of the ["Auth0 Developer Resources"](https://developer.auth0.com/resources), a place where you can explore the authentication and authorization features of the Auth0 Identity Platform.
66

7-
Visit the ["Standard Library/Golang Code Sample: Authorization For Basic APIs"](https://developer.auth0.com/resources/code-samples/api/standard-library/basic-authorization) page for instructions on how to configure and run this code sample and how to integrate it with a Single-Page Application (SPA) of your choice.
7+
Visit the ["Standard Library/Golang Code Sample: Role-Based Access Control For Basic APIs"](https://developer.auth0.com/resources/code-samples/api/standard-library/basic-role-based-access-control) page for instructions on how to configure and run this code sample and how to integrate it with a Single-Page Application (SPA) of your choice.
88

99
## Why Use Auth0?
1010

pkg/helpers/helpers.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ import (
77
"os"
88
)
99

10+
func Contains(s []string, e string) bool {
11+
for _, a := range s {
12+
if a == e {
13+
return true
14+
}
15+
}
16+
return false
17+
}
18+
1019
func SafeGetEnv(key string) string {
1120
if os.Getenv(key) == "" {
1221
log.Fatalf("The environment variable '%s' doesn't exist or is not set", key)

pkg/middleware/auth.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package middleware
22

33
import (
4+
"context"
45
"log"
56
"net/http"
67
"net/url"
@@ -16,10 +17,46 @@ import (
1617
)
1718

1819
const (
19-
missingJWTErrorMessage = "Requires authentication"
20-
invalidJWTErrorMessage = "Bad credentials"
20+
missingJWTErrorMessage = "Requires authentication"
21+
invalidJWTErrorMessage = "Bad credentials"
22+
permissionDeniedErrorMessage = "Permission denied"
2123
)
2224

25+
type CustomClaims struct {
26+
Permissions []string `json:"permissions"`
27+
}
28+
29+
func (c CustomClaims) Validate(ctx context.Context) error {
30+
return nil
31+
}
32+
33+
func (c CustomClaims) HasPermissions(expectedClaims []string) bool {
34+
if len(expectedClaims) == 0 {
35+
return false
36+
}
37+
for _, scope := range expectedClaims {
38+
if !helpers.Contains(c.Permissions, scope) {
39+
return false
40+
}
41+
}
42+
return true
43+
}
44+
45+
func ValidatePermissions(expectedClaims []string, next http.Handler) http.Handler {
46+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
47+
token := r.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)
48+
claims := token.CustomClaims.(*CustomClaims)
49+
if !claims.HasPermissions(expectedClaims) {
50+
errorMessage := ErrorMessage{Message: permissionDeniedErrorMessage}
51+
if err := helpers.WriteJSON(w, http.StatusForbidden, errorMessage); err != nil {
52+
log.Printf("Failed to write error message: %v", err)
53+
}
54+
return
55+
}
56+
next.ServeHTTP(w, r)
57+
})
58+
}
59+
2360
func ValidateJWT(audience, domain string, next http.Handler) http.Handler {
2461
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2562
issuerURL, err := url.Parse("https://" + domain + "/")
@@ -34,6 +71,9 @@ func ValidateJWT(audience, domain string, next http.Handler) http.Handler {
3471
validator.RS256,
3572
issuerURL.String(),
3673
[]string{audience},
74+
validator.WithCustomClaims(func() validator.CustomClaims {
75+
return new(CustomClaims)
76+
}),
3777
)
3878
if err != nil {
3979
log.Fatalf("Failed to set up the jwt validator")

pkg/router/router.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ func Router(audience, domain string) http.Handler {
1313
router.HandleFunc("/", middleware.NotFoundHandler)
1414
router.HandleFunc("/api/messages/public", middleware.PublicApiHandler)
1515
router.Handle("/api/messages/protected", middleware.ValidateJWT(audience, domain, http.HandlerFunc(middleware.ProtectedApiHandler)))
16-
router.Handle("/api/messages/admin", middleware.ValidateJWT(audience, domain, http.HandlerFunc(middleware.AdminApiHandler)))
16+
router.Handle("/api/messages/admin",
17+
middleware.ValidateJWT(audience, domain,
18+
middleware.ValidatePermissions([]string{"read:admin-messages"},
19+
http.HandlerFunc(middleware.AdminApiHandler))))
1720

1821
return middleware.HandleCacheControl(router)
1922
}

0 commit comments

Comments
 (0)