Skip to content

Commit

Permalink
JWKS JWT validation
Browse files Browse the repository at this point in the history
Grabs the issuer after parsing the token and tries to verify it using
the issuer's well known JWKS.
  • Loading branch information
rubiojr committed Jan 6, 2022
1 parent 2b54126 commit a4c4e1f
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 5 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ require github.com/charmbracelet/charm v0.9.2
replace github.com/charmbracelet/charm v0.9.2 => github.com/rubiojr/charm v0.9.2-0.20211223100512-c561032f2167

require (
github.com/MicahParks/keyfunc v1.0.1
github.com/charmbracelet/keygen v0.1.2
github.com/gin-gonic/gin v1.7.7
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang-jwt/jwt/v4 v4.2.0
github.com/spf13/cobra v1.3.0
github.com/stretchr/testify v1.7.0
)
Expand Down
7 changes: 5 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/MicahParks/keyfunc v1.0.1 h1:BIKfiyELXV0A8SRCHQaByJW0s71xXpArhzfTS3uf26c=
github.com/MicahParks/keyfunc v1.0.1/go.mod h1:R8RZa27qn+5cHTfYLJ9/+7aSb5JIdz7cl0XFo0o4muo=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
Expand Down Expand Up @@ -197,8 +199,9 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down
2 changes: 1 addition & 1 deletion internal/middleware/charmauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strings"

"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"github.com/golang-jwt/jwt/v4"
)

func CharmAuth(charmURL string) gin.HandlerFunc {
Expand Down
74 changes: 74 additions & 0 deletions internal/middleware/jwks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package middleware

import (
"context"
"fmt"
"log"
"net/http"
"net/url"
"time"

"github.com/MicahParks/keyfunc"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)

func JWKS() gin.HandlerFunc {
return func(c *gin.Context) {
auth := c.GetHeader("Authorization")
encodedToken := auth[len("Bearer "):]
p := jwt.Parser{}
t, _, err := p.ParseUnverified(encodedToken, &jwt.RegisteredClaims{})
if err != nil {
log.Printf("failed parsing token: %s", err)
c.AbortWithStatus(http.StatusBadRequest)
return
}

iss := t.Claims.(*jwt.RegisteredClaims).Issuer
jwksURL, err := url.Parse(iss + "/.well-known/jwks.json")
if err != nil {
log.Printf("invalid issuer URL: %s", err)
c.AbortWithStatus(http.StatusBadRequest)
return
}

err = validateToken(encodedToken, jwksURL.String())
if err != nil {
log.Printf("could not validate token: %s", err)
c.AbortWithStatus(http.StatusUnauthorized)
}
}
}

func validateToken(encToken string, jwksURL string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
options := keyfunc.Options{
Ctx: ctx,
RefreshErrorHandler: func(err error) {
log.Printf("refreshing JWKS failed: %s", err)
},
RefreshInterval: time.Hour,
RefreshRateLimit: time.Minute * 5,
RefreshTimeout: time.Second * 10,
RefreshUnknownKID: true,
}

jwks, err := keyfunc.Get(jwksURL, options)
if err != nil {
return err
}

var token *jwt.Token
if token, err = jwt.Parse(encToken, jwks.Keyfunc); err != nil {
return err
}

if !token.Valid {
return fmt.Errorf("invalid jwt token")
}
log.Println("The token is valid.")

return nil
}
3 changes: 2 additions & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ func (s *Server) Serve(ctx context.Context) error {
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
uploads := router.Group(UploadRoute)
uploads.Use(middleware.CharmAuth(s.config.CharmServerURL))
//uploads.Use(middleware.CharmAuth(s.config.CharmServerURL))
uploads.Use(middleware.JWKS())
uploads.POST("/", middleware.Uploads(s.config.UploadsPath, 32<<20))
router.StaticFS("/", http.Dir(s.config.UploadsPath))
log.Printf("serving on: %s", s.config.Addr)
Expand Down

0 comments on commit a4c4e1f

Please sign in to comment.