Change paseto package to go-paseto (#45)

This commit is contained in:
itsscb 2023-09-28 23:12:50 +02:00 committed by GitHub
parent e26f46b5d2
commit 8ccc74b677
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 107 additions and 40 deletions

View File

@ -14,7 +14,7 @@ var config util.Config
func TestMain(m *testing.M) {
config = util.Config{
Environment: "production",
TokenSymmetricKey: "12345678901234567890123456789012",
TokenPrivateKeyHex: "099c0b96725b99e95719c92aec580809ac58fc14be2105ed2656f1f6c464593d8cacd6c7bed924b9cf207ab3cff1c59be4e5865260c4dafa29699244bd4ea2de",
AccessTokenDuration: time.Minute * 1,
RefreshTokenDuration: time.Minute * 2,
}

View File

@ -21,7 +21,7 @@ type Server struct {
// NewServer creates a new HTTP server and sets up routing
func NewServer(config util.Config, store db.Store) (*Server, error) {
tokenMaker, err := token.NewPasetoMaker(config.TokenSymmetricKey)
tokenMaker, err := token.NewPasetoMaker(config.TokenPrivateKeyHex)
if err != nil {
return nil, fmt.Errorf("cannot create token maker: %w", err)
}

View File

@ -5,4 +5,4 @@ ENVIRONMENT=development
LOG_OUTPUT=text
ACCESS_TOKEN_DURATION=15m
REFRESH_TOKEN_DURATION=24h
TOKEN_SYMMETRIC_KEY=12345678901234567890123456789012
TOKEN_PRIVATEKEY_HEX=099c0b96725b99e95719c92aec580809ac58fc14be2105ed2656f1f6c464593d8cacd6c7bed924b9cf207ab3cff1c59be4e5865260c4dafa29699244bd4ea2de

View File

@ -0,0 +1 @@
c2e4d86dd824e33696b3d3208c11c93eff2a64f06deb8514e0ab821070f2d504f6dc25b460fbfd04f2d7e3aaf87fa1e934d8a4ed210726921aa1b51274a9bc58

7
go.mod
View File

@ -5,11 +5,10 @@ go 1.21
toolchain go1.21.1
require (
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29
aidanwoods.dev/go-paseto v1.5.0
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.1.2
github.com/lib/pq v1.10.9
github.com/o1egl/paseto v1.0.0
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
go.uber.org/mock v0.3.0
@ -18,8 +17,7 @@ require (
)
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
aidanwoods.dev/go-result v0.1.0 // indirect
github.com/bytedance/sonic v1.10.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
@ -41,7 +39,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect

17
go.sum
View File

@ -1,3 +1,7 @@
aidanwoods.dev/go-paseto v1.5.0 h1:FKrHrip6HfZfuzLuz2NVnM7wQ3Ql+mKcWWcgDr3Mb1g=
aidanwoods.dev/go-paseto v1.5.0/go.mod h1:9J13iCMdWrkfK1AxAg9QDHLaDMYSEP1ldbFiR+DfmVc=
aidanwoods.dev/go-result v0.1.0 h1:y/BMIRX6q3HwaorX1Wzrjo3WUdiYeyWbvGe18hKS3K8=
aidanwoods.dev/go-result v0.1.0/go.mod h1:yridkWghM7AXSFA6wzx0IbsurIm1Lhuro3rYef8FBHM=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@ -38,13 +42,6 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU=
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 h1:1DcvRPZOdbQRg5nAHt2jrc5QbV0AGuhDdfQI6gXjiFE=
github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
@ -193,12 +190,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/o1egl/paseto v1.0.0 h1:bwpvPu2au176w4IBlhbyUv/S5VPptERIA99Oap5qUd0=
github.com/o1egl/paseto v1.0.0/go.mod h1:5HxsZPmw/3RI2pAwGo1HhOOwSdvBpcuVzO7uDkm+CLU=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -252,7 +245,6 @@ go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -351,7 +343,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

16
token/main_test.go Normal file
View File

@ -0,0 +1,16 @@
package token
import (
"os"
"testing"
"aidanwoods.dev/go-paseto"
)
var devPrivateKeyHex string
func TestMain(m *testing.M) {
devPrivateKeyHex = paseto.NewV4AsymmetricSecretKey().ExportHex()
os.Exit(m.Run())
}

View File

@ -0,0 +1,32 @@
package token
import (
"log"
"os"
"aidanwoods.dev/go-paseto"
)
const (
devPrivateKeyFile = "development_privatekey.asc"
)
func GenerateKeyPair() (err error) {
secretKey := paseto.NewV4AsymmetricSecretKey()
if err = os.WriteFile(devPrivateKeyFile, []byte(secretKey.ExportHex()), 0666); err != nil {
log.Fatalf("could not create development_privatekey.asc: %v", err)
return err
}
return err
}
func GetPrivateKey() (paseto.V4AsymmetricSecretKey, error) {
f, err := os.ReadFile(devPrivateKeyFile)
if err != nil {
return paseto.V4AsymmetricSecretKey{}, err
}
return paseto.NewV4AsymmetricSecretKeyFromHex(string(f))
}

View File

@ -1,28 +1,30 @@
package token
import (
"fmt"
"time"
"github.com/aead/chacha20poly1305"
"github.com/o1egl/paseto"
"aidanwoods.dev/go-paseto"
"github.com/google/uuid"
)
// PasetoMaker is a PASETO token maker
type PasetoMaker struct {
paseto *paseto.V2
symmetricKey []byte
privateKey paseto.V4AsymmetricSecretKey
publicKey paseto.V4AsymmetricPublicKey
parser paseto.Parser
}
// NewPasetoMaker creates a new PasetoMaker
func NewPasetoMaker(symmetricKey string) (Maker, error) {
if len(symmetricKey) != chacha20poly1305.KeySize {
return nil, fmt.Errorf("invalid key size: must be exactly %d characters", chacha20poly1305.KeySize)
func NewPasetoMaker(privateKeyHex string) (Maker, error) {
privateKey, err := paseto.NewV4AsymmetricSecretKeyFromHex(privateKeyHex)
if err != nil {
return nil, err
}
maker := &PasetoMaker{
paseto: paseto.NewV2(),
symmetricKey: []byte(symmetricKey),
privateKey: privateKey,
publicKey: privateKey.Public(),
parser: paseto.NewParser(),
}
return maker, nil
@ -35,19 +37,47 @@ func (maker *PasetoMaker) CreateToken(email string, duration time.Duration) (str
return "", payload, err
}
token, err := maker.paseto.Encrypt(maker.symmetricKey, payload, nil)
return token, payload, err
token := paseto.NewToken()
token.SetNotBefore(time.Now())
token.SetIssuedAt(payload.IssuedAt)
token.SetExpiration(payload.ExpiredAt)
token.SetString("id", payload.ID.String())
token.SetString("email", payload.Email)
signed := token.V4Sign(maker.privateKey, nil)
return signed, payload, err
}
// VerifyToken checks if the token is valid or not
func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) {
payload := &Payload{}
err := maker.paseto.Decrypt(token, maker.symmetricKey, payload, nil)
t, err := maker.parser.ParseV4Public(maker.publicKey, token, nil)
if err != nil {
return nil, err
}
payload.ExpiredAt, err = t.GetExpiration()
if err != nil {
return nil, ErrInvalidToken
}
payload.IssuedAt, err = t.GetIssuedAt()
if err != nil {
return nil, ErrInvalidToken
}
payload.Email, err = t.GetString("email")
if err != nil {
return nil, ErrInvalidToken
}
uid, err := t.GetString("id")
if err != nil {
return nil, ErrInvalidToken
}
payload.ID = uuid.MustParse(uid)
err = payload.Valid()
if err != nil {
return nil, err

View File

@ -9,11 +9,11 @@ import (
)
func TestPasetoMaker(t *testing.T) {
maker, err := NewPasetoMaker(util.RandomString(32))
maker, err := NewPasetoMaker(devPrivateKeyHex)
require.NoError(t, err)
email := util.RandomEmail()
duration := time.Minute
duration := time.Minute * 2
issuedAt := time.Now()
expiredAt := issuedAt.Add(duration)
@ -34,7 +34,7 @@ func TestPasetoMaker(t *testing.T) {
}
func TestExpiredPasetoToken(t *testing.T) {
maker, err := NewPasetoMaker(util.RandomString(32))
maker, err := NewPasetoMaker(devPrivateKeyHex)
require.NoError(t, err)
token, payload, err := maker.CreateToken(util.RandomEmail(), -time.Minute)

View File

@ -9,8 +9,8 @@ import (
// Different types of error returned by the VerifyToken function
var (
ErrInvalidToken = errors.New("token is invalid")
ErrExpiredToken = errors.New("token has expired")
ErrInvalidToken = errors.New("this token is invalid")
ErrExpiredToken = errors.New("this token has expired")
)
// Payload contains the payload data of the token

View File

@ -12,7 +12,7 @@ type Config struct {
ServerAddress string `mapstructure:"SERVER_ADDRESS"`
Environment string `mapstructure:"ENVIRONMENT"`
LogOutput string `mapstructure:"LOG_OUTPUT"`
TokenSymmetricKey string `mapstructure:"TOKEN_SYMMETRIC_KEY"`
TokenPrivateKeyHex string `mapstructure:"TOKEN_PRIVATEKEY_HEX"`
AccessTokenDuration time.Duration `mapstructure:"ACCESS_TOKEN_DURATION"`
RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"`
}