{"id":13408783,"url":"https://github.com/adam-hanna/jwt-auth","last_synced_at":"2026-01-12T10:14:57.563Z","repository":{"id":48318190,"uuid":"62676285","full_name":"adam-hanna/jwt-auth","owner":"adam-hanna","description":"This package provides json web token (jwt) middleware for goLang http servers","archived":false,"fork":false,"pushed_at":"2021-08-01T23:03:58.000Z","size":233,"stargazers_count":231,"open_issues_count":4,"forks_count":43,"subscribers_count":13,"default_branch":"develop","last_synced_at":"2024-07-31T20:32:04.786Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adam-hanna.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-05T23:31:43.000Z","updated_at":"2024-07-30T11:34:21.000Z","dependencies_parsed_at":"2022-09-21T11:10:24.704Z","dependency_job_id":null,"html_url":"https://github.com/adam-hanna/jwt-auth","commit_stats":null,"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-hanna%2Fjwt-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-hanna%2Fjwt-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-hanna%2Fjwt-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adam-hanna%2Fjwt-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adam-hanna","download_url":"https://codeload.github.com/adam-hanna/jwt-auth/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243584459,"owners_count":20314765,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:00:55.229Z","updated_at":"2026-01-12T10:14:57.554Z","avatar_url":"https://github.com/adam-hanna.png","language":"Go","funding_links":[],"categories":["Authentication and OAuth","身份验证和OAuth","Authentication and Authorization","Authentication \u0026 OAuth","认证和授权","認證和授權","认证和OAuth授权","Uncategorized","\u003cspan id=\"身份验证和oauth-authentication-and-auth\"\u003e身份验证和OAuth Authentication and Auth\u003c/span\u003e"],"sub_categories":["Contents"],"readme":"[![Build Status](https://travis-ci.org/adam-hanna/jwt-auth.svg?branch=master)](https://travis-ci.org/adam-hanna/jwt-auth) [![Coverage Status](https://coveralls.io/repos/github/adam-hanna/jwt-auth/badge.svg?branch=master)](https://coveralls.io/github/adam-hanna/jwt-auth?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/adam-hanna/jwt-auth)](https://goreportcard.com/report/github.com/adam-hanna/jwt-auth) [![GoDoc](https://godoc.org/github.com/adam-hanna/jwt-auth/jwt?status.svg)](https://godoc.org/github.com/adam-hanna/jwt-auth/jwt)\n\n# jwt-auth\njwt auth middleware in goLang.\n\nIf you're interested in using sessions, check out my [sessions library](https://github.com/adam-hanna/sessions)!\n\n**README Contents:**\n\n1. [Quickstart](https://github.com/adam-hanna/jwt-auth#quickstart)\n2. [Performance](https://github.com/adam-hanna/jwt-auth#performance)\n3. [Goals](https://github.com/adam-hanna/jwt-auth#goals)\n4. [Design](https://github.com/adam-hanna/jwt-auth#design)\n5. [API](https://github.com/adam-hanna/jwt-auth#api)\n6. [TODO](https://github.com/adam-hanna/jwt-auth#todo)\n7. [Test Coverage](https://github.com/adam-hanna/jwt-auth#test-coverage)\n8. [License](https://github.com/adam-hanna/jwt-auth#license)\n\n## Quickstart\n~~~ go\npackage main\n\nimport (\n  \"net/http\"\n  \"log\"\n  \"time\"\n\n  \"github.com/adam-hanna/jwt-auth/jwt\"\n)\n\nvar restrictedRoute jwt.Auth\n\nvar restrictedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n  w.Write([]byte(\"Welcome to the secret area!\"))\n})\n\nvar regularHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n  w.Write([]byte(\"Hello, World!\"))\n})\n\nfunc main() {\n  authErr := jwt.New(\u0026restrictedRoute, jwt.Options{\n    SigningMethodString:   \"RS256\",\n    PrivateKeyLocation:    \"keys/app.rsa\",     // `$ openssl genrsa -out app.rsa 2048`\n    PublicKeyLocation:     \"keys/app.rsa.pub\", // `$ openssl rsa -in app.rsa -pubout \u003e app.rsa.pub`\n    RefreshTokenValidTime: 72 * time.Hour,\n    AuthTokenValidTime:    15 * time.Minute,\n    Debug:                 false,\n    IsDevEnv:              true,\n  })\n  if authErr != nil {\n    log.Println(\"Error initializing the JWT's!\")\n    log.Fatal(authErr)\n  }\n\n  http.HandleFunc(\"/\", regularHandler)\n  // this will never be available because we never issue tokens\n  // see login_logout example for how to provide tokens\n  http.Handle(\"/restricted\", restrictedRoute.Handler(restrictedHandler))\n\n  log.Println(\"Listening on localhost:3000\")\n  http.ListenAndServe(\"127.0.0.1:3000\", nil)\n}\n~~~\n\n## Performance\nYMMV\n\n~~~ bash\n$ cd jwt \u0026\u0026 go test -bench=.\n\nBenchmarkBaseServer-2                        10000      137517 ns/op\nBenchmarkValidAuthTokenWithCookies-2          5000      303160 ns/op\nBenchmarkExpiredAuthTokenWithCookies-2        5000      323933 ns/op\nPASS\nok    github.com/adam-hanna/jwt-auth/jwt  15.463s\n~~~\n\n## Goals\nIt is important to understand the objective of this auth architecture. It certainly is not an applicable design for all use cases. Please read and understand the goals, below, and make changes to your own workflow to suit your specific needs.\n\n1. Protection of non-critical API's (e.g. not meant for financial, healthcare, gov't, etc. services)\n2. Stateless\n3. User sessions\n4. CSRF protection\n5. Web and/or mobile\n\n## Design\nThe design of this auth system is based on the three major components, listed below.\n\n1. Short-lived (minutes) JWT Auth Token\n2. Longer-lived (hours/days) JWT Refresh Token\n3. CSRF secret string\n\n### 1. Short-lived (minutes) JWT Auth Token\nThe short-lived jwt auth token allows the user to make stateless requests to protected API endpoints. It has an expiration time of 15 minutes by default and will be refreshed by the longer-lived refresh token.\n\n### 2. Longer-lived (hours/days) JWT Refresh Token\nThis longer-lived token will be used to update the auth tokens. These tokens have a 72 hour expiration time by default which will be updated each time an auth token is refreshed.\n\nThese refresh tokens contain an id that can be revoked by an authorized client.\n\n### 3. CSRF Secret String\nA CSRF secret string will be provided to each client and will be identical to the CSRF secret in the auth and refresh tokens and will change each time an auth token is refreshed. These secrets will live in an \"X-CSRF-Token\" response header, by default, but the header key can be set as an option. These secrets will be sent along with the auth and refresh tokens on each API request. \n\nWhen requests are made to a protected endpoint, this CSRF secret needs to be sent to the server either as a hidden form value with a name of \"X-CSRF-Token\", in the request header with the key of \"X-CSRF-Token\", or in the \"Authorization\" request header with a value of \"Bearer \" + token. This secret will be checked against the secret provided in the auth token in order to prevent CSRF attacks. It will be refreshed each time the auth token is refreshed from the refresh token.\n\n## Cookies or Bearer Tokens?\nThis API is set up to either use cookies (default) or bearer tokens. To use bearer tokens, set the BearerTokens option equal to true in the config settings.\n\nWhen using bearer tokens, you'll need to include the auth and (optionally [the]) refresh jwt's (along with your csrf secret) in each request. Include them in the request headers. The keys can be defined in the auth options, but default to \"X-Auth-Token\" and \"X-Refresh-Token\", respectively. See the bearerTokens example for sample code of both.\n\nIdeally, if using bearer tokens, they should be stored in a location that cannot be accessed with javascript. You want to be able to separate your csrf secret from your jwt's. If using web, I suggest using cookies. If using mobile, store these in a secure manner!\n\nIf you are using cookies, the auth and refresh jwt's will automatically be included. You only need to include the csrf token.\n\n## API\n\n### Create a new jwt middleware\n~~~ go\nvar restrictedRoute jwt.Auth\n~~~\n\n### JWT middleware options\n~~~ go\ntype Options struct {\n  SigningMethodString   string // one of \"HS256\", \"HS384\", \"HS512\", \"RS256\", \"RS384\", \"RS512\", \"ES256\", \"ES384\", \"ES512\"\n  PrivateKeyLocation    string // only for RSA and ECDSA signing methods; only required if VerifyOnlyServer is false\n  PublicKeyLocation     string // only for RSA and ECDSA signing methods\n  HMACKey               []byte // only for HMAC-SHA signing method\n  VerifyOnlyServer      bool // false = server can verify and issue tokens (default); true = server can only verify tokens\n  BearerTokens          bool // false = server uses cookies to transport jwts (default); true = server uses request headers\n  RefreshTokenValidTime time.Duration\n  AuthTokenValidTime    time.Duration\n  AuthTokenName         string // defaults to \"AuthToken\" for cookies and \"X-Auth-Token\" for bearer tokens\n  RefreshTokenName      string // defaults to \"RefreshToken\" for cookies and \"X-Refresh-Token\" for bearer tokens\n  CSRFTokenName         string // defaults to \"X-CSRF-Token\"\n  Debug                 bool // true = more logs are shown\n  IsDevEnv              bool // true = in development mode; this sets http cookies (if used) to insecure; false = production mode; this sets http cookies (if used) to secure\n}\n~~~\n\n### ClaimsType\n~~~ go\ntype ClaimsType struct {\n  // Standard claims are the standard jwt claims from the ietf standard\n  // https://tools.ietf.org/html/rfc7519\n  jwt.StandardClaims\n  Csrf               string\n  CustomClaims       map[string]interface{}\n}\n~~~\n\nNote that there is a \"CustomClaims\" map that allows you to set whatever you want. See \"IssueTokenClaims\" and \"GrabTokenClaims\", below, for more.\n\n### Initialize new JWT middleware\n~~~ go\nauthErr := jwt.New(\u0026restrictedRoute, jwt.Options{\n  SigningMethodString:   \"RS256\",\n  PrivateKeyLocation:    \"keys/app.rsa\",     // `$ openssl genrsa -out app.rsa 2048`\n  PublicKeyLocation:     \"keys/app.rsa.pub\", // `$ openssl rsa -in app.rsa -pubout \u003e app.rsa.pub`\n  RefreshTokenValidTime: 72 * time.Hour,\n  AuthTokenValidTime:    15 * time.Minute,\n  Debug:                 false,\n  IsDevEnv:              true,\n})\nif authErr != nil {\n  log.Println(\"Error initializing the JWT's!\")\n  log.Fatal(authErr)\n}\n~~~\n\n### Handle a restricted route (see below for integration with popular frameworks)\n~~~ go\n// outside of main()\nvar restrictedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n  csrfSecret := w.Header().Get(\"X-CSRF-Token\")\n  claims, err := restrictedRoute.GrabTokenClaims(r)\n  log.Println(claims)\n  \n  if err != nil {\n    http.Error(w, \"Internal Server Error\", 500)\n  } else {\n    templates.RenderTemplate(w, \"restricted\", \u0026templates.RestrictedPage{ csrfSecret, claims.CustomClaims[\"Role\"].(string) })\n  }\n})\n\nfunc main() {\n  ...\n  http.Handle(\"/restricted\", restrictedRoute.Handler(restrictedHandler))\n\n  log.Println(\"Listening on localhost:3000\")\n  http.ListenAndServe(\"127.0.0.1:3000\", nil)\n}\n~~~\n\n### Issue new tokens and CSRF secret (for instance, when a user provides valid login credentials)\n~~~ go\n// in a handler func\nclaims := jwt.ClaimsType{}\nclaims.StandardClaims.Id = \"fakeTokenId123\"\nclaims.CustomClaims = make(map[string]interface{})\nclaims.CustomClaims[\"Role\"] = \"user\"\n\nerr := restrictedRoute.IssueNewTokens(w, \u0026claims)\nif err != nil {\n  http.Error(w, \"Internal Server Error\", 500)\n}\n\nw.WriteHeader(http.StatusOK)\n~~~\n\nNote: a token Id must be provided if you'd later like the ability to revoke this token!\n\n### Get a CSRF secret from a response\n~~~ go\n// in a handler func\n// note: this works because if the middleware has made it this far, the JWT middleware has written a CSRF secret to the response writer (w)\ncsrfSecret := w.Header().Get(\"X-CSRF-Token\")\n~~~\n\n### Get the expiration time of the refresh token, in Unix time\n~~~ go\n// in a handler func\n// note: this works because if the middleware has made it this far, the JWT middleware has written this to the response writer (w)\n// note: also, this won't be exact and might be a few milliseconds off from the token's actual expiry\nrefreshExpirationTime := w.Header().Get(\"Refresh-Expiry\")\n~~~\n\n### Get the expiration time of the auth token, in Unix time\n~~~ go\n// in a handler func\n// note: this works because if the middleware has made it this far, the JWT middleware has written this to the response writer (w)\n// note: also, this won't be exact and might be a few milliseconds off from the token's actual expiry\nauthExpirationTime := w.Header().Get(\"Auth-Expiry\")\n~~~\n\n### Get claims from a request\n~~~ go\n// in a handler func\nclaims, err := restrictedRoute.GrabTokenClaims(r)\nlog.Println(claims)\n~~~\n\n### Nullify auth and refresh tokens (for instance, when a user logs out)\n~~~ go\n// in a handler func\nerr = restrictedRoute.NullifyTokens(w, r)\nif err != nil {\n  http.Error(w, \"Internal Server Error\", 500)\n  return\n}\n\nhttp.Redirect(w, r, \"/login\", 302)\n~~~\n\n### Token Id checker\nA function is used to check if a refresh token id has been revoked. You can either use a blacklist of revoked tokens, or a whitelist of allowed tokens. Your call. This function simply needs to return true if the token id has not been revoked. This function is run every time an auth token is refreshed.\n~~~go\nvar restrictedRoute jwt.Auth\n\n// create a database of refresh tokens\n// map key is the jti (json token identifier)\n// the val doesn't represent anything but could be used to hold \"valid\", \"revoked\", etc.\n// in the real world, you would store these in your db. This is just an example.\nvar refreshTokens map[string]string\n\nrestrictedRoute.SetCheckTokenIdFunction(CheckRefreshToken)\n\nfunc CheckRefreshToken(jti string) bool {\n  return refreshTokens[jti] != \"\"\n}\n~~~\n\n### Token Id revoker\nA function that adds a token id to a blacklist of revoked tokens, or revokes it from a whitelist of allowed tokens (however you'd like to do it).\n~~~go\nvar restrictedRoute jwt.Auth\n\n// create a database of refresh tokens\n// map key is the jti (json token identifier)\n// the val doesn't represent anything but could be used to hold \"valid\", \"revoked\", etc.\n// in the real world, you would store these in your db. This is just an example.\nvar refreshTokens map[string]string\n\nrestrictedRoute.SetRevokeTokenFunction(DeleteRefreshToken)\n\nfunc DeleteRefreshToken(jti string) error {\n  delete(refreshTokens, jti)\n  return nil\n}\n~~~\n\n### 500 error handling\nSet the response to a 500 error.\n~~~go\nvar restrictedRoute jwt.Auth\n\nrestrictedRoute.SetErrorHandler(myErrorHandler)\n\nvar myErrorHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n  http.Error(w, \"I pitty the fool who has a 500 internal server error\", 500)\n  return\n})\n~~~\n\n### 401 unauthorized handling\nSet the response to a 401 unauthorized request\n~~~go\nvar restrictedRoute jwt.Auth\n\nrestrictedRoute.SetUnauthorizedHandler(MyUnauthorizedHandler)\n\nvar MyUnauthorizedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n  http.Error(w, \"I pitty the fool who is unauthorized\", 401)\n  return\n})\n~~~\n\n\n## Integration with popular goLang web Frameworks (untested)\n\nThe architecture of this package was inspired by [Secure](https://github.com/unrolled/secure), so I believe the integrations, below, should work. But they are untested.\n\n### [Echo](https://github.com/labstack/echo)\n~~~ go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/adam-hanna/jwt-auth/jwt\"\n\t\"github.com/labstack/echo\"\n)\n\nvar restrictedRoute jwt.Auth\n\nfunc main() {\n\tauthErr := jwt.New(\u0026restrictedRoute, jwt.Options{\n\t\tSigningMethodString:   \"RS256\",\n\t\tPrivateKeyLocation:    \"keys/app.rsa\",     // `$ openssl genrsa -out app.rsa 2048`\n\t\tPublicKeyLocation:     \"keys/app.rsa.pub\", // `$ openssl rsa -in app.rsa -pubout \u003e app.rsa.pub`\n\t\tRefreshTokenValidTime: 72 * time.Hour,\n\t\tAuthTokenValidTime:    15 * time.Minute,\n\t\tDebug:                 false,\n\t\tIsDevEnv:              true,\n\t})\n\tif authErr != nil {\n\t\tlog.Println(\"Error initializing the JWT's!\")\n\t\tlog.Fatal(authErr)\n\t}\n\n\te := echo.New()\n\n\te.GET(\"/public\", func(c echo.Context) error {\n\t\tc.String(http.StatusOK, \"Hello, world!\")\n\t\treturn nil\n\t})\n\n\te.GET(\"/restricted\", func(c echo.Context) error {\n\t\tc.String(http.StatusOK, \"Restricted\")\n\t\treturn nil\n\t}, echo.WrapMiddleware(restrictedRoute.Handler))\n\n\te.Logger.Fatal(e.Start(\":1323\"))\n}\n~~~\n\n### [Gin](https://github.com/gin-gonic/gin)\n~~~ go\n// main.go\npackage main\n\nimport (\n  \"log\"\n\n  \"github.com/gin-gonic/gin\"\n  \"github.com/adam-hanna/jwt-auth/jwt\"\n)\n\nvar restrictedRoute jwt.Auth\n\nfunc main() {\n  authErr := jwt.New(\u0026restrictedRoute, jwt.Options{\n    SigningMethodString:   \"RS256\",\n    PrivateKeyLocation:    \"keys/app.rsa\",     // `$ openssl genrsa -out app.rsa 2048`\n    PublicKeyLocation:     \"keys/app.rsa.pub\", // `$ openssl rsa -in app.rsa -pubout \u003e app.rsa.pub`\n    RefreshTokenValidTime: 72 * time.Hour,\n    AuthTokenValidTime:    15 * time.Minute,\n    Debug:                 false,\n    IsDevEnv:              true,\n  })\n  if authErr != nil {\n    log.Println(\"Error initializing the JWT's!\")\n    log.Fatal(authErr)\n  }\n  restrictedFunc := func() gin.HandlerFunc {\n    return func(c *gin.Context) {\n      err := restrictedRoute.Process(c.Writer, c.Request)\n\n      // If there was an error, do not continue.\n      if err != nil {\n        return\n      }\n\n      c.Next()\n    }\n  }()\n\n  router := gin.Default()\n  router.Use(restrictedFunc)\n\n  router.GET(\"/\", func(c *gin.Context) {\n    c.String(200, \"Restricted\")\n  })\n\n  router.Run(\"127.0.0.1:3000\")\n}\n~~~\n\n### [Goji](https://github.com/zenazn/goji)\n~~~ go\n// main.go\npackage main\n\nimport (\n  \"net/http\"\n  \"log\"\n\n  \"github.com/adam-hanna/jwt-auth/jwt\"\n  \"github.com/zenazn/goji\"\n  \"github.com/zenazn/goji/web\"\n)\n\nvar restrictedRoute jwt.Auth\n\nfunc main() {\n  authErr := jwt.New(\u0026restrictedRoute, jwt.Options{\n    SigningMethodString:   \"RS256\",\n    PrivateKeyLocation:    \"keys/app.rsa\",     // `$ openssl genrsa -out app.rsa 2048`\n    PublicKeyLocation:     \"keys/app.rsa.pub\", // `$ openssl rsa -in app.rsa -pubout \u003e app.rsa.pub`\n    RefreshTokenValidTime: 72 * time.Hour,\n    AuthTokenValidTime:    15 * time.Minute,\n    Debug:                 false,\n    IsDevEnv:              true,\n  })\n  if authErr != nil {\n    log.Println(\"Error initializing the JWT's!\")\n    log.Fatal(authErr)\n  }\n\n  goji.Get(\"/\", func(c web.C, w http.ResponseWriter, req *http.Request) {\n    w.Write([]byte(\"Restricted\"))\n  })\n  goji.Use(restrictedRoute.Handler)\n  goji.Serve() // Defaults to \":8000\".\n}\n~~~\n\n### [Iris](https://github.com/kataras/iris)\n~~~ go\n//main.go\npackage main\n\nimport (\n  \"log\"\n\n  \"github.com/kataras/iris\"\n  \"github.com/adam-hanna/jwt-auth/jwt\"\n)\n\nvar restrictedRoute jwt.Auth\n\nfunc main() {\n  authErr := jwt.New(\u0026restrictedRoute, jwt.Options{\n    SigningMethodString:   \"RS256\",\n    PrivateKeyLocation:    \"keys/app.rsa\",     // `$ openssl genrsa -out app.rsa 2048`\n    PublicKeyLocation:     \"keys/app.rsa.pub\", // `$ openssl rsa -in app.rsa -pubout \u003e app.rsa.pub`\n    RefreshTokenValidTime: 72 * time.Hour,\n    AuthTokenValidTime:    15 * time.Minute,\n    Debug:                 false,\n    IsDevEnv:              true,\n  })\n  if authErr != nil {\n    log.Println(\"Error initializing the JWT's!\")\n    log.Fatal(authErr)\n  }\n\n  iris.UseFunc(func(c *iris.Context) {\n    err := restrictedRoute.Process(c.ResponseWriter, c.Request)\n\n    // If there was an error, do not continue.\n    if err != nil {\n      return\n    }\n\n    c.Next()\n  })\n\n  iris.Get(\"/home\", func(c *iris.Context) {\n    c.SendStatus(200,\"Restricted\")\n  })\n\n  iris.Listen(\":8080\")\n\n}\n\n~~~~\n\n### [Negroni](https://github.com/codegangsta/negroni)\nNote this implementation has a special helper function called `HandlerFuncWithNext`.\n~~~ go\n// main.go\npackage main\n\nimport (\n  \"net/http\"\n  \"log\"\n\n  \"github.com/codegangsta/negroni\"\n  \"github.com/adam-hanna/jwt-auth/jwt\"\n)\n\nvar restrictedRoute jwt.Auth\n\nfunc main() {\n  mux := http.NewServeMux()\n  mux.HandleFunc(\"/\", func(w http.ResponseWriter, req *http.Request) {\n    w.Write([]byte(\"Restricted\"))\n  })\n\n  authErr := jwt.New(\u0026restrictedRoute, jwt.Options{\n    SigningMethodString:   \"RS256\",\n    PrivateKeyLocation:    \"keys/app.rsa\",     // `$ openssl genrsa -out app.rsa 2048`\n    PublicKeyLocation:     \"keys/app.rsa.pub\", // `$ openssl rsa -in app.rsa -pubout \u003e app.rsa.pub`\n    RefreshTokenValidTime: 72 * time.Hour,\n    AuthTokenValidTime:    15 * time.Minute,\n    Debug:                 false,\n    IsDevEnv:              true,\n  })\n  if authErr != nil {\n    log.Println(\"Error initializing the JWT's!\")\n    log.Fatal(authErr)\n  }\n\n  n := negroni.Classic()\n  n.Use(negroni.HandlerFunc(restrictedRoute.HandlerFuncWithNext))\n  n.UseHandler(mux)\n\n  n.Run(\"127.0.0.1:3000\")\n}\n~~~\n\n## TODO\n1. Clean up the tests\n\n## Test Coverage\n~~~ bash\n$ cd jwt \u0026\u0026 go test -coverprofile=test/coverage.out\n\ncoverage: 84.7% of statements\n\n$ go tool cover -html=test/coverage.out\n~~~\n\n## License\n~~~\nThe MIT License (MIT)\n\nCopyright (c) 2016 Adam Hanna\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n~~~\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadam-hanna%2Fjwt-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadam-hanna%2Fjwt-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadam-hanna%2Fjwt-auth/lists"}