-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnegroniJWT.go
190 lines (175 loc) · 4.91 KB
/
negroniJWT.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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package negroniJWT
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"os"
"io/ioutil"
jwt "github.com/dgrijalva/jwt-go"
"github.com/docker/libtrust"
"github.com/gorilla/context"
"math/big"
"net/http"
"strings"
"sync"
"time"
"fmt"
)
const (
context_key = "jwt"
)
var (
once sync.Once
privKeyPEMEncoded []byte
pubKeyPEMEncoded []byte
privateKeyPath string
publicKeyPath string
pubKeyId string
privKey *rsa.PrivateKey
failRequest bool
)
func Init(alwaysFailRequest bool, privKeyPath, pubKeyPath string) {
privateKeyPath = privKeyPath
publicKeyPath = pubKeyPath
_, privKeyError := os.Stat(privateKeyPath)
_, pubKeyError := os.Stat(publicKeyPath)
failRequest = alwaysFailRequest
if os.IsNotExist(privKeyError) || os.IsNotExist(pubKeyError) {
fmt.Println("[negroniJWT] No Keys found, generating RS256 private and public keys")
once.Do(generateKeys)
} else {
fmt.Println("[negroniJWT] Loading keys")
once.Do(loadKeys)
}
}
func writePEMToFile(filename string, b *pem.Block) {
f, err := os.OpenFile(filename, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0777)
if err != nil {
panic(err)
}
err = pem.Encode(f, b)
if err != nil {
panic(err)
}
}
func loadKeys() {
privBytes, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
panic(err)
}
pubBytes, err := ioutil.ReadFile(publicKeyPath)
if err != nil {
panic(err)
}
privKeyPEMEncodedBytes, _ := pem.Decode(privBytes)
privKeyPEMEncoded = pem.EncodeToMemory(privKeyPEMEncodedBytes)
pubKeyPEMEncodedBytes, _ := pem.Decode(pubBytes)
pubKeyPEMEncoded = pem.EncodeToMemory(pubKeyPEMEncodedBytes)
}
func generateKeys() {
var err error
privKey, err = rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
panic(err)
}
privateKeyPEMBlock := &pem.Block{
Type: "PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privKey),
}
writePEMToFile(privateKeyPath, privateKeyPEMBlock)
privKeyPEMEncoded = pem.EncodeToMemory(privateKeyPEMBlock)
pubANS1, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey)
if err != nil {
panic(err)
}
publicKeyPEMBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: pubANS1,
}
writePEMToFile(publicKeyPath, publicKeyPEMBlock)
pubKeyPEMEncoded = pem.EncodeToMemory(publicKeyPEMBlock)
libTrustPubKey, err := libtrust.UnmarshalPublicKeyPEM(pubKeyPEMEncoded)
if err != nil {
panic(err)
}
pubKeyId = libTrustPubKey.KeyID()
}
func Bundle() (p []byte, err error) {
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "Σ Acme Co",
},
NotBefore: time.Unix(1000, 0),
NotAfter: time.Unix(100000, 0),
SubjectKeyId: []byte(pubKeyId),
BasicConstraintsValid: true,
IsCA: false,
}
out, err := x509.CreateCertificate(rand.Reader, template, template, &privKey.PublicKey, privKey)
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(nil)
if err = pem.Encode(buf, &pem.Block{
Type: "CERTIFICATE",
Bytes: out,
}); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func getClaims(token string) (claims map[string]interface{}, err error) {
parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return jwt.ParseRSAPublicKeyFromPEM(pubKeyPEMEncoded)
})
if err != nil {
return nil, err
}
if parsedToken.Valid {
return parsedToken.Claims.(jwt.MapClaims), nil
}
return nil, errors.New("Token is invalid")
}
// Middleware the main middleware function. Use with negroni.HandlerFunc to apply middleware like so :
// n := negroni.Classic()
// n.Use(negroni.HandlerFunc(negroniJWT.Middleware))
func Middleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
var authToken string = strings.Replace(r.Header.Get("Authorization"), "Bearer ", "", 1)
claims, err := getClaims(authToken)
if err == nil || failRequest == false {
if err == nil {
context.Set(r, context_key, claims)
}
next(rw, r)
} else {
rw.WriteHeader(401)
}
}
// GenerateToken generates the base64 encoded JSON Web Token including the claims map provided and and expiration time.
func GenerateToken(claims map[string]interface{}, expiration time.Time) (s string, err error) {
t := jwt.New(jwt.GetSigningMethod("RS256"))
t.Header["typ"] = "JWT"
t.Header["alg"] = "RS256"
t.Header["kid"] = pubKeyId
claims["exp"] = expiration.Unix()
t.Claims = jwt.MapClaims(claims)
privKey, err := jwt.ParseRSAPrivateKeyFromPEM(privKeyPEMEncoded)
if err != nil {
return "", err
}
return t.SignedString(privKey)
}
// Get attempts to retrieve the claims map for request. If there was an error decoding the JSON Web Token.
func Get(r *http.Request) (claims map[string]interface{}, ok bool) {
c, ok := context.GetOk(r, context_key)
if !ok {
return claims, ok
}
claims, castOk := c.(map[string]interface{})
return claims, castOk
}