features/token-things #2
@ -49,6 +49,7 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient.
|
|||||||
router.Pre(middleware.Logger())
|
router.Pre(middleware.Logger())
|
||||||
router.Pre(middleware.Recover())
|
router.Pre(middleware.Recover())
|
||||||
router.Use(middleware.Static("/internal/static"))
|
router.Use(middleware.Static("/internal/static"))
|
||||||
|
//router.Use(RefreshJwtMiddleware(apiClient))
|
||||||
|
|
||||||
router.GET("/", s.HomeIndex)
|
router.GET("/", s.HomeIndex)
|
||||||
router.GET("/about", s.HomeAbout)
|
router.GET("/about", s.HomeAbout)
|
||||||
@ -57,57 +58,23 @@ func NewServer(ctx context.Context, configs config.Configs, apiClient apiclient.
|
|||||||
debug.GET("/cookies", s.DebugCookies)
|
debug.GET("/cookies", s.DebugCookies)
|
||||||
|
|
||||||
articles := router.Group("/articles")
|
articles := router.Group("/articles")
|
||||||
|
//articles.Use(ValidateJwtMiddleware(configs.JwtSecret))
|
||||||
articles.GET("", s.ArticlesList)
|
articles.GET("", s.ArticlesList)
|
||||||
|
|
||||||
sources := router.Group("/sources")
|
sources := router.Group("/sources")
|
||||||
|
//sources.Use(ValidateJwtMiddleware(configs.JwtSecret))
|
||||||
sources.GET("", s.ListAllSources)
|
sources.GET("", s.ListAllSources)
|
||||||
|
sources.GET("/add", s.AddSource)
|
||||||
|
|
||||||
users := router.Group("/users")
|
users := router.Group("/users")
|
||||||
users.GET("/login", s.UserLogin)
|
users.GET("/login", s.UserLogin)
|
||||||
users.POST("/login", s.UserAfterLogin)
|
users.POST("/login", s.UserAfterLogin)
|
||||||
users.GET("/signup", s.UserSignUp)
|
users.GET("/signup", s.UserSignUp)
|
||||||
users.POST("/signup", s.UserAfterSignUp)
|
users.POST("/signup", s.UserAfterSignUp)
|
||||||
|
users.Use(ValidateJwtMiddleware(configs.JwtSecret))
|
||||||
users.GET("/logout", s.UsersLogout)
|
users.GET("/logout", s.UsersLogout)
|
||||||
users.GET("/profile", s.UserProfile)
|
users.GET("/profile", s.UserProfile)
|
||||||
|
|
||||||
s.Router = router
|
s.Router = router
|
||||||
return s
|
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()
|
|
||||||
//}
|
|
||||||
|
64
internal/handlers/middleware.go
Normal file
64
internal/handlers/middleware.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.jamestombleson.com/jtom38/newsbot-portal/internal/config"
|
||||||
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
"git.jamestombleson.com/jtom38/newsbot-portal/internal/domain"
|
||||||
|
"git.jamestombleson.com/jtom38/newsbot-portal/internal/views/layout"
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@ -33,7 +36,31 @@ type jwtToken struct {
|
|||||||
jwt.RegisteredClaims
|
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)
|
cookie, err := ctx.Cookie(domain.CookieToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return jwtToken{}, err
|
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) {
|
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 {
|
if err != nil {
|
||||||
return jwtToken{}, err
|
return jwtToken{}, err
|
||||||
@ -58,9 +85,6 @@ func ValidateJwt(ctx echo.Context, sharedSecret, issuer string) (jwtToken, error
|
|||||||
if !claims.Exp.After(time.Now()) {
|
if !claims.Exp.After(time.Now()) {
|
||||||
return jwtToken{}, errors.New("the jwt token has expired")
|
return jwtToken{}, errors.New("the jwt token has expired")
|
||||||
}
|
}
|
||||||
//if claims.Iss != issuer {
|
|
||||||
// return jwtToken{}, errors.New("the issuer was invalid")
|
|
||||||
//}
|
|
||||||
|
|
||||||
return *claims, nil
|
return *claims, nil
|
||||||
}
|
}
|
||||||
@ -90,3 +114,22 @@ func Render(ctx echo.Context, statusCode int, t templ.Component) error {
|
|||||||
|
|
||||||
return t.Render(request, ctx.Response().Writer)
|
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 {
|
||||||
|
request = context.WithValue(request, domain.UserNameContext, username.Value)
|
||||||
|
} else {
|
||||||
|
request = context.WithValue(request, domain.UserNameContext, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.Render(request, ctx.Response().Writer)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user