added code for token issuance and verification
This commit is contained in:
parent
b7992a0bb7
commit
8bee855aa8
4
Makefile
4
Makefile
@ -33,6 +33,7 @@ DATA = \
|
|||||||
xterm.html
|
xterm.html
|
||||||
|
|
||||||
CMD_DATA=\
|
CMD_DATA=\
|
||||||
|
cmd/rsa.key \
|
||||||
cmd/tls.crt \
|
cmd/tls.crt \
|
||||||
cmd/tls.key
|
cmd/tls.key
|
||||||
|
|
||||||
@ -81,4 +82,7 @@ cmd/tls.crt:
|
|||||||
cmd/tls.key:
|
cmd/tls.key:
|
||||||
openssl req -x509 -newkey rsa:4096 -keyout cmd/tls.key -out cmd/tls.crt -sha256 -days 36500 -nodes -subj "/CN=$(NAME)" --addext "subjectAltName=DNS:$(NAME),IP:10.0.0.1,IP:::1"
|
openssl req -x509 -newkey rsa:4096 -keyout cmd/tls.key -out cmd/tls.crt -sha256 -days 36500 -nodes -subj "/CN=$(NAME)" --addext "subjectAltName=DNS:$(NAME),IP:10.0.0.1,IP:::1"
|
||||||
|
|
||||||
|
cmd/rsa.key:
|
||||||
|
openssl genrsa -traditional -out cmd/rsa.key 2048
|
||||||
|
|
||||||
.PHONY: clean test
|
.PHONY: clean test
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "crypto/rsa"
|
||||||
import "crypto/tls"
|
import "crypto/tls"
|
||||||
import "crypto/x509"
|
import "crypto/x509"
|
||||||
import "encoding/base64"
|
import "encoding/base64"
|
||||||
|
import "encoding/pem"
|
||||||
import "errors"
|
import "errors"
|
||||||
import "fmt"
|
import "fmt"
|
||||||
import "hodu"
|
import "hodu"
|
||||||
@ -49,6 +51,8 @@ type AuthConfig struct {
|
|||||||
Realm string `yaml:"realm"`
|
Realm string `yaml:"realm"`
|
||||||
Creds []string `yaml:"credentials"`
|
Creds []string `yaml:"credentials"`
|
||||||
TokenTtl string `yaml:"token-ttl"`
|
TokenTtl string `yaml:"token-ttl"`
|
||||||
|
TokenRsaKeyText string `yaml:"token-rsa-key-text"`
|
||||||
|
TokenRsaKeyFile string `yaml:"token-rsa-key-file"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CTLServiceConfig struct {
|
type CTLServiceConfig struct {
|
||||||
@ -346,11 +350,14 @@ func make_tls_client_config(cfg *ClientTLSConfig) (*tls.Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
func make_server_basic_auth_config(cfg *AuthConfig) (*hodu.ServerAuthConfig, error) {
|
func make_server_auth_config(cfg *AuthConfig) (*hodu.ServerAuthConfig, error) {
|
||||||
var config hodu.ServerAuthConfig
|
var config hodu.ServerAuthConfig
|
||||||
var cred string
|
var cred string
|
||||||
var b []byte
|
var b []byte
|
||||||
var x []string
|
var x []string
|
||||||
|
var rsa_key_text []byte
|
||||||
|
var rk *rsa.PrivateKey
|
||||||
|
var pb *pem.Block
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
config.Enabled = cfg.Enabled
|
config.Enabled = cfg.Enabled
|
||||||
@ -361,6 +368,7 @@ func make_server_basic_auth_config(cfg *AuthConfig) (*hodu.ServerAuthConfig, err
|
|||||||
return nil, fmt.Errorf("invalid token ttl %s - %s", cred, err)
|
return nil, fmt.Errorf("invalid token ttl %s - %s", cred, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert user credentials
|
||||||
for _, cred = range cfg.Creds {
|
for _, cred = range cfg.Creds {
|
||||||
b, err = base64.StdEncoding.DecodeString(cred)
|
b, err = base64.StdEncoding.DecodeString(cred)
|
||||||
if err == nil { cred = string(b) }
|
if err == nil { cred = string(b) }
|
||||||
@ -368,11 +376,33 @@ func make_server_basic_auth_config(cfg *AuthConfig) (*hodu.ServerAuthConfig, err
|
|||||||
// each entry must be of the form username:password
|
// each entry must be of the form username:password
|
||||||
x = strings.Split(cred, ":")
|
x = strings.Split(cred, ":")
|
||||||
if len(x) != 2 {
|
if len(x) != 2 {
|
||||||
return nil, fmt.Errorf("invalid basic auth credential - %s", cred)
|
return nil, fmt.Errorf("invalid auth credential - %s", cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Creds[x[0]] = x[1]
|
config.Creds[x[0]] = x[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// load rsa key
|
||||||
|
if cfg.TokenRsaKeyText == "" && cfg.TokenRsaKeyFile != "" {
|
||||||
|
rsa_key_text, err = os.ReadFile(cfg.TokenRsaKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read %s - %s", cfg.TokenRsaKeyFile, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(rsa_key_text) == 0 { rsa_key_text = []byte(cfg.TokenRsaKeyText) }
|
||||||
|
if len(rsa_key_text) == 0 { rsa_key_text = hodu_rsa_key_text }
|
||||||
|
|
||||||
|
pb, b = pem.Decode(rsa_key_text)
|
||||||
|
if pb == nil || len(b) > 0 {
|
||||||
|
return nil, fmt.Errorf("invalid token rsa key text %s - no block or too many blocks", string(rsa_key_text))
|
||||||
|
}
|
||||||
|
|
||||||
|
rk, err = x509.ParsePKCS1PrivateKey(pb.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid token rsa key text %s - %s", string(rsa_key_text), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
config.TokenRsaKey = rk
|
||||||
return &config, nil
|
return &config, nil
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ var HODU_VERSION string = "0.0.0"
|
|||||||
var hodu_tls_cert_text []byte
|
var hodu_tls_cert_text []byte
|
||||||
//go:embed tls.key
|
//go:embed tls.key
|
||||||
var hodu_tls_key_text []byte
|
var hodu_tls_key_text []byte
|
||||||
|
//go:embed rsa.key
|
||||||
|
var hodu_rsa_key_text []byte
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
type signal_handler struct {
|
type signal_handler struct {
|
||||||
@ -125,7 +127,7 @@ func server_main(ctl_addrs []string, rpc_addrs []string, pxy_addrs []string, wpx
|
|||||||
if len(config.PxyAddrs) <= 0 { config.PxyAddrs = cfg.PXY.Service.Addrs }
|
if len(config.PxyAddrs) <= 0 { config.PxyAddrs = cfg.PXY.Service.Addrs }
|
||||||
if len(config.WpxAddrs) <= 0 { config.WpxAddrs = cfg.WPX.Service.Addrs }
|
if len(config.WpxAddrs) <= 0 { config.WpxAddrs = cfg.WPX.Service.Addrs }
|
||||||
|
|
||||||
config.CtlAuth, err = make_server_basic_auth_config(&cfg.CTL.Service.Auth)
|
config.CtlAuth, err = make_server_auth_config(&cfg.CTL.Service.Auth)
|
||||||
if err != nil { return err }
|
if err != nil { return err }
|
||||||
|
|
||||||
config.CtlPrefix = cfg.CTL.Service.Prefix
|
config.CtlPrefix = cfg.CTL.Service.Prefix
|
||||||
|
27
cmd/rsa.key
Normal file
27
cmd/rsa.key
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAsTC9roInjDzu12tjv1CsOM4jvuB6/5vv+cmOMF5GLMVTnJCW
|
||||||
|
6U9onsOi6iN2rzlf5glkjdtijXCPL6QEX3YLYPD4NFCiOGIPhCHjWC4nBjI7LEEm
|
||||||
|
0SqrArMhPiyYLmnkA961a7mDw9dcr5JQBDq2ZyTe917N229Jr4PCZbHLboOxNlp3
|
||||||
|
QLSyxE5tfKZea53qm8SUF8maBvnOH8igvuYOek3iRMg3T+GoxCqy2gE1qznvwsaK
|
||||||
|
PdmTTzbIbc7XNU7t5yT6fZTvjUqs4WBuHqud4unE//KAT5vfxDdQFGcb45oMwxcK
|
||||||
|
bf03w4ZsBNvAcgCkWW+ophEOZRPkKrluHjVdNwIDAQABAoIBAARZ/5aNEL6TcoQs
|
||||||
|
2X7F0uz0NxGFfs/POxYF2q2aaxvHXtXOAT7KmfWoNVSNuWj1PkMugN8w/5scpA+V
|
||||||
|
9huIESB42oeiYVGEKwBiOqycOY4f5q8gDH1/kEKZNpxJyRT+ucBUlF0IadGB9P9E
|
||||||
|
1x07eeZPlAA8Pk8AzSz3zerkcmwM2lYYG851QyuiiTReSec3LLDcJvG5xAXZrIY0
|
||||||
|
Zwm7qv8uvjJGqMVYlywMnRngeNywP9ZaOJ38vdmWMu4bBF+QwydOAB9A7O9zluDZ
|
||||||
|
wK7OBedAZkRT15luZ1lkuTrKVZEaugD8dbt6BBLuhbPRRGuFb4WoNaVI3CRu9RSX
|
||||||
|
72gYkRECgYEA9x0IAFGc8DmCHOP/S+uy0VjvLGYh4QN3/0UOLRvoREzF0FtAxqci
|
||||||
|
bPASGmSCJEDL93JNjlxhITDUUawyOGRgAAXyAkE9MWmv18+pfTNTDeoaeXsBqcLz
|
||||||
|
f9LCNc3mCx93tvCK7gfIYs8Ef0QKfdrsQwMGlutgXmjE+pexNXPFWEcCgYEAt4/8
|
||||||
|
gsXi7tsCQp1YiP7VFZjoXSLejq+7pQrGV58PzlZKiOH/M5S6YS8wgm5oIEMLq2UP
|
||||||
|
nUn+FBCJ/I2b6HIdVq/Jr77XHcBFSZZEQbXe2gxTTucj6BTja1kSEilOquaaPvbR
|
||||||
|
WEs0+50rsgH0nLqSbMZZRkxOAUu9nObFvHA6O5ECgYEAzzd8+id13suam/dkoZlo
|
||||||
|
PbzB8w1B45oxCdIybQk13/AxAONEklCcwZUe2RrnNtdPMpSbDIHSwS5dHI+1HSyu
|
||||||
|
g9Z4dgOW+NSTK/lrOx3Ky6Q/xxaq8lwULF/jk5KxESq2DKXxGmFUW+cU8lNwKNFn
|
||||||
|
xVnIMM335bMdWrXRV+1Y0wkCgYBbXYOl47Esij35wi+LIKwW7+DYWr7D7pxLba2D
|
||||||
|
d1x6q2C1+Sb5GZIbRU2z3hhd1oE8cjTvaSDaA9Fqr2FmtUX9G8obe7W+zTCvi+e1
|
||||||
|
fTzK80+T+mBY5+y6Rb9E4uKRFe64YEma1PQuOPDCzU5fpE21bpSI9PnukzBxpDvP
|
||||||
|
q1yQwQKBgQCXiW8UghuwIp3INFzBTedBHNKBwRd82ZIhBWLcgWxC/EyWsRRFpJj4
|
||||||
|
HlVRYOvi2Q3DV6+Yn8zg3OeBhudGfCRCTkENbzAalcWqr9qb3Q4y26tZZQ9yNKk1
|
||||||
|
jJ2OfVw4K/6L49iVNF/2kLdbRebQXwngQUmiZSai5MlrHOFYkkiwaA==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
58
jwt.go
58
jwt.go
@ -1,7 +1,7 @@
|
|||||||
package hodu
|
package hodu
|
||||||
|
|
||||||
import "crypto"
|
import "crypto"
|
||||||
import "crypto/hmac"
|
//import "crypto/hmac"
|
||||||
import "crypto/rand"
|
import "crypto/rand"
|
||||||
import "crypto/rsa"
|
import "crypto/rsa"
|
||||||
import "encoding/base64"
|
import "encoding/base64"
|
||||||
@ -10,6 +10,7 @@ import "fmt"
|
|||||||
import "hash"
|
import "hash"
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
|
/*
|
||||||
func Sign(data []byte, privkey *rsa.PrivateKey) ([]byte, error) {
|
func Sign(data []byte, privkey *rsa.PrivateKey) ([]byte, error) {
|
||||||
var h hash.Hash
|
var h hash.Hash
|
||||||
|
|
||||||
@ -47,8 +48,11 @@ func VerifyHS512(data []byte, key string, sig []byte) error {
|
|||||||
if !hmac.Equal(h.Sum(nil), sig) { return fmt.Errorf("invalid signature") }
|
if !hmac.Equal(h.Sum(nil), sig) { return fmt.Errorf("invalid signature") }
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
type JWT struct {
|
type JWT[T any] struct {
|
||||||
|
key *rsa.PrivateKey
|
||||||
|
claims *T
|
||||||
}
|
}
|
||||||
|
|
||||||
type JWTHeader struct {
|
type JWTHeader struct {
|
||||||
@ -58,39 +62,47 @@ type JWTHeader struct {
|
|||||||
|
|
||||||
type JWTClaimMap map[string]interface{}
|
type JWTClaimMap map[string]interface{}
|
||||||
|
|
||||||
func (j *JWT) Sign(claims interface{}) (string, error) {
|
func NewJWT[T any](key *rsa.PrivateKey, claims *T) *JWT[T] {
|
||||||
|
return &JWT[T]{key: key, claims: claims}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JWT[T]) SignRS512() (string, error) {
|
||||||
var h JWTHeader
|
var h JWTHeader
|
||||||
var hb []byte
|
var hb []byte
|
||||||
var cb []byte
|
var cb []byte
|
||||||
var ss string
|
var ss string
|
||||||
var sb []byte
|
var sb []byte
|
||||||
|
var hs hash.Hash
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
h.Algo = "HS512"
|
h.Algo = "RS512"
|
||||||
h.Type = "JWT"
|
h.Type = "JWT"
|
||||||
|
|
||||||
hb, err = json.Marshal(h)
|
hb, err = json.Marshal(h)
|
||||||
if err != nil { return "", err }
|
if err != nil { return "", err }
|
||||||
|
|
||||||
cb, err = json.Marshal(claims)
|
cb, err = json.Marshal(j.claims)
|
||||||
if err != nil { return "", err }
|
if err != nil { return "", err }
|
||||||
|
|
||||||
ss = base64.RawURLEncoding.EncodeToString(hb) + "." + base64.RawURLEncoding.EncodeToString(cb)
|
ss = base64.RawURLEncoding.EncodeToString(hb) + "." + base64.RawURLEncoding.EncodeToString(cb)
|
||||||
sb, err = SignHS512([]byte(ss), "hello")
|
|
||||||
|
hs = crypto.SHA512.New()
|
||||||
|
hs.Write([]byte(ss))
|
||||||
|
|
||||||
|
sb, err = rsa.SignPKCS1v15(rand.Reader, j.key, crypto.SHA512, hs.Sum(nil))
|
||||||
if err != nil { return "", err }
|
if err != nil { return "", err }
|
||||||
|
|
||||||
//fmt.Printf ("%+v %+v %s\n", string(hb), string(cb), (ss + "." + base64.RawURLEncoding.EncodeToString(sb)))
|
//fmt.Printf ("%+v %+v %s\n", string(hb), string(cb), (ss + "." + base64.RawURLEncoding.EncodeToString(sb)))
|
||||||
return ss + "." + base64.RawURLEncoding.EncodeToString(sb), nil
|
return ss + "." + base64.RawURLEncoding.EncodeToString(sb), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JWT) Verify(tok string) error {
|
func (j *JWT[T]) VerifyRS512(tok string) error {
|
||||||
var segs []string
|
var segs []string
|
||||||
var hb []byte
|
var hb []byte
|
||||||
var cb []byte
|
var cb []byte
|
||||||
var sb []byte
|
var ss []byte
|
||||||
var jh JWTHeader
|
var jh JWTHeader
|
||||||
var jcm JWTClaimMap
|
var hs hash.Hash
|
||||||
var x string
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
segs = strings.Split(tok, ".")
|
segs = strings.Split(tok, ".")
|
||||||
@ -100,25 +112,23 @@ func (j *JWT) Verify(tok string) error {
|
|||||||
if err != nil { return fmt.Errorf("invalid header - %s", err.Error()) }
|
if err != nil { return fmt.Errorf("invalid header - %s", err.Error()) }
|
||||||
err = json.Unmarshal(hb, &jh)
|
err = json.Unmarshal(hb, &jh)
|
||||||
if err != nil { return fmt.Errorf("invalid header - %s", err.Error()) }
|
if err != nil { return fmt.Errorf("invalid header - %s", err.Error()) }
|
||||||
//fmt.Printf ("DECODED HEADER [%+v]\n", jh)
|
|
||||||
|
if jh.Algo != "RS512" || jh.Type != "JWT" { return fmt.Errorf("invalid header content %+v", jh) }
|
||||||
|
|
||||||
cb, err = base64.RawURLEncoding.DecodeString(segs[1])
|
cb, err = base64.RawURLEncoding.DecodeString(segs[1])
|
||||||
if err != nil { return fmt.Errorf("invalid claims - %s", err.Error()) }
|
if err != nil { return fmt.Errorf("invalid claims - %s", err.Error()) }
|
||||||
err = json.Unmarshal(cb, &jcm)
|
err = json.Unmarshal(cb, j.claims)
|
||||||
if err != nil { return fmt.Errorf("invalid header - %s", err.Error()) }
|
if err != nil { return fmt.Errorf("invalid claims - %s", err.Error()) }
|
||||||
//fmt.Printf ("DECODED CLAIMS [%+v]\n", jcm)
|
|
||||||
|
|
||||||
x, err = j.Sign(jcm)
|
ss, err = base64.RawURLEncoding.DecodeString(segs[2])
|
||||||
if err != nil { return err }
|
if err != nil { return fmt.Errorf("invalid signature - %s", err.Error()) }
|
||||||
|
|
||||||
if x != tok { return fmt.Errorf("signature mismatch") }
|
hs = crypto.SHA512.New()
|
||||||
//fmt.Printf ("VERIFICATION OK...[%s] [%s]\n", x, tok)
|
hs.Write([]byte(segs[0]))
|
||||||
|
hs.Write([]byte("."))
|
||||||
// sb, err = base64.RawURLEncoding.DecodeString(segs[2])
|
hs.Write([]byte(segs[1]))
|
||||||
// if err != nil { return fmt.Errorf("invalid signature - %s", err.Error()) }
|
err = rsa.VerifyPKCS1v15(&j.key.PublicKey, crypto.SHA512, hs.Sum(nil), ss)
|
||||||
// TODO: check expiry and others...
|
if err != nil { return fmt.Errorf("unverifiable signature - %s", err.Error()) }
|
||||||
|
|
||||||
_ = sb
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
19
jwt_test.go
19
jwt_test.go
@ -1,10 +1,11 @@
|
|||||||
package hodu_test
|
package hodu_test
|
||||||
|
|
||||||
|
import "crypto/rand"
|
||||||
|
import "crypto/rsa"
|
||||||
import "hodu"
|
import "hodu"
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestJwt(t *testing.T) {
|
func TestJwt(t *testing.T) {
|
||||||
var j hodu.JWT
|
|
||||||
var tok string
|
var tok string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -18,9 +19,21 @@ func TestJwt(t *testing.T) {
|
|||||||
jc.Abc = "def"
|
jc.Abc = "def"
|
||||||
jc.Donkey = "kong"
|
jc.Donkey = "kong"
|
||||||
jc.IssuedAt = 111
|
jc.IssuedAt = 111
|
||||||
tok, err = j.Sign(&jc)
|
|
||||||
|
var key *rsa.PrivateKey
|
||||||
|
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil { t.Fatalf("keygen failure - %s", err.Error()) }
|
||||||
|
|
||||||
|
var j *hodu.JWT[JWTClaim]
|
||||||
|
j = hodu.NewJWT(key, &jc)
|
||||||
|
tok, err = j.SignRS512()
|
||||||
if err != nil { t.Fatalf("signing failure - %s", err.Error()) }
|
if err != nil { t.Fatalf("signing failure - %s", err.Error()) }
|
||||||
|
|
||||||
err = j.Verify(tok)
|
jc = JWTClaim{}
|
||||||
|
err = j.VerifyRS512(tok)
|
||||||
if err != nil { t.Fatalf("verification failure - %s", err.Error()) }
|
if err != nil { t.Fatalf("verification failure - %s", err.Error()) }
|
||||||
|
|
||||||
|
if jc.Abc != "def" { t.Fatal("decoding failure of Abc field") }
|
||||||
|
if jc.Donkey != "kong" { t.Fatal("decoding failure of Donkey field") }
|
||||||
|
if jc.IssuedAt != 111 { t.Fatal("decoding failure of Issued field") }
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,16 @@ import "net/http"
|
|||||||
import "strings"
|
import "strings"
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
type ServerTokenClaim struct {
|
||||||
|
ExpiresAt int64 `json:"exp"`
|
||||||
|
IssuedAt int64 `json:"iat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type json_out_token struct {
|
||||||
|
AccessToken string `json:"access-token"`
|
||||||
|
RefreshToken string `json:"refresh-token"`
|
||||||
|
}
|
||||||
|
|
||||||
type json_out_server_conn struct {
|
type json_out_server_conn struct {
|
||||||
Id ConnId `json:"id"`
|
Id ConnId `json:"id"`
|
||||||
ServerAddr string `json:"server-addr"`
|
ServerAddr string `json:"server-addr"`
|
||||||
@ -86,9 +96,16 @@ func (ctl *server_ctl) Authenticate(req *http.Request) string {
|
|||||||
|
|
||||||
auth_parts = strings.Fields(auth_hdr)
|
auth_parts = strings.Fields(auth_hdr)
|
||||||
if len(auth_parts) == 2 && strings.EqualFold(auth_parts[0], "Bearer") {
|
if len(auth_parts) == 2 && strings.EqualFold(auth_parts[0], "Bearer") {
|
||||||
var jwt JWT
|
var jwt *JWT[ServerTokenClaim]
|
||||||
err = jwt.Verify(strings.TrimSpace(auth_parts[1]))
|
var claim ServerTokenClaim
|
||||||
if err == nil { return "" }
|
jwt = NewJWT(s.cfg.CtlAuth.TokenRsaKey, &claim)
|
||||||
|
err = jwt.VerifyRS512(strings.TrimSpace(auth_parts[1]))
|
||||||
|
if err == nil {
|
||||||
|
// verification ok. let's check the actual payload
|
||||||
|
var now time.Time
|
||||||
|
now = time.Now()
|
||||||
|
if now.After(time.Unix(claim.IssuedAt, 0)) && now.Before(time.Unix(claim.ExpiresAt, 0)) { return "" } // not expired
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fall back to basic authentication
|
// fall back to basic authentication
|
||||||
@ -104,16 +121,6 @@ func (ctl *server_ctl) Authenticate(req *http.Request) string {
|
|||||||
|
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
|
|
||||||
type ServerTokenClaim struct {
|
|
||||||
ExpiresAt int64 `json:"exp"`
|
|
||||||
IssuedAt int64 `json:"iat"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type json_out_token struct {
|
|
||||||
AccessToken string `json:"access-token"`
|
|
||||||
RefreshToken string `json:"refresh-token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctl *server_ctl_token) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
|
func (ctl *server_ctl_token) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
|
||||||
var s *Server
|
var s *Server
|
||||||
var status_code int
|
var status_code int
|
||||||
@ -125,8 +132,8 @@ func (ctl *server_ctl_token) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
|||||||
|
|
||||||
switch req.Method {
|
switch req.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
var jwt JWT
|
var jwt *JWT[ServerTokenClaim]
|
||||||
var jc ServerTokenClaim
|
var claim ServerTokenClaim
|
||||||
var tok string
|
var tok string
|
||||||
var now time.Time
|
var now time.Time
|
||||||
|
|
||||||
@ -136,9 +143,10 @@ func (ctl *server_ctl_token) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
now = time.Now()
|
now = time.Now()
|
||||||
jc.IssuedAt = now.Unix()
|
claim.IssuedAt = now.Unix()
|
||||||
jc.ExpiresAt = now.Add(s.cfg.CtlAuth.TokenTtl).Unix()
|
claim.ExpiresAt = now.Add(s.cfg.CtlAuth.TokenTtl).Unix()
|
||||||
tok, err = jwt.Sign(&jc)
|
jwt = NewJWT(s.cfg.CtlAuth.TokenRsaKey, &claim)
|
||||||
|
tok, err = jwt.SignRS512()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status_code = WriteJsonRespHeader(w, http.StatusInternalServerError)
|
status_code = WriteJsonRespHeader(w, http.StatusInternalServerError)
|
||||||
je.Encode(JsonErrmsg{Text: err.Error()})
|
je.Encode(JsonErrmsg{Text: err.Error()})
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package hodu
|
package hodu
|
||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
import "crypto/rsa"
|
||||||
import "crypto/tls"
|
import "crypto/tls"
|
||||||
import "errors"
|
import "errors"
|
||||||
import "fmt"
|
import "fmt"
|
||||||
@ -49,6 +50,7 @@ type ServerAuthConfig struct {
|
|||||||
Realm string
|
Realm string
|
||||||
Creds ServerAuthCredMap
|
Creds ServerAuthCredMap
|
||||||
TokenTtl time.Duration
|
TokenTtl time.Duration
|
||||||
|
TokenRsaKey *rsa.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user