/
ssh_jwt.go
77 lines (70 loc) · 2.12 KB
/
ssh_jwt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package ssh_jwt
import (
"encoding/json"
"fmt"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/crypto/ssh/agent"
"time"
)
type signingMethodSSHAgent struct {
namespace string
agentClient agent.Agent
}
// SetupSigningMethod creates and registers a JWT signing method using SSH keys
func SetupSigningMethod(namespace string, a agent.Agent) (jwt.SigningMethod, error) {
sm := &signingMethodSSHAgent{
namespace: namespace,
agentClient: a,
}
jwt.RegisterSigningMethod(sm.Alg(), func() jwt.SigningMethod { return sm })
return sm, nil
}
func (s *signingMethodSSHAgent) Sign(signingString string, key interface{}) (string, error) {
keyStr, ok := key.(string)
if !ok {
return "", fmt.Errorf("key must be string")
}
return SignSSH(signingString, s.namespace, keyStr, s.agentClient)
}
func (s *signingMethodSSHAgent) Verify(signingString, signature string, key interface{}) error {
keyStr, ok := key.(string)
if !ok {
return fmt.Errorf("key must be string")
}
return VerifySSHSignature(signingString, signature, s.namespace, keyStr)
}
func (s *signingMethodSSHAgent) Alg() string {
return fmt.Sprintf("SSH-signature-%s", s.namespace)
}
// AuthorizeToken verifies a given token and, if successful, returns the token's public key and expiration time. A
// non-expiring token will have a nil expiration time.
func (s *signingMethodSSHAgent) AuthorizeToken(token string) (string, *time.Time, error) {
parsedToken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
pubkey, ok := t.Header["kid"]
if !ok {
return nil, fmt.Errorf("no pubkey")
}
return pubkey, nil
}, jwt.WithValidMethods([]string{s.Alg()}))
if err != nil {
return "", nil, err
}
signingKey, ok := parsedToken.Header["kid"].(string)
if !ok {
return "", nil, fmt.Errorf("signing key is not a string")
}
var expirationDate *time.Time
claims, ok := parsedToken.Claims.(jwt.MapClaims)
if ok {
switch iat := claims["exp"].(type) {
case float64:
tm := time.Unix(int64(iat), 0)
expirationDate = &tm
case json.Number:
v, _ := iat.Int64()
tm := time.Unix(v, 0)
expirationDate = &tm
}
}
return signingKey, expirationDate, nil
}