edit: jwt
add: VerifyOption
edit: Verify
This commit is contained in:
parent
a65a3fe0b3
commit
876df38907
30
jwt/jwt.go
30
jwt/jwt.go
@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.daebt.dev/auth/algo"
|
"git.daebt.dev/auth/algo"
|
||||||
)
|
)
|
||||||
@ -24,6 +25,11 @@ var (
|
|||||||
//
|
//
|
||||||
ErrNotJWTType = errors.New("token of not JWT type")
|
ErrNotJWTType = errors.New("token of not JWT type")
|
||||||
ErrAlgorithmMismatch = errors.New("token is signed by another algorithm")
|
ErrAlgorithmMismatch = errors.New("token is signed by another algorithm")
|
||||||
|
//
|
||||||
|
Err_1 = errors.New("заданного владельца(ов) не существует")
|
||||||
|
Err_2 = errors.New("заданного aud не существует")
|
||||||
|
ErrExpired = errors.New("token expired")
|
||||||
|
ErrNotActivated = errors.New("token yet not activated")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
@ -80,7 +86,7 @@ func (t *Token) decodeSegment(str string, val any) error {
|
|||||||
return json.Unmarshal(buf, val)
|
return json.Unmarshal(buf, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) Verify(a algo.Algorithm) error {
|
func (t *Token) Verify(a algo.Algorithm, o ...VerifyOption) error {
|
||||||
if a == nil {
|
if a == nil {
|
||||||
return ErrAlgorithmNil
|
return ErrAlgorithmNil
|
||||||
}
|
}
|
||||||
@ -91,6 +97,28 @@ func (t *Token) Verify(a algo.Algorithm) error {
|
|||||||
return ErrAlgorithmMismatch
|
return ErrAlgorithmMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, err := t.Payload.GetExpirationTime(); err != nil && !errors.Is(err, ErrKeyNotExist) {
|
||||||
|
return err
|
||||||
|
} else if !errors.Is(err, ErrKeyNotExist) {
|
||||||
|
if time.Now().After(val) {
|
||||||
|
return ErrExpired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, err := t.Payload.GetNotBefore(); err != nil && !errors.Is(err, ErrKeyNotExist) {
|
||||||
|
return err
|
||||||
|
} else if !errors.Is(err, ErrKeyNotExist) {
|
||||||
|
if time.Now().Before(val) {
|
||||||
|
return ErrNotActivated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range o {
|
||||||
|
if err := f(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return a.Verify(t.body, t.sign)
|
return a.Verify(t.body, t.sign)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
146
jwt/jwt_test.go
146
jwt/jwt_test.go
@ -1,6 +1,7 @@
|
|||||||
package jwt_test
|
package jwt_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -47,89 +48,92 @@ func TestCreate(t *testing.T) {
|
|||||||
|
|
||||||
tm := time.Now()
|
tm := time.Now()
|
||||||
|
|
||||||
var data = [][]jwt.Option{
|
var data = []*struct {
|
||||||
|
o []jwt.Option
|
||||||
|
v []jwt.VerifyOption
|
||||||
|
e error
|
||||||
|
}{
|
||||||
{
|
{
|
||||||
jwt.WithHeaderKeyId([]byte("019451f1-f789-72a6-836d-3bc6146ad76a")),
|
[]jwt.Option{
|
||||||
jwt.WithIssuer("https://git.daebt.dev"),
|
jwt.WithIssuer("https://git.daebt.dev"),
|
||||||
jwt.WithAudience("https://0.example.com"),
|
},
|
||||||
jwt.WithSubject("example:0"),
|
nil,
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
jwt.WithHeaderKeyId([]byte("019451f1-f789-72a6-836d-3bc6146ad76a")),
|
[]jwt.Option{
|
||||||
jwt.WithIssuer("https://git.daebt.dev"),
|
jwt.WithIssuer("https://git.daebt.dev"),
|
||||||
jwt.WithAudience("https://1.example.com"),
|
jwt.WithIssuedAt(tm),
|
||||||
jwt.WithSubject("example:1"),
|
jwt.WithExpirationTime(tm.Add(time.Hour)),
|
||||||
jwt.WithIssuedAt(tm),
|
},
|
||||||
jwt.WithExpirationTime(tm.Add(time.Hour)),
|
nil,
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
jwt.WithHeaderKeyId([]byte("019451f1-f789-72a6-2911-3bc6146ad76a")),
|
[]jwt.Option{
|
||||||
jwt.WithIssuer("https://git.daebt.dev"),
|
jwt.WithIssuer("https://git.daebt.dev"),
|
||||||
jwt.WithAudience("https://2.example.com"),
|
jwt.WithIssuedAt(tm),
|
||||||
jwt.WithSubject("example:2"),
|
jwt.WithExpirationTime(tm.Add(-time.Hour)),
|
||||||
jwt.WithIssuedAt(tm),
|
},
|
||||||
jwt.WithNotBefore(tm),
|
nil,
|
||||||
|
jwt.ErrExpired,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
jwt.WithHeaderKeyId([]byte("019451f1-f789-72a6-2911-3bc6146ad76a")),
|
[]jwt.Option{
|
||||||
jwt.WithIssuer("https://git.daebt.dev"),
|
jwt.WithIssuer("https://git.daebt.dev"),
|
||||||
jwt.WithAudience("https://3.example.com"),
|
jwt.WithIssuedAt(tm),
|
||||||
jwt.WithSubject("example:3"),
|
jwt.WithNotBefore(tm.Add(time.Hour)),
|
||||||
jwt.WithIssuedAt(tm),
|
},
|
||||||
jwt.WithNotBefore(tm.Add(time.Minute)),
|
nil,
|
||||||
|
jwt.ErrNotActivated,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
jwt.WithHeaderKeyId([]byte("019451f1-f789-72a6-2911-3bc6146ad76a")),
|
[]jwt.Option{
|
||||||
jwt.WithIssuer("https://git.daebt.dev"),
|
jwt.WithIssuer("https://git.daebt.dev"),
|
||||||
jwt.WithAudience("https://4.example.com"),
|
jwt.WithIssuedAt(tm),
|
||||||
jwt.WithSubject("example:4"),
|
},
|
||||||
jwt.WithIssuedAt(tm),
|
[]jwt.VerifyOption{
|
||||||
jwt.WithNotBefore(tm.Add(time.Minute)),
|
jwt.WithVerifyIssuer("https://git.daebt.dev"),
|
||||||
jwt.WithExpirationTime(tm.Add(time.Hour)),
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]jwt.Option{
|
||||||
|
jwt.WithIssuedAt(tm),
|
||||||
|
},
|
||||||
|
[]jwt.VerifyOption{
|
||||||
|
jwt.WithVerifyIssuer(),
|
||||||
|
},
|
||||||
|
jwt.ErrKeyNotExist,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]jwt.Option{
|
||||||
|
jwt.WithIssuer("https://git.daebt.dev"),
|
||||||
|
jwt.WithIssuedAt(tm),
|
||||||
|
},
|
||||||
|
[]jwt.VerifyOption{
|
||||||
|
jwt.WithVerifyIssuer(),
|
||||||
|
},
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range data {
|
for i, v := range data {
|
||||||
// t.Run(fmt.Sprint(i), func(t *testing.T) {
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
val, err := jwt.New(v...).Sign(alg)
|
val, err := jwt.New(v.o...).Sign(alg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
// t.Log(val)
|
|
||||||
fmt.Printf(`"%s",`, val)
|
tkn, err := jwt.Parse(val)
|
||||||
// })
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tkn.Verify(alg, v.v...); !errors.Is(err, v.e) {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestJwt(t *testing.T) {
|
|
||||||
// alg, err := rs.NewRS256(
|
|
||||||
// rs.WithGenerateKey(2048),
|
|
||||||
// )
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatal(err.Error())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// tkn := New(
|
|
||||||
// WithSubject("main-jwt-token"),
|
|
||||||
// )
|
|
||||||
|
|
||||||
// str, err := tkn.Sign(alg)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatal(err.Error())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// tkn, err = Parse(str)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatal(err.Error())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if err := tkn.Verify(alg); err != nil {
|
|
||||||
// t.Fatal(err.Error())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// tkn.Payload.Range(func(key string, val any) bool {
|
|
||||||
// t.Log(key, val)
|
|
||||||
// return true
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|||||||
@ -39,21 +39,22 @@ func WithAudience(aud ...string) Option {
|
|||||||
// WithExpirationTime устанавливает время истечения срока действия, по истечении которого JWT НЕ ДОЛЖЕН быть принят к обработке
|
// WithExpirationTime устанавливает время истечения срока действия, по истечении которого JWT НЕ ДОЛЖЕН быть принят к обработке
|
||||||
func WithExpirationTime(exp time.Time) Option {
|
func WithExpirationTime(exp time.Time) Option {
|
||||||
return func(t *Token) {
|
return func(t *Token) {
|
||||||
t.Payload.AppendArg("exp", exp)
|
|
||||||
|
t.Payload.AppendArg("exp", exp.Unix())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithNotBefore устанавливает время, до которого JWT НЕ ДОЛЖЕН быть принят к обработке
|
// WithNotBefore устанавливает время, до которого JWT НЕ ДОЛЖЕН быть принят к обработке
|
||||||
func WithNotBefore(nbf time.Time) Option {
|
func WithNotBefore(nbf time.Time) Option {
|
||||||
return func(t *Token) {
|
return func(t *Token) {
|
||||||
t.Payload.AppendArg("nbf", nbf)
|
t.Payload.AppendArg("nbf", nbf.Unix())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithIssuedAt устанавливает время, когда был создан JWT
|
// WithIssuedAt устанавливает время, когда был создан JWT
|
||||||
func WithIssuedAt(iat time.Time) Option {
|
func WithIssuedAt(iat time.Time) Option {
|
||||||
return func(t *Token) {
|
return func(t *Token) {
|
||||||
t.Payload.AppendArg("iat", iat)
|
t.Payload.AppendArg("iat", iat.Unix())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,3 +89,50 @@ func WithHeaderKeyId(kid []byte) Option {
|
|||||||
t.Header.AppendArg("kid", base64.RawURLEncoding.EncodeToString(kid))
|
t.Header.AppendArg("kid", base64.RawURLEncoding.EncodeToString(kid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VerifyOption func(*Token) error
|
||||||
|
|
||||||
|
// WithVerifyIssuer проверяет существуют ли заданные владельцы
|
||||||
|
func WithVerifyIssuer(iss ...string) VerifyOption {
|
||||||
|
return func(t *Token) error {
|
||||||
|
val, err := t.Payload.GetIssuer()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(iss) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range iss {
|
||||||
|
if v == val {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err_1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithVerifyAudience(aud ...string) VerifyOption {
|
||||||
|
return func(t *Token) error {
|
||||||
|
val, err := t.Payload.GetAudience()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(aud) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range aud {
|
||||||
|
for _, v := range val {
|
||||||
|
if a == v {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err_2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user