working on auth
This commit is contained in:
parent
a2739dbbcd
commit
5954ec2501
@ -5,4 +5,4 @@ GRAPH_URL = http://localhost:3009
|
||||
VITE_GRAPH_URL = http://localhost:3009
|
||||
DB_URL = postgresql://root:root@127.0.0.1/rano_dev?search_path=public&sslmode=disable
|
||||
MAILER_TEMPLATES_DIR = mailer/templates
|
||||
MAILER_FROM_ADDRESS = NoReply<no-reply@my-app.com>
|
||||
MAILER_FROM_ADDRESS = NoReply<no-reply@my-app.com>
|
3
config/certs/auth
Normal file
3
config/certs/auth
Normal file
@ -0,0 +1,3 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEA3EQvTeaEjR5CMk2Ka6/tUl9NaPRpvRggeto+vmReWB4=
|
||||
-----END PUBLIC KEY-----
|
3
config/certs/auth.pub
Normal file
3
config/certs/auth.pub
Normal file
@ -0,0 +1,3 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VwAyEA3EQvTeaEjR5CMk2Ka6/tUl9NaPRpvRggeto+vmReWB4=
|
||||
-----END PUBLIC KEY-----
|
@ -38,7 +38,7 @@ type (
|
||||
WebPort int `env:"WEB_PORT"`
|
||||
WebURL string `env:"WEB_URL"`
|
||||
GraphPort int `env:"GRAPH_PORT"`
|
||||
GrapURL string `env:"GRAPH_URL"`
|
||||
GraphURL string `env:"GRAPH_URL"`
|
||||
DbURL string `env:"DB_URL"`
|
||||
MailerTplDir string `env:"MAILER_TEMPLATES_DIR"`
|
||||
MailerFrom string `env:"MAILER_FROM_ADDRESS"`
|
||||
|
63
db/client.go
63
db/client.go
@ -8,6 +8,9 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"contrib.go.opencensus.io/integrations/ocsql"
|
||||
@ -58,24 +61,31 @@ func Client() *ent.Client {
|
||||
return cl
|
||||
}
|
||||
|
||||
type entity interface {
|
||||
GetID() (int64, error)
|
||||
}
|
||||
|
||||
// A AuditHook is an example for audit-log hook.
|
||||
func AuditHook(next ent.Mutator) ent.Mutator {
|
||||
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (v ent.Value, err error) {
|
||||
// start timer
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
saveAudit(ctx, m.Type(), m.Op(), v, err, time.Since(start))
|
||||
}()
|
||||
|
||||
// do the operation
|
||||
v, err = next.Mutate(ctx, m)
|
||||
|
||||
// audit log
|
||||
var id int64
|
||||
if v != nil {
|
||||
ev := reflect.Indirect(reflect.ValueOf(v))
|
||||
f := ev.FieldByName("ID")
|
||||
if f.IsValid() {
|
||||
id = f.Int()
|
||||
}
|
||||
}
|
||||
|
||||
saveAudit(ctx, m.Type(), m.Op(), id, err, time.Since(start))
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func saveAudit(ctx context.Context, t string, op ent.Op, v ent.Value, err error, d time.Duration) {
|
||||
func saveAudit(ctx context.Context, t string, op ent.Op, id int64, err error, d time.Duration) {
|
||||
if t == "Audit" {
|
||||
// skip Audit table operations
|
||||
return
|
||||
@ -95,15 +105,30 @@ func saveAudit(ctx context.Context, t string, op ent.Op, v ent.Value, err error,
|
||||
entOp = "UpdateOne"
|
||||
}
|
||||
|
||||
big.
|
||||
reqID := ctx.Value("RequestID")
|
||||
ip := ctx.Value("RequestIP")
|
||||
ua := ctx.Value("RequestUA")
|
||||
|
||||
if en, ok := v.(entity); ok {
|
||||
id, _ := en.GetID()
|
||||
logger.Info("%s %s %s-%s(%d) ip=%s ua=%s t=%v error=%e", reqID, status, op.String(), t, id, ip, ua, d, err)
|
||||
} else {
|
||||
logger.Info("%s %s %s-%s ip=%s ua=%s t=%v error=%e", reqID, status, op.String(), t, ip, ua, d, err)
|
||||
var sb strings.Builder
|
||||
if reqID, ok := ctx.Value("RequestID").(string); ok {
|
||||
sb.WriteString(reqID + " ")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
sb.WriteString("failed ")
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("%s:%s:%d ", entOp, t, id))
|
||||
|
||||
if ip, ok := ctx.Value("RequestIP").(string); ok {
|
||||
sb.WriteString(fmt.Sprintf("ip=%s ", ip))
|
||||
}
|
||||
|
||||
if ua, ok := ctx.Value("RequestUA").(string); ok {
|
||||
sb.WriteString(fmt.Sprintf("ip=%s ", ua))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
sb.WriteString(fmt.Sprintf("error=%s ", err))
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf("t=%s", d))
|
||||
|
||||
logger.Info(sb.String())
|
||||
}
|
||||
|
11
go.mod
11
go.mod
@ -6,6 +6,7 @@ require (
|
||||
github.com/99designs/gqlgen v0.17.56
|
||||
github.com/brianvoe/gofakeit/v7 v7.1.2
|
||||
github.com/jackc/pgx/v5 v5.7.1
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.2
|
||||
github.com/sqids/sqids-go v0.4.1
|
||||
github.com/vektah/gqlparser/v2 v2.5.19
|
||||
gitserver.in/patialtech/mux v0.3.1
|
||||
@ -17,11 +18,13 @@ require (
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-openapi/inflect v0.21.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@ -31,8 +34,14 @@ require (
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc v1.0.6 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/zclconf/go-cty v1.15.0 // indirect
|
||||
@ -51,7 +60,7 @@ require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
|
19
go.sum
19
go.sum
@ -42,6 +42,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
|
||||
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/dhui/dktest v0.4.3 h1:wquqUxAFdcUgabAVLvSCOKOlag5cIZuaOjYIBOWdsR0=
|
||||
@ -80,6 +82,8 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y=
|
||||
@ -134,6 +138,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
|
||||
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
|
||||
github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.2 h1:6poete4MPsO8+LAEVhpdrNI4Xp2xdiafgl2RD89moBc=
|
||||
github.com/lestrrat-go/jwx/v2 v2.1.2/go.mod h1:pO+Gz9whn7MPdbsqSJzG8TlEpMZCwQDXnFJ+zsUVh8Y=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
@ -159,6 +175,8 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
|
||||
@ -171,6 +189,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
|
@ -17,15 +17,10 @@ import (
|
||||
"gitserver.in/patialtech/rano/graph/model"
|
||||
"gitserver.in/patialtech/rano/util/crypto"
|
||||
"gitserver.in/patialtech/rano/util/logger"
|
||||
"gitserver.in/patialtech/rano/util/uid"
|
||||
)
|
||||
|
||||
type (
|
||||
SessionUser struct {
|
||||
ID string
|
||||
Email string
|
||||
Name string
|
||||
RoleID int
|
||||
}
|
||||
AuthUser = model.AuthUser
|
||||
)
|
||||
|
||||
@ -37,7 +32,7 @@ var (
|
||||
)
|
||||
|
||||
func CtxWithUser(ctx context.Context, u *AuthUser) context.Context {
|
||||
return context.WithValue(ctx, config.AuthUserCtxKey, &SessionUser{
|
||||
return context.WithValue(ctx, config.AuthUserCtxKey, &AuthUser{
|
||||
ID: u.ID,
|
||||
Email: u.Email,
|
||||
Name: u.Name,
|
||||
@ -45,8 +40,8 @@ func CtxWithUser(ctx context.Context, u *AuthUser) context.Context {
|
||||
})
|
||||
}
|
||||
|
||||
func CtxUser(ctx context.Context) *SessionUser {
|
||||
u, _ := ctx.Value(config.AuthUserCtxKey).(*SessionUser)
|
||||
func CtxUser(ctx context.Context) *AuthUser {
|
||||
u, _ := ctx.Value(config.AuthUserCtxKey).(*AuthUser)
|
||||
return u
|
||||
}
|
||||
|
||||
@ -62,16 +57,35 @@ func NewSession(ctx context.Context, email, pwd string) (*AuthUser, error) {
|
||||
|
||||
// 30 day token life
|
||||
until := time.Now().Add(time.Hour * 24 * 30).UTC()
|
||||
// user IP
|
||||
ip, _ := ctx.Value("RequestIP").(string)
|
||||
// user Agent
|
||||
ua, _ := ctx.Value("RequestUA").(string)
|
||||
|
||||
// create sesion entry in db
|
||||
db.Client().UserSession.Create().
|
||||
// create session entry in db
|
||||
s, err := db.Client().UserSession.Create().
|
||||
SetUserID(u.ID).
|
||||
SetIssuedAt(time.Now().UTC()).
|
||||
SetExpiresAt(until).
|
||||
SetIP("").
|
||||
SetUserAgent("")
|
||||
SetIP(ip).
|
||||
SetUserAgent(ua).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, ErrUnexpected
|
||||
}
|
||||
|
||||
sid, err := uid.Encode(
|
||||
uint64(u.ID),
|
||||
uint64(s.ID),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, ErrUnexpected
|
||||
}
|
||||
|
||||
return &AuthUser{
|
||||
ID: sid,
|
||||
Name: fullName(u.FirstName, *u.MiddleName, u.LastName),
|
||||
}, nil
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ var (
|
||||
// newTokenToVerifyEmail for a user for given duration
|
||||
func newTokenToVerifyEmail(userID int64, d time.Duration) (string, error) {
|
||||
expiresAt := time.Now().Add(d).UTC().UnixMilli()
|
||||
return uid.Encode([]uint64{
|
||||
return uid.Encode(
|
||||
uint64(userID),
|
||||
1, // identifies that its token to verify email
|
||||
uint64(expiresAt),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// tokenToVerifyEmail will check for valid email token that is yet not expired
|
||||
|
99
util/crypto/ed25519.go
Normal file
99
util/crypto/ed25519.go
Normal file
@ -0,0 +1,99 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// NewPersistedED25519 public & private keys
|
||||
func NewPersistedED25519() ([]byte, []byte, error) {
|
||||
pub, prv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// marshal public key
|
||||
pubB, err := marshalEdPublicKey(pub)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// marshal private key
|
||||
prvB, err := marshalEdPrivateKey(prv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// done
|
||||
return pubB, prvB, nil
|
||||
}
|
||||
|
||||
func marshalEdPrivateKey(key ed25519.PrivateKey) ([]byte, error) {
|
||||
b, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enc := pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: b,
|
||||
},
|
||||
)
|
||||
return enc, nil
|
||||
}
|
||||
|
||||
func parseEdPrivateKey(d []byte) (ed25519.PrivateKey, error) {
|
||||
block, _ := pem.Decode(d)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to parse PEM block containing the key")
|
||||
}
|
||||
|
||||
b, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pub := b.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
return pub, nil
|
||||
default:
|
||||
return nil, errors.New("key type is not RSA")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func marshalEdPublicKey(key ed25519.PublicKey) ([]byte, error) {
|
||||
b, err := x509.MarshalPKIXPublicKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enc := pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: b,
|
||||
},
|
||||
)
|
||||
return enc, nil
|
||||
}
|
||||
|
||||
func parseEdPublicKey(d []byte) (ed25519.PublicKey, error) {
|
||||
block, _ := pem.Decode(d)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to parse PEM block containing the key")
|
||||
}
|
||||
|
||||
b, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pub := b.(type) {
|
||||
case ed25519.PublicKey:
|
||||
return pub, nil
|
||||
default:
|
||||
return nil, errors.New("key type is not RSA")
|
||||
}
|
||||
}
|
16
util/crypto/ed25519_test.go
Normal file
16
util/crypto/ed25519_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
public, private, err := NewPersistedED25519()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Log(string(public))
|
||||
t.Log(string(private))
|
||||
}
|
||||
|
||||
}
|
58
util/jwt/jwt.go
Normal file
58
util/jwt/jwt.go
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2024 Patial Tech. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||
j "github.com/lestrrat-go/jwx/v2/jwt"
|
||||
)
|
||||
|
||||
func Sign(key ed25519.PrivateKey, claims map[string]interface{}, issuer string, d time.Duration) (string, error) {
|
||||
prv, err := jwk.FromRaw(key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create JWK, %w", err)
|
||||
}
|
||||
|
||||
builder := j.NewBuilder().
|
||||
Issuer(issuer).
|
||||
IssuedAt(time.Now()).
|
||||
Expiration(time.Now().Add(d))
|
||||
|
||||
for k, v := range claims {
|
||||
builder = builder.Claim(k, v)
|
||||
}
|
||||
|
||||
token, err := builder.Build()
|
||||
signed, err := j.Sign(token, j.WithKey(jwa.EdDSA, prv))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate signed payload, %w", err)
|
||||
}
|
||||
|
||||
return string(signed), nil
|
||||
}
|
||||
|
||||
func Parse(key ed25519.PrivateKey, payload string) (j.Token, error) {
|
||||
prv, err := jwk.FromRaw(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create JWK, %w", err)
|
||||
}
|
||||
|
||||
pub, err := jwk.PublicKeyOf(prv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed on jwk.FromRaw, %w", err)
|
||||
}
|
||||
|
||||
token, err := j.Parse([]byte(payload), j.WithKey(jwa.EdDSA, pub))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed on jwk.FromRaw, %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
@ -13,7 +13,7 @@ var opts sqids.Options = sqids.Options{
|
||||
}
|
||||
|
||||
// Encode a slice of IDs into one unique ID
|
||||
func Encode(ids []uint64) (string, error) {
|
||||
func Encode(ids ...uint64) (string, error) {
|
||||
s, err := sqids.New()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
Loading…
Reference in New Issue
Block a user