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