Skip to content

Commit

Permalink
feat jwt
Browse files Browse the repository at this point in the history
  • Loading branch information
a3510377 committed May 3, 2024
1 parent 0ae112e commit bfa8e94
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 0 deletions.
1 change: 1 addition & 0 deletions server/api/manage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package api
1 change: 1 addition & 0 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Config struct {
Address string `yaml:"address"`
AllowOrigins []string `yaml:"allow_origins"`
MemberFile string `yaml:"member_file"`
JwtExpireDay int `yaml:"jwt_expire_day"`
} `yaml:"api"`
Cache struct {
Root string `yaml:"root"`
Expand Down
1 change: 1 addition & 0 deletions server/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ api:
allow_origins:
- '*'
member_file: data/data/members.yaml
jwt_expire_day: 7
cache:
root: ./data/api/cache
uuid_file: uuid.json
Expand Down
62 changes: 62 additions & 0 deletions server/config/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package config

import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)

var (
jwtPublicKey *ecdsa.PublicKey
jwtPrivateKey *ecdsa.PrivateKey
)

func GetJwtPrivateKey() *ecdsa.PrivateKey {
return jwtPrivateKey
}

func GetJwtPublicKey() *ecdsa.PublicKey {
return jwtPublicKey
}

func LoadPrivateKey(path string) error {
content, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read private key file: %w", err)
}

block, _ := pem.Decode(content)
if block == nil {
return fmt.Errorf("failed to decode private key")
}

key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}

jwtPrivateKey = key
return nil
}

func LoadPublicKey(path string) error {
content, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("failed to read public key file: %w", err)
}

block, _ := pem.Decode(content)
if block == nil {
return fmt.Errorf("failed to decode public key")
}

key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse public key: %w", err)
}

jwtPublicKey = key.(*ecdsa.PublicKey)
return nil
}
1 change: 1 addition & 0 deletions server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ toolchain go1.22.2
require (
github.com/deckarep/golang-set/v2 v2.6.0
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/mattn/go-colorable v0.1.13
github.com/sirupsen/logrus v1.9.3
gopkg.in/yaml.v2 v2.4.0
Expand Down
2 changes: 2 additions & 0 deletions server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
Expand Down
1 change: 1 addition & 0 deletions server/model/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package model
80 changes: 80 additions & 0 deletions server/pkg/auth/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package auth

import (
"errors"
"time"

"server/config"

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

const JWT_ISSUER = "ctec-api-server"

var ErrTokenValidation = errors.New("token validation failed")

type JwtCustomClaims struct {
jwt.RegisteredClaims
ID uint `json:"id"`
}

type JwtToken struct {
Token string `json:"token"`
Claims JwtCustomClaims `json:"claims"`
}

func New() *JwtToken {
return &JwtToken{}
}

// CreateUserToken creates a token for the user
func CreateUserToken(ID uint) (*JwtToken, error) {
expiresAt := time.Now().Add(time.Hour * 24 * time.Duration(config.Get().API.JwtExpireDay))
claims := JwtCustomClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: JWT_ISSUER,
ExpiresAt: jwt.NewNumericDate(expiresAt),
},
ID: ID,
}

t := jwt.New(jwt.SigningMethodES256)
t.Claims = &claims
tokenString, err := t.SignedString(config.GetJwtPrivateKey())
if err != nil {
return nil, err
}

return &JwtToken{Token: tokenString, Claims: claims}, nil
}

// ParseToken parses the token and returns the claims
func (j *JwtToken) ParseToken(token string) (*JwtCustomClaims, error) {
t, err := jwt.ParseWithClaims(token, &JwtCustomClaims{}, func(t *jwt.Token) (any, error) {
return config.GetJwtPublicKey(), nil
})
if err != nil {
return nil, err
}

claims, ok := t.Claims.(*JwtCustomClaims)
if !ok || !t.Valid {
return nil, ErrTokenValidation
}

return claims, nil
}

// JwtClaims returns the claims from the token
func (j *JwtToken) JwtClaims(c *gin.Context) (*JwtCustomClaims, error) {
token := c.GetHeader("Authorization")
claims, err := j.ParseToken(token)
return claims, err
}

// JwtUserId returns the user ID from the token
func (j *JwtToken) JwtUserId(c *gin.Context) uint {
claims, _ := j.JwtClaims(c)
return claims.ID
}
16 changes: 16 additions & 0 deletions server/pkg/database/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package database

import (
"strconv"
"time"
)

type Model struct {
ID uint `gorm:"primarykey" json:"id,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}

func (m *Model) IDtoString() string {
return strconv.Itoa(int(m.ID))
}

0 comments on commit bfa8e94

Please sign in to comment.