1package jwt_test
2
3// Example HTTP auth using asymmetric crypto/RSA keys
4// This is based on a (now outdated) example at https://gist.github.com/cryptix/45c33ecf0ae54828e63b
5
6import (
7	"bytes"
8	"crypto/rsa"
9	"fmt"
10	"io"
11	"io/ioutil"
12	"log"
13	"net"
14	"net/http"
15	"net/url"
16	"strings"
17	"time"
18
19	"github.com/golang-jwt/jwt"
20	"github.com/golang-jwt/jwt/request"
21)
22
23// location of the files used for signing and verification
24const (
25	privKeyPath = "test/sample_key"     // openssl genrsa -out app.rsa keysize
26	pubKeyPath  = "test/sample_key.pub" // openssl rsa -in app.rsa -pubout > app.rsa.pub
27)
28
29var (
30	verifyKey  *rsa.PublicKey
31	signKey    *rsa.PrivateKey
32	serverPort int
33	// storing sample username/password pairs
34	// don't do this on a real server
35	users = map[string]string{
36		"test": "known",
37	}
38)
39
40// read the key files before starting http handlers
41func init() {
42	signBytes, err := ioutil.ReadFile(privKeyPath)
43	fatal(err)
44
45	signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
46	fatal(err)
47
48	verifyBytes, err := ioutil.ReadFile(pubKeyPath)
49	fatal(err)
50
51	verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
52	fatal(err)
53
54	http.HandleFunc("/authenticate", authHandler)
55	http.HandleFunc("/restricted", restrictedHandler)
56
57	// Setup listener
58	listener, err := net.ListenTCP("tcp", &net.TCPAddr{})
59	fatal(err)
60	serverPort = listener.Addr().(*net.TCPAddr).Port
61
62	log.Println("Listening...")
63	go func() {
64		fatal(http.Serve(listener, nil))
65	}()
66}
67
68var start func()
69
70func fatal(err error) {
71	if err != nil {
72		log.Fatal(err)
73	}
74}
75
76// Define some custom types were going to use within our tokens
77type CustomerInfo struct {
78	Name string
79	Kind string
80}
81
82type CustomClaimsExample struct {
83	*jwt.StandardClaims
84	TokenType string
85	CustomerInfo
86}
87
88func Example_getTokenViaHTTP() {
89	// See func authHandler for an example auth handler that produces a token
90	res, err := http.PostForm(fmt.Sprintf("http://localhost:%v/authenticate", serverPort), url.Values{
91		"user": {"test"},
92		"pass": {"known"},
93	})
94	if err != nil {
95		fatal(err)
96	}
97
98	if res.StatusCode != 200 {
99		fmt.Println("Unexpected status code", res.StatusCode)
100	}
101
102	// Read the token out of the response body
103	buf := new(bytes.Buffer)
104	io.Copy(buf, res.Body)
105	res.Body.Close()
106	tokenString := strings.TrimSpace(buf.String())
107
108	// Parse the token
109	token, err := jwt.ParseWithClaims(tokenString, &CustomClaimsExample{}, func(token *jwt.Token) (interface{}, error) {
110		// since we only use the one private key to sign the tokens,
111		// we also only use its public counter part to verify
112		return verifyKey, nil
113	})
114	fatal(err)
115
116	claims := token.Claims.(*CustomClaimsExample)
117	fmt.Println(claims.CustomerInfo.Name)
118
119	//Output: test
120}
121
122func Example_useTokenViaHTTP() {
123
124	// Make a sample token
125	// In a real world situation, this token will have been acquired from
126	// some other API call (see Example_getTokenViaHTTP)
127	token, err := createToken("foo")
128	fatal(err)
129
130	// Make request.  See func restrictedHandler for example request processor
131	req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost:%v/restricted", serverPort), nil)
132	fatal(err)
133	req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
134	res, err := http.DefaultClient.Do(req)
135	fatal(err)
136
137	// Read the response body
138	buf := new(bytes.Buffer)
139	io.Copy(buf, res.Body)
140	res.Body.Close()
141	fmt.Println(buf.String())
142
143	// Output: Welcome, foo
144}
145
146func createToken(user string) (string, error) {
147	// create a signer for rsa 256
148	t := jwt.New(jwt.GetSigningMethod("RS256"))
149
150	// set our claims
151	t.Claims = &CustomClaimsExample{
152		&jwt.StandardClaims{
153			// set the expire time
154			// see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20#section-4.1.4
155			ExpiresAt: time.Now().Add(time.Minute * 1).Unix(),
156		},
157		"level1",
158		CustomerInfo{user, "human"},
159	}
160
161	// Creat token string
162	return t.SignedString(signKey)
163}
164
165// reads the form values, checks them and creates the token
166func authHandler(w http.ResponseWriter, r *http.Request) {
167	// make sure its post
168	if r.Method != "POST" {
169		w.WriteHeader(http.StatusBadRequest)
170		fmt.Fprintln(w, "No POST", r.Method)
171		return
172	}
173
174	user := r.FormValue("user")
175	pass := r.FormValue("pass")
176
177	log.Printf("Authenticate: user[%s] pass[%s]\n", user, pass)
178
179	// check values
180	if user != "test" || pass != "known" {
181		w.WriteHeader(http.StatusForbidden)
182		fmt.Fprintln(w, "Wrong info")
183		return
184	}
185
186	tokenString, err := createToken(user)
187	if err != nil {
188		w.WriteHeader(http.StatusInternalServerError)
189		fmt.Fprintln(w, "Sorry, error while Signing Token!")
190		log.Printf("Token Signing error: %v\n", err)
191		return
192	}
193
194	w.Header().Set("Content-Type", "application/jwt")
195	w.WriteHeader(http.StatusOK)
196	fmt.Fprintln(w, tokenString)
197}
198
199// only accessible with a valid token
200func restrictedHandler(w http.ResponseWriter, r *http.Request) {
201	// Get token from request
202	token, err := request.ParseFromRequestWithClaims(r, request.OAuth2Extractor, &CustomClaimsExample{}, func(token *jwt.Token) (interface{}, error) {
203		// since we only use the one private key to sign the tokens,
204		// we also only use its public counter part to verify
205		return verifyKey, nil
206	})
207
208	// If the token is missing or invalid, return error
209	if err != nil {
210		w.WriteHeader(http.StatusUnauthorized)
211		fmt.Fprintln(w, "Invalid token:", err)
212		return
213	}
214
215	// Token is valid
216	fmt.Fprintln(w, "Welcome,", token.Claims.(*CustomClaimsExample).Name)
217	return
218}
219