...

Source file src/github.com/golang-jwt/jwt/v4/http_example_test.go

Documentation: github.com/golang-jwt/jwt/v4

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

View as plain text