From bbfcac82043b7e1dbf0906cf1bf15e3ec25cf34c Mon Sep 17 00:00:00 2001 From: Alan Graham Date: Sun, 29 Sep 2024 07:34:20 +0000 Subject: [PATCH] add support to verify tokens using public key --- .gitignore | 3 +++ cmd/nettica-api/main.go | 31 ++++++++++++++++++++++++++ util/util.go | 49 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/.gitignore b/.gitignore index caaea7d..2cee7c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # go environment .env +# pem files +*.pem + # Vendor vendor/* diff --git a/cmd/nettica-api/main.go b/cmd/nettica-api/main.go index 9495f48..57a13dc 100644 --- a/cmd/nettica-api/main.go +++ b/cmd/nettica-api/main.go @@ -136,6 +136,37 @@ func main() { c.Set("oauth2Token", oauth2Token) c.Next() return + } else if token != "" { + id_token := c.Request.Header.Get("X-OAUTH2-ID-TOKEN") + if id_token != "" { + new_token := &oauth2.Token{ + AccessToken: token, + TokenType: "Bearer", + RefreshToken: "", + Expiry: time.Now().Add(time.Hour * 24), + } + m := make(map[string]interface{}) + m["id_token"] = id_token + new_token = new_token.WithExtra(m) + + // check if token is valid + oauth2Token, err := util.ValidateToken(new_token.AccessToken) + if err != nil { + log.WithFields(log.Fields{ + "err": err, + "token": oauth2Token, + }).Error("failed to get token info") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + // cache token + cacheDb.Set(token, new_token, 4*time.Hour) + + // will be accessible in auth endpoints + c.Set("oauth2Token", new_token) + c.Next() + } } // avoid 401 page for refresh after logout diff --git a/util/util.go b/util/util.go index 6ec2392..ce0f629 100644 --- a/util/util.go +++ b/util/util.go @@ -9,8 +9,11 @@ import ( "os" "regexp" "strings" + "time" "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt" + "golang.org/x/oauth2" ) var ( @@ -259,3 +262,49 @@ func RandomString(n int) (string, error) { return string(ret), nil } + +// ValidateToken validates a token + +func ValidateToken(token string) (*oauth2.Token, error) { + // validate the JWT with our private key + + // verify the jwt signature + + // parse the token + claims := jwt.MapClaims{} + parsedToken, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) { + bytes, err := os.ReadFile(os.Getenv("OAUTH2_PUBLIC_KEY_PEM")) + if err != nil { + return nil, err + } + return jwt.ParseRSAPublicKeyFromPEM(bytes) + }) + + if err != nil { + return nil, err + } + + // validate the claims + if parsedToken.Valid { + + //if claims["email"] == nil || claims["email"] == "" { + // return nil, errors.New("email is required") + //} + + // create a new oauth2.Token from the claims + oauth2Token := &oauth2.Token{ + AccessToken: token, + TokenType: "Bearer", + RefreshToken: "", + Expiry: time.Now().Add(4 * time.Hour), + } + + //oauth2Token = oauth2Token.WithExtra(map[string]interface{}{ // Add the ID token to the extra parameters + // "id_token": token}) + + return oauth2Token, nil + + } + + return nil, errors.New("invalid token") +}