- Notifications
You must be signed in to change notification settings - Fork 371
/
Copy pathhttp_example_test.go
206 lines (169 loc) · 5.11 KB
/
http_example_test.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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package jwt_test
// Example HTTP auth using asymmetric crypto/RSA keys
// This is based on a (now outdated) example at https://gist.github.com/cryptix/45c33ecf0ae54828e63b
import (
"crypto/rsa"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5/request"
)
// location of the files used for signing and verification
const (
privKeyPath="test/sample_key"// openssl genrsa -out app.rsa keysize
pubKeyPath="test/sample_key.pub"// openssl rsa -in app.rsa -pubout > app.rsa.pub
)
var (
verifyKey*rsa.PublicKey
signKey*rsa.PrivateKey
serverPortint
)
// read the key files before starting http handlers
funcinit() {
signBytes, err:=os.ReadFile(privKeyPath)
fatal(err)
signKey, err=jwt.ParseRSAPrivateKeyFromPEM(signBytes)
fatal(err)
verifyBytes, err:=os.ReadFile(pubKeyPath)
fatal(err)
verifyKey, err=jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
fatal(err)
http.HandleFunc("/authenticate", authHandler)
http.HandleFunc("/restricted", restrictedHandler)
// Setup listener
listener, err:=net.ListenTCP("tcp", &net.TCPAddr{})
fatal(err)
serverPort=listener.Addr().(*net.TCPAddr).Port
log.Println("Listening...")
gofunc() {
fatal(http.Serve(listener, nil))
}()
}
funcfatal(errerror) {
iferr!=nil {
log.Fatal(err)
}
}
// Define some custom types were going to use within our tokens
typeCustomerInfostruct {
Namestring
Kindstring
}
typeCustomClaimsExamplestruct {
jwt.RegisteredClaims
TokenTypestring
CustomerInfo
}
funcExample_getTokenViaHTTP() {
// See func authHandler for an example auth handler that produces a token
res, err:=http.PostForm(fmt.Sprintf("http://localhost:%v/authenticate", serverPort), url.Values{
"user": {"test"},
"pass": {"known"},
})
fatal(err)
ifres.StatusCode!=200 {
fmt.Println("Unexpected status code", res.StatusCode)
}
// Read the token out of the response body
buf, err:=io.ReadAll(res.Body)
fatal(err)
_=res.Body.Close()
tokenString:=strings.TrimSpace(string(buf))
// Parse the token
token, err:=jwt.ParseWithClaims(tokenString, &CustomClaimsExample{}, func(token*jwt.Token) (interface{}, error) {
// since we only use the one private key to sign the tokens,
// we also only use its public counter part to verify
returnverifyKey, nil
})
fatal(err)
claims:=token.Claims.(*CustomClaimsExample)
fmt.Println(claims.Name)
// Output: test
}
funcExample_useTokenViaHTTP() {
// Make a sample token
// In a real world situation, this token will have been acquired from
// some other API call (see Example_getTokenViaHTTP)
token, err:=createToken("foo")
fatal(err)
// Make request. See func restrictedHandler for example request processor
req, err:=http.NewRequest("GET", fmt.Sprintf("http://localhost:%v/restricted", serverPort), nil)
fatal(err)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
res, err:=http.DefaultClient.Do(req)
fatal(err)
// Read the response body
buf, err:=io.ReadAll(res.Body)
fatal(err)
_=res.Body.Close()
fmt.Printf("%s", buf)
// Output: Welcome, foo
}
funccreateToken(userstring) (string, error) {
// create a signer for rsa 256
t:=jwt.New(jwt.GetSigningMethod("RS256"))
// set our claims
t.Claims=&CustomClaimsExample{
jwt.RegisteredClaims{
// set the expire time
// see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute*1)),
},
"level1",
CustomerInfo{user, "human"},
}
// Creat token string
returnt.SignedString(signKey)
}
// reads the form values, checks them and creates the token
funcauthHandler(w http.ResponseWriter, r*http.Request) {
// make sure its post
ifr.Method!="POST" {
w.WriteHeader(http.StatusBadRequest)
_, _=fmt.Fprintln(w, "No POST", r.Method)
return
}
user:=r.FormValue("user")
pass:=r.FormValue("pass")
log.Printf("Authenticate: user[%s] pass[%s]\n", user, pass)
// check values
ifuser!="test"||pass!="known" {
w.WriteHeader(http.StatusForbidden)
_, _=fmt.Fprintln(w, "Wrong info")
return
}
tokenString, err:=createToken(user)
iferr!=nil {
w.WriteHeader(http.StatusInternalServerError)
_, _=fmt.Fprintln(w, "Sorry, error while Signing Token!")
log.Printf("Token Signing error: %v\n", err)
return
}
w.Header().Set("Content-Type", "application/jwt")
w.WriteHeader(http.StatusOK)
_, _=fmt.Fprintln(w, tokenString)
}
// only accessible with a valid token
funcrestrictedHandler(w http.ResponseWriter, r*http.Request) {
// Get token from request
token, err:=request.ParseFromRequest(r, request.OAuth2Extractor, func(token*jwt.Token) (interface{}, error) {
// since we only use the one private key to sign the tokens,
// we also only use its public counter part to verify
returnverifyKey, nil
}, request.WithClaims(&CustomClaimsExample{}))
// If the token is missing or invalid, return error
iferr!=nil {
w.WriteHeader(http.StatusUnauthorized)
_, _=fmt.Fprintln(w, "Invalid token:", err)
return
}
// Token is valid
_, _=fmt.Fprintln(w, "Welcome,", token.Claims.(*CustomClaimsExample).Name)
}