1// A useful example app.  You can use this to debug your tokens on the command line.
2// This is also a great place to look at how you might use this library.
3//
4// Example usage:
5// The following will create and sign a token, then verify it and output the original claims.
6//     echo {\"foo\":\"bar\"} | bin/jwt -key test/sample_key -alg RS256 -sign - | bin/jwt -key test/sample_key.pub -verify -
7package main
8
9import (
10	"encoding/json"
11	"flag"
12	"fmt"
13	"io"
14	"io/ioutil"
15	"os"
16	"regexp"
17	"strings"
18
19	jwt "github.com/dgrijalva/jwt-go"
20)
21
22var (
23	// Options
24	flagAlg     = flag.String("alg", "", "signing algorithm identifier")
25	flagKey     = flag.String("key", "", "path to key file or '-' to read from stdin")
26	flagCompact = flag.Bool("compact", false, "output compact JSON")
27	flagDebug   = flag.Bool("debug", false, "print out all kinds of debug data")
28	flagClaims  = make(ArgList)
29	flagHead    = make(ArgList)
30
31	// Modes - exactly one of these is required
32	flagSign   = flag.String("sign", "", "path to claims object to sign, '-' to read from stdin, or '+' to use only -claim args")
33	flagVerify = flag.String("verify", "", "path to JWT token to verify or '-' to read from stdin")
34	flagShow   = flag.String("show", "", "path to JWT file or '-' to read from stdin")
35)
36
37func main() {
38	// Plug in Var flags
39	flag.Var(flagClaims, "claim", "add additional claims. may be used more than once")
40	flag.Var(flagHead, "header", "add additional header params. may be used more than once")
41
42	// Usage message if you ask for -help or if you mess up inputs.
43	flag.Usage = func() {
44		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
45		fmt.Fprintf(os.Stderr, "  One of the following flags is required: sign, verify\n")
46		flag.PrintDefaults()
47	}
48
49	// Parse command line options
50	flag.Parse()
51
52	// Do the thing.  If something goes wrong, print error to stderr
53	// and exit with a non-zero status code
54	if err := start(); err != nil {
55		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
56		os.Exit(1)
57	}
58}
59
60// Figure out which thing to do and then do that
61func start() error {
62	if *flagSign != "" {
63		return signToken()
64	} else if *flagVerify != "" {
65		return verifyToken()
66	} else if *flagShow != "" {
67		return showToken()
68	} else {
69		flag.Usage()
70		return fmt.Errorf("None of the required flags are present.  What do you want me to do?")
71	}
72}
73
74// Helper func:  Read input from specified file or stdin
75func loadData(p string) ([]byte, error) {
76	if p == "" {
77		return nil, fmt.Errorf("No path specified")
78	}
79
80	var rdr io.Reader
81	if p == "-" {
82		rdr = os.Stdin
83	} else if p == "+" {
84		return []byte("{}"), nil
85	} else {
86		if f, err := os.Open(p); err == nil {
87			rdr = f
88			defer f.Close()
89		} else {
90			return nil, err
91		}
92	}
93	return ioutil.ReadAll(rdr)
94}
95
96// Print a json object in accordance with the prophecy (or the command line options)
97func printJSON(j interface{}) error {
98	var out []byte
99	var err error
100
101	if *flagCompact == false {
102		out, err = json.MarshalIndent(j, "", "    ")
103	} else {
104		out, err = json.Marshal(j)
105	}
106
107	if err == nil {
108		fmt.Println(string(out))
109	}
110
111	return err
112}
113
114// Verify a token and output the claims.  This is a great example
115// of how to verify and view a token.
116func verifyToken() error {
117	// get the token
118	tokData, err := loadData(*flagVerify)
119	if err != nil {
120		return fmt.Errorf("Couldn't read token: %v", err)
121	}
122
123	// trim possible whitespace from token
124	tokData = regexp.MustCompile(`\s*$`).ReplaceAll(tokData, []byte{})
125	if *flagDebug {
126		fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData))
127	}
128
129	// Parse the token.  Load the key from command line option
130	token, err := jwt.Parse(string(tokData), func(t *jwt.Token) (interface{}, error) {
131		data, err := loadData(*flagKey)
132		if err != nil {
133			return nil, err
134		}
135		if isEs() {
136			return jwt.ParseECPublicKeyFromPEM(data)
137		} else if isRs() {
138			return jwt.ParseRSAPublicKeyFromPEM(data)
139		}
140		return data, nil
141	})
142
143	// Print some debug data
144	if *flagDebug && token != nil {
145		fmt.Fprintf(os.Stderr, "Header:\n%v\n", token.Header)
146		fmt.Fprintf(os.Stderr, "Claims:\n%v\n", token.Claims)
147	}
148
149	// Print an error if we can't parse for some reason
150	if err != nil {
151		return fmt.Errorf("Couldn't parse token: %v", err)
152	}
153
154	// Is token invalid?
155	if !token.Valid {
156		return fmt.Errorf("Token is invalid")
157	}
158
159	// Print the token details
160	if err := printJSON(token.Claims); err != nil {
161		return fmt.Errorf("Failed to output claims: %v", err)
162	}
163
164	return nil
165}
166
167// Create, sign, and output a token.  This is a great, simple example of
168// how to use this library to create and sign a token.
169func signToken() error {
170	// get the token data from command line arguments
171	tokData, err := loadData(*flagSign)
172	if err != nil {
173		return fmt.Errorf("Couldn't read token: %v", err)
174	} else if *flagDebug {
175		fmt.Fprintf(os.Stderr, "Token: %v bytes", len(tokData))
176	}
177
178	// parse the JSON of the claims
179	var claims jwt.MapClaims
180	if err := json.Unmarshal(tokData, &claims); err != nil {
181		return fmt.Errorf("Couldn't parse claims JSON: %v", err)
182	}
183
184	// add command line claims
185	if len(flagClaims) > 0 {
186		for k, v := range flagClaims {
187			claims[k] = v
188		}
189	}
190
191	// get the key
192	var key interface{}
193	key, err = loadData(*flagKey)
194	if err != nil {
195		return fmt.Errorf("Couldn't read key: %v", err)
196	}
197
198	// get the signing alg
199	alg := jwt.GetSigningMethod(*flagAlg)
200	if alg == nil {
201		return fmt.Errorf("Couldn't find signing method: %v", *flagAlg)
202	}
203
204	// create a new token
205	token := jwt.NewWithClaims(alg, claims)
206
207	// add command line headers
208	if len(flagHead) > 0 {
209		for k, v := range flagHead {
210			token.Header[k] = v
211		}
212	}
213
214	if isEs() {
215		if k, ok := key.([]byte); !ok {
216			return fmt.Errorf("Couldn't convert key data to key")
217		} else {
218			key, err = jwt.ParseECPrivateKeyFromPEM(k)
219			if err != nil {
220				return err
221			}
222		}
223	} else if isRs() {
224		if k, ok := key.([]byte); !ok {
225			return fmt.Errorf("Couldn't convert key data to key")
226		} else {
227			key, err = jwt.ParseRSAPrivateKeyFromPEM(k)
228			if err != nil {
229				return err
230			}
231		}
232	}
233
234	if out, err := token.SignedString(key); err == nil {
235		fmt.Println(out)
236	} else {
237		return fmt.Errorf("Error signing token: %v", err)
238	}
239
240	return nil
241}
242
243// showToken pretty-prints the token on the command line.
244func showToken() error {
245	// get the token
246	tokData, err := loadData(*flagShow)
247	if err != nil {
248		return fmt.Errorf("Couldn't read token: %v", err)
249	}
250
251	// trim possible whitespace from token
252	tokData = regexp.MustCompile(`\s*$`).ReplaceAll(tokData, []byte{})
253	if *flagDebug {
254		fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData))
255	}
256
257	token, err := jwt.Parse(string(tokData), nil)
258	if token == nil {
259		return fmt.Errorf("malformed token: %v", err)
260	}
261
262	// Print the token details
263	fmt.Println("Header:")
264	if err := printJSON(token.Header); err != nil {
265		return fmt.Errorf("Failed to output header: %v", err)
266	}
267
268	fmt.Println("Claims:")
269	if err := printJSON(token.Claims); err != nil {
270		return fmt.Errorf("Failed to output claims: %v", err)
271	}
272
273	return nil
274}
275
276func isEs() bool {
277	return strings.HasPrefix(*flagAlg, "ES")
278}
279
280func isRs() bool {
281	return strings.HasPrefix(*flagAlg, "RS")
282}
283