From 5361e3c65559e3a46eb3e40f731e4f13bc644746 Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Sun, 7 Jul 2024 07:58:26 -0700 Subject: [PATCH] Attempting middleware but unsure. Made new funcs to check jwt --- internal/handlers/handler.go | 43 +++------------------- internal/handlers/middleware.go | 64 +++++++++++++++++++++++++++++++++ internal/handlers/util.go | 55 ++++++++++++++++++++++++---- 3 files changed, 118 insertions(+), 44 deletions(-) create mode 100644 internal/handlers/middleware.go diff --git a/internal/handlers/handler.go b/internal/handlers/handler.go index 9ef3e35..8015ba8 100644 --- a/internal/handlers/handler.go +++ b/internal/handlers/handler.go @@ -49,6 +49,7 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient. router.Pre(middleware.Logger()) router.Pre(middleware.Recover()) router.Use(middleware.Static("/internal/static")) + //router.Use(RefreshJwtMiddleware(apiClient)) router.GET("/", s.HomeIndex) router.GET("/about", s.HomeAbout) @@ -57,57 +58,23 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient. debug.GET("/cookies", s.DebugCookies) articles := router.Group("/articles") + //articles.Use(ValidateJwtMiddleware(configs.JwtSecret)) articles.GET("", s.ArticlesList) sources := router.Group("/sources") + //sources.Use(ValidateJwtMiddleware(configs.JwtSecret)) sources.GET("", s.ListAllSources) + sources.GET("/add", s.AddSource) users := router.Group("/users") users.GET("/login", s.UserLogin) users.POST("/login", s.UserAfterLogin) users.GET("/signup", s.UserSignUp) users.POST("/signup", s.UserAfterSignUp) + users.Use(ValidateJwtMiddleware(configs.JwtSecret)) users.GET("/logout", s.UsersLogout) users.GET("/profile", s.UserProfile) s.Router = router return s } - -// If the token is not valid then an json error will be returned. -// If the token has the wrong scope, a json error will be returned. -// If the token passes all the checks, it is valid and is returned back to the caller. -//func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtToken, error) { -// token, err := s.getJwtTokenFromContext(c) -// if err != nil { -// s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) -// } -// -// err = token.hasExpired() -// if err != nil { -// return JwtToken{}, errors.New(ErrJwtExpired) -// //s.WriteMessage(c, ErrJwtExpired, http.StatusUnauthorized) -// } -// -// err = token.hasScope(requiredScope) -// if err != nil { -// return JwtToken{}, errors.New(ErrJwtScopeMissing) -// //s.WriteMessage(c, ErrJwtScopeMissing, http.StatusUnauthorized) -// } -// -// if token.Iss != s.config.ServerAddress { -// return JwtToken{}, errors.New(ErrJwtInvalidIssuer) -// //s.WriteMessage(c, ErrJwtInvalidIssuer, http.StatusUnauthorized) -// } -// -// return token, nil -//} - -//func (s *Handler) GetUserIdFromJwtToken(c echo.Context) int64 { -// token, err := s.getJwtTokenFromContext(c) -// if err != nil { -// s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) -// } -// -// return token.GetUserId() -//} diff --git a/internal/handlers/middleware.go b/internal/handlers/middleware.go new file mode 100644 index 0000000..942974e --- /dev/null +++ b/internal/handlers/middleware.go @@ -0,0 +1,64 @@ +package handlers + +import ( + "time" + + "git.jamestombleson.com/jtom38/newsbot-portal/apiclient" + "git.jamestombleson.com/jtom38/newsbot-portal/internal/domain" + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" +) + +func ValidateJwtMiddleware(jwtSecret string) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + cookie, err := c.Cookie(domain.CookieToken) + if err != nil { + return err + } + + if cookie.Value == "" { + return echo.NewHTTPError(401, "Authorization token is missing.") + } + + token, err := jwt.ParseWithClaims(cookie.Value, &jwtToken{}, func(token *jwt.Token) (interface{}, error) { + return []byte(jwtSecret), nil + }) + if err != nil { + return err + } + + if !token.Valid { + return echo.NewHTTPError(401, "Invalid authorization token.") + //return errors.New("invalid jwt token") + } + + claims := token.Claims.(*jwtToken) + if !claims.Exp.After(time.Now()) { + return echo.NewHTTPError(401, "Your Authorization token has expired.") + //return errors.New("the jwt token has expired") + } + //if claims.Iss != issuer { + // return jwtToken{}, errors.New("the issuer was invalid") + //} + + return next(c) + } + } +} + +func RefreshJwtMiddleware(api apiclient.ApiClient) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + resp, err := api.Users.RefreshJwtTokenFromContext(c) + if err != nil { + return next(c) + } + + SetCookie(c, domain.CookieToken, resp.Token, "/") + SetCookie(c, domain.CookieRefreshToken, resp.RefreshToken, "/") + + return next(c) + } + } +} \ No newline at end of file diff --git a/internal/handlers/util.go b/internal/handlers/util.go index 0eaea40..3309c6e 100644 --- a/internal/handlers/util.go +++ b/internal/handlers/util.go @@ -4,9 +4,12 @@ import ( "context" "errors" "net/http" + "strings" "time" + "git.jamestombleson.com/jtom38/newsbot-portal/internal/config" "git.jamestombleson.com/jtom38/newsbot-portal/internal/domain" + "git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout" "github.com/a-h/templ" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo/v4" @@ -33,7 +36,31 @@ type jwtToken struct { jwt.RegisteredClaims } -func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error) { +func IsLoggedIn(ctx echo.Context) error { + _, err := GetUserInfo(ctx) + if err != nil { + return err + } + + return nil +} + +func HasValidScope(ctx echo.Context, scope string) error { + token, err := GetUserInfo(ctx) + if err != nil { + return err + } + + userScopes := strings.Join(token.Scopes, ",") + if strings.Contains(userScopes, scope) { + return nil + } + + return errors.New("required permission is missing") +} + +func GetUserInfo(ctx echo.Context) (jwtToken, error) { + cfg := config.New() cookie, err := ctx.Cookie(domain.CookieToken) if err != nil { return jwtToken{}, err @@ -44,7 +71,7 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error } token, err := jwt.ParseWithClaims(cookie.Value, &jwtToken{}, func(token *jwt.Token) (interface{}, error) { - return []byte(sharedSecret), nil + return []byte(cfg.JwtSecret), nil }) if err != nil { return jwtToken{}, err @@ -58,9 +85,6 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error if !claims.Exp.After(time.Now()) { return jwtToken{}, errors.New("the jwt token has expired") } - //if claims.Iss != issuer { - // return jwtToken{}, errors.New("the issuer was invalid") - //} return *claims, nil } @@ -79,7 +103,26 @@ func Render(ctx echo.Context, statusCode int, t templ.Component) error { // take the request context and make it a var request := ctx.Request().Context() - + + //Check to see if we the echo context has the cookie we are looking for, if so, create a new context based on what we had and add the value + username, err := ctx.Cookie(domain.CookieUser) + if err == nil { + request = context.WithValue(request, domain.UserNameContext, username.Value) + } else { + request = context.WithValue(request, domain.UserNameContext, "") + } + + return t.Render(request, ctx.Response().Writer) +} + +func RenderError(ctx echo.Context, err error) error { + var t templ.Component = layout.Error(err) + ctx.Response().Writer.WriteHeader(200) + ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML) + + // take the request context and make it a var + request := ctx.Request().Context() + //Check to see if we the echo context has the cookie we are looking for, if so, create a new context based on what we had and add the value username, err := ctx.Cookie(domain.CookieUser) if err == nil {