Fixes, refactor, a lot of things happen
This commit is contained in:
@@ -12,7 +12,7 @@ func init() {
|
||||
ErrLoadingEnvs = godotenv.Load()
|
||||
}
|
||||
|
||||
func GetEnv(name, defVal string) string {
|
||||
func GetEnv(name string, defVal string) string { // FIXME defVal and return types
|
||||
env := os.Getenv(name)
|
||||
if env == "" {
|
||||
return defVal
|
||||
|
||||
@@ -6,4 +6,5 @@ type AuthLoginRequest struct {
|
||||
}
|
||||
|
||||
type AuthLoginResponse struct {
|
||||
JWTToken string `json:"jwt_token"`
|
||||
}
|
||||
|
||||
5
src/internal/app/definition/health.go
Normal file
5
src/internal/app/definition/health.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package definition
|
||||
|
||||
type HealthResponse struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
def "git.pbiernat.dev/egommerce/identity-service/internal/app/definition"
|
||||
"git.pbiernat.dev/egommerce/identity-service/internal/app/service"
|
||||
)
|
||||
|
||||
var AuthLoginHandler *Handler
|
||||
|
||||
func init() {
|
||||
AuthLoginHandler = &Handler{
|
||||
Handle: AuthLoginHandlerFunc,
|
||||
Request: &def.AuthLoginRequest{},
|
||||
Response: &def.AuthLoginResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
func AuthLoginHandlerFunc(h *Handler, w http.ResponseWriter) (interface{}, int, error) {
|
||||
var req = h.Request.(*def.AuthLoginRequest)
|
||||
// u := entity.TestUser
|
||||
|
||||
token, err := service.AuthService.Login(req)
|
||||
if err != nil {
|
||||
return nil, http.StatusForbidden, err
|
||||
}
|
||||
|
||||
service.AuthService.SetCookie(w, service.AuthService.TokenCookieName, token)
|
||||
// service.AuthService.SetCookie(w, service.AuthService.RefreshTokenCookieName, refreshTtoken)
|
||||
|
||||
// log.Println("user:", u, "req:", token, "err:", err)
|
||||
|
||||
return nil, http.StatusOK, nil
|
||||
}
|
||||
@@ -1,18 +1 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type NotFoundHandler struct{}
|
||||
|
||||
func (NotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
encodeResponse(w, &response{http.StatusNotFound, ""}, errors.New("Path "+r.RequestURI+" not found"))
|
||||
}
|
||||
|
||||
type MethodNotAllowedHandler struct{}
|
||||
|
||||
func (MethodNotAllowedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
encodeResponse(w, &response{http.StatusMethodNotAllowed, ""}, errors.New("Method Not Allowed: "+r.Method))
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
def "git.pbiernat.dev/egommerce/identity-service/internal/app/definition"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
Addr string
|
||||
DB *pgxpool.Pool
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
*Env
|
||||
Handle HandlerFunc
|
||||
Request interface{}
|
||||
Response interface{}
|
||||
Params Set
|
||||
}
|
||||
|
||||
type HandlerFunc func(h *Handler, w http.ResponseWriter) (interface{}, int, error)
|
||||
|
||||
type Set map[string]string
|
||||
|
||||
type response struct {
|
||||
Status int
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func Init(e *Env, h *Handler) *Handler {
|
||||
// return &Handler{e, h.Handle, h.Request, h.Response, Set{}}
|
||||
h.Env = e
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := decodeRequestData(r, h.Request); err != nil {
|
||||
log.Println("Decode request data error:", err.Error())
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
h.Params = mux.Vars(r)
|
||||
res, code, err := h.Handle(h, w)
|
||||
|
||||
encodeResponse(w, &response{code, res}, err)
|
||||
}
|
||||
|
||||
func decodeRequestData(r *http.Request, v interface{}) error {
|
||||
buf, _ := io.ReadAll(r.Body)
|
||||
rdr := io.NopCloser(bytes.NewReader(buf))
|
||||
r.Body = io.NopCloser(bytes.NewReader(buf))
|
||||
|
||||
json.NewDecoder(rdr).Decode(&v)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeResponse(w http.ResponseWriter, res *response, e error) {
|
||||
if e != nil {
|
||||
encodeError(w, res.Status, e)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(res.Status)
|
||||
if res.Data != nil {
|
||||
json.NewEncoder(w).Encode(res.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeError(w http.ResponseWriter, status int, e error) {
|
||||
w.WriteHeader(status)
|
||||
|
||||
json.NewEncoder(w).Encode(def.Error(e.Error()))
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var HealthCheckHandler *Handler
|
||||
|
||||
func init() {
|
||||
HealthCheckHandler = &Handler{
|
||||
Handle: HealthCheckHandlerFunc,
|
||||
Request: &HealthCheckRequest{},
|
||||
Response: &HealthCheckResponse{},
|
||||
}
|
||||
}
|
||||
|
||||
type HealthCheckRequest struct {
|
||||
}
|
||||
|
||||
type HealthCheckResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data *HealthCheckResponseBody `json:"data"`
|
||||
}
|
||||
|
||||
type HealthCheckResponseBody struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func HealthCheckHandlerFunc(_ *Handler, w http.ResponseWriter) (interface{}, int, error) {
|
||||
return &HealthCheckResponseBody{
|
||||
Message: "This is welcome health message. Everything seems to be alright ;)",
|
||||
Status: "OK",
|
||||
}, http.StatusOK, nil
|
||||
|
||||
// return &HealthCheckResponse{
|
||||
// Status: http.StatusText(http.StatusOK),
|
||||
// Data: &HealthCheckResponseBody{
|
||||
// Message: "This is welcome health message. Everything seems to be alright ;)",
|
||||
// },
|
||||
// }, http.StatusOK, nil
|
||||
}
|
||||
10
src/internal/app/handler/secret.go
Normal file
10
src/internal/app/handler/secret.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func SecretHandler(c *fiber.Ctx) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
package app
|
||||
|
||||
import "log"
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
const AppName = "identity-svc"
|
||||
|
||||
func Panic(v ...any) {
|
||||
log.Panicln(Name+":", v)
|
||||
log.Panicln(AppName+":", v)
|
||||
}
|
||||
|
||||
func Panicf(format string, v ...any) {
|
||||
log.Panicf(Name+": "+format, v...)
|
||||
log.Panicf(AppName+": "+format, v...)
|
||||
}
|
||||
|
||||
func Panicln(v ...any) {
|
||||
v = append([]any{Name + ":"}, v...)
|
||||
v = append([]any{AppName + ":"}, v...)
|
||||
log.Panicln(v...)
|
||||
}
|
||||
|
||||
37
src/internal/app/middleware/jwt.go
Normal file
37
src/internal/app/middleware/jwt.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
jwtMiddleware "github.com/gofiber/jwt/v2"
|
||||
)
|
||||
|
||||
// JWTProtected func for specify routes group with JWT authentication.
|
||||
// See: https://github.com/gofiber/jwt
|
||||
func JWTProtected() func(*fiber.Ctx) error {
|
||||
// Create config for JWT authentication middleware.
|
||||
config := jwtMiddleware.Config{
|
||||
SigningKey: []byte(os.Getenv("JWT_SECRET_KEY")),
|
||||
ContextKey: "jwt", // used in private routes
|
||||
ErrorHandler: jwtError,
|
||||
}
|
||||
|
||||
return jwtMiddleware.New(config)
|
||||
}
|
||||
|
||||
func jwtError(c *fiber.Ctx, err error) error {
|
||||
// Return status 400 Bad Request and failed authentication error.
|
||||
if err.Error() == "Missing or malformed JWT" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"msg": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
// Return status 401 Unauthorized and failed authentication error.
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"error": true,
|
||||
"msg": err.Error(),
|
||||
})
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.pbiernat.dev/egommerce/identity-service/internal/app/handler"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func SetupRouter(env *handler.Env) *mux.Router {
|
||||
r := mux.NewRouter()
|
||||
r.NotFoundHandler = &handler.NotFoundHandler{}
|
||||
r.MethodNotAllowedHandler = &handler.MethodNotAllowedHandler{}
|
||||
|
||||
r.Use(PrepareHeadersMiddleware)
|
||||
r.Use(ValidateJsonBodyMiddleware) // probably not needed
|
||||
r.Use(LoggingMiddleware)
|
||||
|
||||
r.Handle("/health", handler.Init(env, handler.HealthCheckHandler)).Methods(http.MethodGet)
|
||||
r.Handle("/login", handler.Init(env, handler.AuthLoginHandler)).Methods(http.MethodPost)
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
def "git.pbiernat.dev/egommerce/identity-service/internal/app/definition"
|
||||
"git.pbiernat.dev/egommerce/identity-service/internal/app/handler"
|
||||
)
|
||||
|
||||
const Name = "REST API Service"
|
||||
|
||||
type Server struct {
|
||||
*http.Server
|
||||
}
|
||||
|
||||
func NewServer(env *handler.Env) *Server {
|
||||
return &Server{
|
||||
&http.Server{
|
||||
Handler: SetupRouter(env),
|
||||
Addr: env.Addr,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Start() {
|
||||
if os.Getenv("LISTEN_PID") == strconv.Itoa(os.Getpid()) {
|
||||
// systemd run
|
||||
f := os.NewFile(3, "from systemd")
|
||||
l, err := net.FileListener(f)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
log.Println("Server listening on " + l.Addr().String())
|
||||
s.Serve(l)
|
||||
} else {
|
||||
|
||||
log.Println("Server listening on " + s.Addr)
|
||||
log.Fatalln(s.ListenAndServe())
|
||||
}
|
||||
}
|
||||
|
||||
func PrepareHeadersMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Keep-Alive", "timeout=5")
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func ValidateJsonBodyMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
buf, _ := io.ReadAll(r.Body)
|
||||
r.Body = io.NopCloser(bytes.NewReader(buf)) // rollack *Request to original state
|
||||
|
||||
if len(buf) > 0 && !json.Valid(buf) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(def.Error("Unable to parse JSON: " + string(buf)))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func LoggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("Request: " + r.RequestURI + " remote: " + r.RemoteAddr + " via: " + r.UserAgent())
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
22
src/internal/app/server/config.go
Normal file
22
src/internal/app/server/config.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package server
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Config struct {
|
||||
AppID string
|
||||
AppName string
|
||||
AppDomain string
|
||||
NetAddr string
|
||||
Port int
|
||||
LoggerAddr string
|
||||
RegistryAddr string
|
||||
DbURL string
|
||||
MongoDbUrl string
|
||||
EventBusURL string
|
||||
EventBusExchange string
|
||||
EventBusQueue string
|
||||
}
|
||||
|
||||
func (c *Config) GetAppFullName() string {
|
||||
return fmt.Sprintf("%s_%s", c.AppName, c.AppID)
|
||||
}
|
||||
12
src/internal/app/server/health_handler.go
Normal file
12
src/internal/app/server/health_handler.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
def "git.pbiernat.dev/egommerce/identity-service/internal/app/definition"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func (s *Server) HealthHandler(c *fiber.Ctx) error {
|
||||
return c.JSON(&def.HealthResponse{
|
||||
Status: "OK",
|
||||
})
|
||||
}
|
||||
24
src/internal/app/server/login_handler.go
Normal file
24
src/internal/app/server/login_handler.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
def "git.pbiernat.dev/egommerce/identity-service/internal/app/definition"
|
||||
"git.pbiernat.dev/egommerce/identity-service/internal/app/service"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func (s *Server) LoginHandler(c *fiber.Ctx) error {
|
||||
data := new(def.AuthLoginRequest)
|
||||
if err := c.BodyParser(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token, err := service.AuthService.Login(data.Username, data.Password)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusUnauthorized)
|
||||
}
|
||||
|
||||
cookie := service.AuthService.Cookie("auth_token", token)
|
||||
c.Cookie(cookie)
|
||||
|
||||
return c.JSON(&def.AuthLoginResponse{JWTToken: token})
|
||||
}
|
||||
30
src/internal/app/server/router.go
Normal file
30
src/internal/app/server/router.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"git.pbiernat.dev/egommerce/go-api-pkg/fluentd"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func SetupRoutes(s *Server) {
|
||||
s.App.Get("/health", s.HealthHandler)
|
||||
|
||||
api := s.App.Group("/api")
|
||||
v1 := api.Group("/v1")
|
||||
v1.Post("/login", s.LoginHandler)
|
||||
}
|
||||
|
||||
func SetupMiddlewares(s *Server) {
|
||||
s.App.Use(LoggingMiddleware(s.log))
|
||||
}
|
||||
|
||||
// Middlewares
|
||||
func LoggingMiddleware(log *fluentd.Logger) func(c *fiber.Ctx) error {
|
||||
return func(c *fiber.Ctx) error {
|
||||
log.Log("Request: %s, remote: %s, via: %s",
|
||||
c.Request().URI().String(),
|
||||
c.Context().RemoteIP().String(),
|
||||
string(c.Context().UserAgent()))
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
111
src/internal/app/server/server.go
Normal file
111
src/internal/app/server/server.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
|
||||
discovery "git.pbiernat.dev/egommerce/go-api-pkg/consul"
|
||||
"git.pbiernat.dev/egommerce/go-api-pkg/fluentd"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
*fiber.App
|
||||
log *fluentd.Logger
|
||||
db *pgxpool.Pool
|
||||
// ebCh *amqp.Channel
|
||||
discovery *discovery.Service
|
||||
name string
|
||||
addr string
|
||||
}
|
||||
|
||||
type Headers struct {
|
||||
RequestID string `reqHeader:"x-request-id"`
|
||||
}
|
||||
|
||||
func NewServer(conf *Config, logger *fluentd.Logger, db *pgxpool.Pool /*, ebCh *amqp.Channel*/) *Server {
|
||||
logger.Log("API_ID: %s", conf.AppID)
|
||||
discovery, err := discovery.NewService(conf.RegistryAddr, conf.AppID, conf.AppName, conf.AppID, conf.AppDomain, conf.Port)
|
||||
if err != nil {
|
||||
logger.Log("Error connecting to %s: %v", conf.RegistryAddr, err)
|
||||
}
|
||||
|
||||
logger.Log("Registering service with name: %s, address: %s", discovery.Name, discovery.Address)
|
||||
err = discovery.Register()
|
||||
if err != nil {
|
||||
logger.Log("register error: %v", err)
|
||||
}
|
||||
|
||||
cnf := fiber.Config{
|
||||
AppName: conf.AppName,
|
||||
ServerHeader: conf.AppName,
|
||||
ReadTimeout: time.Millisecond * 50,
|
||||
WriteTimeout: time.Millisecond * 50,
|
||||
IdleTimeout: time.Millisecond * 50,
|
||||
}
|
||||
s := &Server{
|
||||
fiber.New(cnf),
|
||||
logger,
|
||||
db,
|
||||
/*ebCh,*/
|
||||
discovery,
|
||||
conf.AppName,
|
||||
conf.NetAddr,
|
||||
}
|
||||
|
||||
SetupMiddlewares(s)
|
||||
SetupRoutes(s)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) Start() {
|
||||
err := s.Listen(s.addr)
|
||||
s.log.Log("Starting error: %v", err)
|
||||
}
|
||||
|
||||
func (s *Server) StartWithGracefulShutdown(forever chan struct{}) {
|
||||
go func() {
|
||||
sigint := make(chan os.Signal, 1)
|
||||
signal.Notify(sigint, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigint
|
||||
|
||||
if err := s.gracefulShutdown(); err != nil {
|
||||
s.log.Log("Server is not shutting down! Reason: %v", err)
|
||||
}
|
||||
|
||||
close(forever)
|
||||
}()
|
||||
|
||||
if err := s.Listen(s.addr); err != nil {
|
||||
s.log.Log("Server is not running! Reason: %v", err)
|
||||
}
|
||||
|
||||
<-forever
|
||||
}
|
||||
|
||||
// GetRequestID Return current requets ID - works only when fiber context are running
|
||||
func (s *Server) GetRequestID(c *fiber.Ctx) (string, error) {
|
||||
var hdr = new(Headers)
|
||||
if err := c.ReqHeaderParser(hdr); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hdr.RequestID, nil
|
||||
}
|
||||
|
||||
func (s *Server) gracefulShutdown() error {
|
||||
s.log.Log("Server is going down...")
|
||||
s.log.Log("Unregistering service: %s", s.discovery.GetID())
|
||||
s.discovery.Unregister()
|
||||
|
||||
// s.ebCh.Close()
|
||||
s.db.Close()
|
||||
s.log.Close()
|
||||
|
||||
return s.Shutdown()
|
||||
}
|
||||
@@ -1,113 +1,50 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.pbiernat.dev/egommerce/identity-service/internal/app/config"
|
||||
def "git.pbiernat.dev/egommerce/identity-service/internal/app/definition"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
AuthService *Auth
|
||||
JWTService *JWT
|
||||
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrTokenError = errors.New("failed to generate JWT token")
|
||||
ErrLoginIncorrect = errors.New("login incorrect")
|
||||
)
|
||||
|
||||
func init() {
|
||||
expire, _ := strconv.Atoi(config.GetEnv("AUTH_TOKEN_EXPIRE_TIME", "5"))
|
||||
secret := []byte(config.GetEnv("AUTH_SECRET_HMAC", "B413IlIv9nKQfsMCXTE0Cteo4yHgUEfqaLfjg73sNlh"))
|
||||
|
||||
AuthService = &Auth{expire, "jwt_token", "jwt_token_refresh", secret}
|
||||
cookieExpireTime, _ := strconv.Atoi(config.GetEnv("AUTH_COOKIE_EXPIRE_TIME", "5"))
|
||||
AuthService = &Auth{"jwt_token", "jwt_token_refresh", cookieExpireTime}
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
ExpireTime int // token expire time in minutes
|
||||
TokenCookieName string
|
||||
RefreshTokenCookieName string
|
||||
|
||||
secret []byte // signing key
|
||||
cookieExpireTime int
|
||||
}
|
||||
|
||||
func (a *Auth) Login(r *def.AuthLoginRequest) (string, error) {
|
||||
if r.Username == "admin" && r.Password == "secret" {
|
||||
token, err := a.createToken()
|
||||
func (a *Auth) Login(login, pass string) (string, error) {
|
||||
if login == "admin" && pass == "secret" {
|
||||
token, err := JWTService.CreateToken()
|
||||
if err != nil {
|
||||
return "", ErrTokenError
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
return "", ErrUserNotFound
|
||||
return "", ErrLoginIncorrect
|
||||
}
|
||||
|
||||
// SetCookie appends cookie header to response
|
||||
func (a *Auth) SetCookie(w http.ResponseWriter, name, token string) {
|
||||
c := &http.Cookie{
|
||||
// Cookie create fiber.Cookie struct
|
||||
func (a *Auth) Cookie(name, value string) *fiber.Cookie {
|
||||
return &fiber.Cookie{
|
||||
Name: name,
|
||||
Value: token,
|
||||
MaxAge: a.ExpireTime * 60,
|
||||
Path: "/",
|
||||
Value: value,
|
||||
MaxAge: a.cookieExpireTime * 300, // FIXME: env/config
|
||||
Path: "/", // FIXME: env/config
|
||||
}
|
||||
http.SetCookie(w, c)
|
||||
}
|
||||
|
||||
func (a Auth) createToken() (string, error) {
|
||||
// log.Println("now:", time.Now().Unix())
|
||||
// log.Println("expire at:", time.Now().Add(time.Duration(a.ExpireTime)*time.Minute).Unix())
|
||||
claims := &jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(time.Duration(a.ExpireTime) * time.Minute).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(a.secret)
|
||||
}
|
||||
|
||||
func (a *Auth) validateToken(tokenStr string) error {
|
||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
|
||||
return a.secret, nil
|
||||
})
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
log.Println(claims)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a Auth) ValidateUserTokenMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cToken, err := r.Cookie(a.TokenCookieName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(def.Error("Missing JWT Token cookie"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.validateToken(cToken.Value); err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
json.NewEncoder(w).Encode(def.Error(err.Error()))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
53
src/internal/app/service/jwt.go
Normal file
53
src/internal/app/service/jwt.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.pbiernat.dev/egommerce/identity-service/internal/app/config"
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
var (
|
||||
tokenExpireTime int
|
||||
tokenSecret []byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
tokenExpireTime, _ = strconv.Atoi(config.GetEnv("JWT_TOKEN_EXPIRE_TIME", "5"))
|
||||
tokenSecret = []byte(config.GetEnv("JWT_SECRET_KEY", "B413IlIv9nKQfsMCXTE0Cteo4yHgUEfqaLfjg73sNlh")) // FIXME env: JWT_SECRET_KEY !!!
|
||||
|
||||
JWTService = &JWT{tokenExpireTime, tokenSecret}
|
||||
}
|
||||
|
||||
type JWT struct {
|
||||
tokenExpireTime int
|
||||
tokenSecret []byte
|
||||
}
|
||||
|
||||
func (s *JWT) CreateToken() (string, error) {
|
||||
claims := &jwt.StandardClaims{
|
||||
ExpiresAt: time.Now().Add(time.Duration(s.tokenExpireTime) * time.Minute).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(s.tokenSecret)
|
||||
}
|
||||
|
||||
func (s *JWT) ValidateToken(tokenStr string) error {
|
||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
return s.tokenSecret, nil
|
||||
})
|
||||
|
||||
if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user