summaryrefslogtreecommitdiff
path: root/cmd/api
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2022-05-13 14:45:14 -0600
committermo khan <mo@mokhan.ca>2022-05-13 14:45:14 -0600
commitd26df9af95676edbc15518aa179fd5f2f389c2e2 (patch)
tree093a856a9b77597f989bcdfec9f0d5ee416c1361 /cmd/api
parent006bec0db7292eb75ac33ca6967af715ea635514 (diff)
squash code into main
Diffstat (limited to 'cmd/api')
-rw-r--r--cmd/api/main.go108
1 files changed, 104 insertions, 4 deletions
diff --git a/cmd/api/main.go b/cmd/api/main.go
index c74f176..d20731e 100644
--- a/cmd/api/main.go
+++ b/cmd/api/main.go
@@ -1,16 +1,116 @@
package main
import (
+ "context"
+ "errors"
"fmt"
"log"
"net/http"
+ "net/url"
+ "os"
+ "strings"
+ "time"
jwtmiddleware "github.com/auth0/go-jwt-middleware/v2"
+ "github.com/auth0/go-jwt-middleware/v2/jwks"
"github.com/auth0/go-jwt-middleware/v2/validator"
"github.com/joho/godotenv"
- "github.com/xlgmokha/api-auth0/pkg/middleware"
)
+type CustomClaims struct {
+ Scope string `json:"scope"`
+}
+
+func (c CustomClaims) Validate(ctx context.Context) error {
+ return nil
+}
+
+func (c CustomClaims) HasScope(expectedScope string) bool {
+ result := strings.Split(c.Scope, " ")
+ for i := range result {
+ if result[i] == expectedScope {
+ return true
+ }
+ }
+ return false
+}
+
+// type TokenExtractor func(r *http.Request) (string, error)
+func Extractor(r *http.Request) (string, error) {
+ authHeader := r.Header.Get("Authorization")
+ fmt.Printf("%v %v\nAuthorization: %v\n", r.Method, r.URL.Path, authHeader)
+ if authHeader == "" {
+ return "", nil
+ }
+
+ authHeaderParts := strings.Fields(authHeader)
+ if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
+ // exchange opaque access token for a JWT access token
+ return "", errors.New("Authorization header format must be Bearer {token}")
+ }
+
+ rawToken := authHeaderParts[1]
+ sections := strings.Split(authHeaderParts[1], ".")
+ if len(sections) != 3 {
+ fmt.Printf("sections: %v\n", len(sections))
+ return "", errors.New("Token is not a JWT")
+ }
+
+ return rawToken, nil
+ return "", nil
+}
+
+func EnsureValidToken() func(next http.Handler) http.Handler {
+ issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute)
+ jwtValidator, err := validator.New(
+ provider.KeyFunc,
+ validator.RS256,
+ issuerURL.String(),
+ []string{os.Getenv("AUTH0_AUDIENCE")},
+ validator.WithCustomClaims(
+ func() validator.CustomClaims {
+ return &CustomClaims{}
+ },
+ ),
+ validator.WithAllowedClockSkew(time.Minute),
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ errorHandler := func(w http.ResponseWriter, r *http.Request, err error) {
+ fmt.Printf("Error: %v\n", err)
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusUnauthorized)
+ w.Write([]byte(`{"message":"Failed to validate JWT."}`))
+ }
+
+ middleware := jwtmiddleware.New(
+ jwtValidator.ValidateToken,
+ jwtmiddleware.WithErrorHandler(errorHandler),
+ jwtmiddleware.WithTokenExtractor(Extractor),
+ )
+
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "OPTIONS" {
+ w.Header().Set("Access-Control-Allow-Credentials", "true")
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+ w.Header().Set("Access-Control-Allow-Headers", "Authorization")
+ w.WriteHeader(http.StatusOK)
+ } else {
+ middleware.CheckJWT(next).ServeHTTP(w, r)
+ }
+ })
+ }
+}
+
func main() {
if err := godotenv.Load(); err != nil {
log.Fatal(err)
@@ -25,7 +125,7 @@ func main() {
w.Write([]byte(`{"message":"public"}`))
}))
- router.Handle("/api/private", middleware.EnsureValidToken()(
+ router.Handle("/api/private", EnsureValidToken()(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("in /api/private handler\n")
w.Header().Set("Access-Control-Allow-Credentials", "true")
@@ -38,7 +138,7 @@ func main() {
}),
))
- router.Handle("/api/private-scoped", middleware.EnsureValidToken()(
+ router.Handle("/api/private-scoped", EnsureValidToken()(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -47,7 +147,7 @@ func main() {
w.Header().Set("Content-Type", "application/json")
token := r.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)
- claims := token.CustomClaims.(*middleware.CustomClaims)
+ claims := token.CustomClaims.(*CustomClaims)
if !claims.HasScope("read:messages") {
w.WriteHeader(http.StatusForbidden)
w.Write([]byte(`{"message":"insufficient scope."}`))