├── .github ├── FUNDING.yml └── workflows │ ├── standard-go-test.yml │ └── standard-stale.yml ├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── test_certs ├── ec256-private.pem ├── ec256-public.pem ├── ed25519-private.pem ├── ed25519-public.pem ├── sample_key └── sample_key.pub ├── tokenauth.go └── tokenauth_test.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: markbates 4 | patreon: buffalo 5 | -------------------------------------------------------------------------------- /.github/workflows/standard-go-test.yml: -------------------------------------------------------------------------------- 1 | name: Standard Test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | 8 | jobs: 9 | call-standard-test: 10 | name: Test 11 | uses: gobuffalo/.github/.github/workflows/go-test.yml@v1 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.github/workflows/standard-stale.yml: -------------------------------------------------------------------------------- 1 | name: Standard Autocloser 2 | 3 | on: 4 | schedule: 5 | - cron: "30 1 * * *" 6 | 7 | jobs: 8 | call-standard-autocloser: 9 | name: Autocloser 10 | uses: gobuffalo/.github/.github/workflows/stale.yml@v1 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | tmp 4 | *.pid 5 | coverage 6 | coverage.data 7 | build/* 8 | *.pbxuser 9 | *.mode1v3 10 | .svn 11 | profile 12 | .console_history 13 | .sass-cache/* 14 | solr/ 15 | .jhw-cache/ 16 | jhw.* 17 | *.sublime* 18 | node_modules/ 19 | dist/ 20 | generated/ 21 | .vendor/ 22 | bin/* 23 | gin-bin 24 | .idea/ 25 | .vscode 26 | cover.out 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Mark Bates 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mw-tokenauth 2 | 3 | [![Standard Test](https://github.com/gobuffalo/mw-tokenauth/actions/workflows/standard-go-test.yml/badge.svg)](https://github.com/gobuffalo/mw-tokenauth/actions/workflows/standard-go-test.yml) 4 | [![Go Reference](https://pkg.go.dev/badge/github.com/gobuffalo/mw-tokenauth.svg)](https://pkg.go.dev/github.com/gobuffalo/mw-tokenauth) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/gobuffalo/mw-tokenauth)](https://goreportcard.com/report/github.com/gobuffalo/mw-tokenauth) 6 | 7 | JWT Token Authentication Middleware for 8 | [Buffalo](https://github.com/gobuffalo/buffalo). 9 | 10 | ## Installation 11 | 12 | ```console 13 | $ go get github.com/gobuffalo/mw-tokenauth 14 | ``` 15 | 16 | ## Usage 17 | 18 | For details on how to use this middleware, see the [Go Reference](https://pkg.go.dev/github.com/gobuffalo/mw-tokenauth) 19 | 20 | You can also gain insight into how to use it by looking at the [tests](https://github.com/gobuffalo/mw-tokenauth/blob/master/tokenauth_test.go) 21 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gobuffalo/mw-tokenauth 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/gobuffalo/buffalo v1.1.0 7 | github.com/gobuffalo/envy v1.10.2 8 | github.com/gobuffalo/httptest v1.5.2 9 | github.com/golang-jwt/jwt/v4 v4.4.3 10 | github.com/stretchr/testify v1.8.1 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 2 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= 3 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 4 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 5 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 6 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 11 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 12 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 13 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 14 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 15 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 16 | github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= 17 | github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 18 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 19 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 20 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 21 | github.com/gobuffalo/buffalo v1.1.0 h1:6y1fUC47QWevaM1ImukJFHNgxiRIT+Y74VcP4ZQaz80= 22 | github.com/gobuffalo/buffalo v1.1.0/go.mod h1:lLsx9Y8bFYu9uvQyIEB3M0QA908ChHUPjwOGumZWARU= 23 | github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= 24 | github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= 25 | github.com/gobuffalo/events v1.4.3 h1:JYDq7NbozP10zaN9Ijfem6Ozox2KacU2fU38RyquXM8= 26 | github.com/gobuffalo/events v1.4.3/go.mod h1:2BwfpV5X63t8xkUcVqIv4IbyAobJazRSVu1F1pgf3rc= 27 | github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE= 28 | github.com/gobuffalo/flect v1.0.0 h1:eBFmskjXZgAOagiTXJH25Nt5sdFwNRcb8DKZsIsAUQI= 29 | github.com/gobuffalo/flect v1.0.0/go.mod h1:l9V6xSb4BlXwsxEMj3FVEub2nkdQjWhPvD8XTTlHPQc= 30 | github.com/gobuffalo/github_flavored_markdown v1.1.3 h1:rSMPtx9ePkFB22vJ+dH+m/EUBS8doQ3S8LeEXcdwZHk= 31 | github.com/gobuffalo/github_flavored_markdown v1.1.3/go.mod h1:IzgO5xS6hqkDmUh91BW/+Qxo/qYnvfzoz3A7uLkg77I= 32 | github.com/gobuffalo/grift v1.5.2 h1:mC0vHRs+nXz+JhkH3sv+rVnnTQRDXrUrOXOPYpgPjpo= 33 | github.com/gobuffalo/grift v1.5.2/go.mod h1:Uf/3T2AR1Vv+t84EPmxCjqQ8oyJwXs0FAoLMFUn/JVs= 34 | github.com/gobuffalo/helpers v0.6.7 h1:C9CedoRSfgWg2ZoIkVXgjI5kgmSpL34Z3qdnzpfNVd8= 35 | github.com/gobuffalo/helpers v0.6.7/go.mod h1:j0u1iC1VqlCaJEEVkZN8Ia3TEzfj/zoXANqyJExTMTA= 36 | github.com/gobuffalo/here v0.6.7/go.mod h1:vuCfanjqckTuRlqAitJz6QC4ABNnS27wLb816UhsPcc= 37 | github.com/gobuffalo/httptest v1.5.2 h1:GpGy520SfY1QEmyPvaqmznTpG4gEQqQ82HtHqyNEreM= 38 | github.com/gobuffalo/httptest v1.5.2/go.mod h1:FA23yjsWLGj92mVV74Qtc8eqluc11VqcWr8/C1vxt4g= 39 | github.com/gobuffalo/logger v1.0.7 h1:LTLwWelETXDYyqF/ASf0nxaIcdEOIJNxRokPcfI/xbU= 40 | github.com/gobuffalo/logger v1.0.7/go.mod h1:u40u6Bq3VVvaMcy5sRBclD8SXhBYPS0Qk95ubt+1xJM= 41 | github.com/gobuffalo/meta v0.3.3 h1:GwPWdbdnp4JrKASvMLa03OtmzISq7z/nE7T6aMqzoYM= 42 | github.com/gobuffalo/meta v0.3.3/go.mod h1:o4B099IUFUfK4555Guqxz1zHAqyuUQ/KtHXi8WvVeFE= 43 | github.com/gobuffalo/nulls v0.4.2 h1:GAqBR29R3oPY+WCC7JL9KKk9erchaNuV6unsOSZGQkw= 44 | github.com/gobuffalo/nulls v0.4.2/go.mod h1:EElw2zmBYafU2R9W4Ii1ByIj177wA/pc0JdjtD0EsH8= 45 | github.com/gobuffalo/plush/v4 v4.1.18 h1:bnPjdMTEUQHqj9TNX2Ck3mxEXYZa+0nrFMNM07kpX9g= 46 | github.com/gobuffalo/plush/v4 v4.1.18/go.mod h1:xi2tJIhFI4UdzIL8sxZtzGYOd2xbBpcFbLZlIPGGZhU= 47 | github.com/gobuffalo/refresh v1.13.3 h1:HYQlI6RiqWUf2yzCXvUHAYqm9M9/teVnox+mjzo/9rQ= 48 | github.com/gobuffalo/refresh v1.13.3/go.mod h1:NkzgLKZGk5suOvgvOD0/VALog0fH29Ib7fwym9JmRxA= 49 | github.com/gobuffalo/tags/v3 v3.1.4 h1:X/ydLLPhgXV4h04Hp2xlbI2oc5MDaa7eub6zw8oHjsM= 50 | github.com/gobuffalo/tags/v3 v3.1.4/go.mod h1:ArRNo3ErlHO8BtdA0REaZxijuWnWzF6PUXngmMXd2I0= 51 | github.com/gobuffalo/validate/v3 v3.3.3 h1:o7wkIGSvZBYBd6ChQoLxkz2y1pfmhbI4jNJYh6PuNJ4= 52 | github.com/gobuffalo/validate/v3 v3.3.3/go.mod h1:YC7FsbJ/9hW/VjQdmXPvFqvRis4vrRYFxr69WiNZw6g= 53 | github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= 54 | github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 55 | github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= 56 | github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 57 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 58 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 59 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 60 | github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= 61 | github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 62 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 63 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 64 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 65 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 66 | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= 67 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 68 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= 69 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 70 | github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= 71 | github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= 72 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 73 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 74 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 75 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 76 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 77 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 78 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 79 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 80 | github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= 81 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 82 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 83 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 84 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 85 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 86 | github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= 87 | github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 88 | github.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y= 89 | github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= 90 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 91 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 92 | github.com/monoculum/formam v3.5.5+incompatible h1:iPl5csfEN96G2N2mGu8V/ZB62XLf9ySTpC8KRH6qXec= 93 | github.com/monoculum/formam v3.5.5+incompatible/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= 94 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 95 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 96 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 97 | github.com/psanford/memfs v0.0.0-20210214183328-a001468d78ef h1:NKxTG6GVGbfMXc2mIk+KphcH6hagbVXhcFkbTgYleTI= 98 | github.com/psanford/memfs v0.0.0-20210214183328-a001468d78ef/go.mod h1:tcaRap0jS3eifrEEllL6ZMd9dg8IlDpi2S1oARrQ+NI= 99 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 100 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 101 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 102 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= 103 | github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 104 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 105 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 106 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E= 107 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= 108 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8= 109 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= 110 | github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 111 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 112 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 113 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 114 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 115 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 116 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 117 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 118 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 119 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 120 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 121 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 122 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 123 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 124 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 125 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 126 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 127 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 128 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 129 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 130 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 131 | golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 132 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU= 133 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 134 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 135 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 136 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 137 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 138 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 139 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 140 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 143 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 144 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 145 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 146 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 147 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 148 | golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY= 149 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 150 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 151 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 152 | golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= 153 | golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 154 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 155 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 156 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 157 | golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= 158 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 159 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 160 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 161 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 162 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 163 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 164 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 165 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 166 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 167 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 168 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 169 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 170 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 171 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 172 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 173 | -------------------------------------------------------------------------------- /test_certs/ec256-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIAh5qA3rmqQQuu0vbKV/+zouz/y/Iy2pLpIcWUSyImSwoAoGCCqGSM49 3 | AwEHoUQDQgAEYD54V/vp+54P9DXarYqx4MPcm+HKRIQzNasYSoRQHQ/6S6Ps8tpM 4 | cT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /test_certs/ec256-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYD54V/vp+54P9DXarYqx4MPcm+HK 3 | RIQzNasYSoRQHQ/6S6Ps8tpMcT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg== 4 | -----END PUBLIC KEY----- 5 | -------------------------------------------------------------------------------- /test_certs/ed25519-private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MC4CAQAwBQYDK2VwBCIEIEFMEZrmlYxczXKFxIlNvNGR5JQvDhTkLovJYxwQd3ua 3 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /test_certs/ed25519-public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MCowBQYDK2VwAyEAWH7z6hpYqvPns2i4n9yymwvB3APhi4LyQ7iHOT6crtE= 3 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /test_certs/sample_key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn 3 | SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i 4 | cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC 5 | PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR 6 | ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA 7 | Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3 8 | n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy 9 | MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9 10 | POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE 11 | KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM 12 | IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn 13 | FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY 14 | mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj 15 | FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U 16 | I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs 17 | 2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn 18 | /iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT 19 | OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86 20 | EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+ 21 | hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0 22 | 4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb 23 | mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry 24 | eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3 25 | CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+ 26 | 9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test_certs/sample_key.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKsTeNem/V41 3 | fGnJm6gOdrj8ym3rFkEU/wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7 4 | mCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBp 5 | HssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2 6 | XrHhR+1DcKJzQBSTAGnpYVaqpsARap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3b 7 | ODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy 8 | 7wIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /tokenauth.go: -------------------------------------------------------------------------------- 1 | // Package tokenauth provides jwt token authorisation middleware 2 | // supports HMAC, RSA, ECDSA, RSAPSS EdDSA algorithms 3 | // uses github.com/golang-jwt/jwt/v4 for jwt implementation 4 | // 5 | // Setting Up tokenauth middleware 6 | // 7 | // Using tokenauth with defaults 8 | // app.Use(tokenauth.New(tokenauth.Options{})) 9 | // Specifying Signing method for JWT 10 | // app.Use(tokenauth.New(tokenauth.Options{ 11 | // SignMethod: jwt.SigningMethodRS256, 12 | // })) 13 | // By default the Key used is loaded from the JWT_SECRET or JWT_PUBLIC_KEY env variable depending 14 | // on the SigningMethod used. However you can retrive the key from a different source. 15 | // app.Use(tokenauth.New(tokenauth.Options{ 16 | // GetKey: func(jwt.SigningMethod) (interface{}, error) { 17 | // // Your Implementation here ... 18 | // }, 19 | // })) 20 | // Default authorisation scheme is Bearer, you can specify your own. 21 | // app.Use(tokenauth.New(tokenauth.Options{ 22 | // AuthScheme: "Token" 23 | // })) 24 | // 25 | // 26 | // Creating a new token 27 | // 28 | // This can be referred from the underlying JWT package being used https://github.com/golang-jwt/jwt 29 | // 30 | // Example 31 | // claims := jwt.MapClaims{} 32 | // claims["userid"] = "123" 33 | // claims["exp"] = time.Now().Add(time.Minute * 5).Unix() 34 | // // add more claims 35 | // token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 36 | // tokenString, err := token.SignedString([]byte(SecretKey)) 37 | // 38 | // 39 | // Getting Claims from JWT token from buffalo context 40 | // 41 | // Example of retriving username from claims (this step is same regardless of the signing method used) 42 | // claims := c.Value("claims").(jwt.MapClaims) 43 | // username := claims["username"].(string) 44 | package tokenauth 45 | 46 | import ( 47 | "errors" 48 | "io/ioutil" 49 | "log" 50 | "net/http" 51 | 52 | "github.com/gobuffalo/buffalo" 53 | "github.com/gobuffalo/envy" 54 | "github.com/golang-jwt/jwt/v4" 55 | ) 56 | 57 | var ( 58 | // ErrTokenInvalid is returned when the token provided is invalid 59 | ErrTokenInvalid = errors.New("token invalid") 60 | // ErrNoToken is returned if no token is supplied in the request. 61 | ErrNoToken = errors.New("token not found in request") 62 | // ErrBadSigningMethod is returned if the token sign method in the request 63 | // does not match the signing method used 64 | ErrBadSigningMethod = errors.New("unexpected signing method") 65 | ) 66 | 67 | // Options for the JWT middleware 68 | type Options struct { 69 | SignMethod jwt.SigningMethod 70 | GetKey func(jwt.SigningMethod) (interface{}, error) 71 | AuthScheme string 72 | } 73 | 74 | // New enables jwt token verification if no Sign method is provided, 75 | // by default uses HMAC 76 | func New(options Options) buffalo.MiddlewareFunc { 77 | // set sign method to HMAC if not provided 78 | if options.SignMethod == nil { 79 | options.SignMethod = jwt.SigningMethodHS256 80 | } 81 | if options.GetKey == nil { 82 | options.GetKey = selectGetKeyFunc(options.SignMethod) 83 | } 84 | // get key for validation 85 | key, err := options.GetKey(options.SignMethod) 86 | // if error on getting key exit. 87 | if err != nil { 88 | log.Fatal("couldn't get key:", err) 89 | } 90 | if options.AuthScheme == "" { 91 | options.AuthScheme = "Bearer" 92 | } 93 | return func(next buffalo.Handler) buffalo.Handler { 94 | return func(c buffalo.Context) error { 95 | // get Authorisation header value 96 | authString := c.Request().Header.Get("Authorization") 97 | 98 | tokenString, err := getJwtToken(authString, options.AuthScheme) 99 | // if error on getting the token, return with status unauthorized 100 | if err != nil { 101 | return c.Error(http.StatusUnauthorized, err) 102 | } 103 | 104 | // validating and parsing the tokenString 105 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 106 | // Validating if algorithm used for signing is same as the algorithm in token 107 | if token.Method.Alg() != options.SignMethod.Alg() { 108 | return nil, ErrBadSigningMethod 109 | } 110 | return key, nil 111 | }) 112 | // if error validating jwt token, return with status unauthorized 113 | if err != nil { 114 | return c.Error(http.StatusUnauthorized, err) 115 | } 116 | 117 | // set the claims as context parameter. 118 | // so that the actions can use the claims from jwt token 119 | c.Set("claims", token.Claims) 120 | // calling next handler 121 | err = next(c) 122 | 123 | return err 124 | } 125 | } 126 | } 127 | 128 | // selectGetKeyFunc is an helper function to choose the GetKey function 129 | // according to the Signing method used 130 | func selectGetKeyFunc(method jwt.SigningMethod) func(jwt.SigningMethod) (interface{}, error) { 131 | switch method.(type) { 132 | case *jwt.SigningMethodRSA: 133 | return GetKeyRSA 134 | case *jwt.SigningMethodECDSA: 135 | return GetKeyECDSA 136 | case *jwt.SigningMethodRSAPSS: 137 | return GetKeyRSAPSS 138 | case *jwt.SigningMethodEd25519: 139 | return GetkeyEdDSA 140 | default: 141 | return GetHMACKey 142 | } 143 | } 144 | 145 | // GetHMACKey gets secret key from env 146 | func GetHMACKey(jwt.SigningMethod) (interface{}, error) { 147 | key, err := envy.MustGet("JWT_SECRET") 148 | return []byte(key), err 149 | } 150 | 151 | // GetKeyRSA gets the public key file location from env and returns rsa.PublicKey 152 | func GetKeyRSA(jwt.SigningMethod) (interface{}, error) { 153 | key, err := envy.MustGet("JWT_PUBLIC_KEY") 154 | if err != nil { 155 | return nil, err 156 | } 157 | keyData, err := ioutil.ReadFile(key) 158 | if err != nil { 159 | return nil, err 160 | } 161 | return jwt.ParseRSAPublicKeyFromPEM(keyData) 162 | } 163 | 164 | // GetKeyRSAPSS uses GetKeyRSA() since both requires rsa.PublicKey 165 | func GetKeyRSAPSS(signingMethod jwt.SigningMethod) (interface{}, error) { 166 | return GetKeyRSA(signingMethod) 167 | } 168 | 169 | // GetKeyECDSA gets the public.pem file location from env and returns ecdsa.PublicKey 170 | func GetKeyECDSA(jwt.SigningMethod) (interface{}, error) { 171 | key, err := envy.MustGet("JWT_PUBLIC_KEY") 172 | if err != nil { 173 | return nil, err 174 | } 175 | keyData, err := ioutil.ReadFile(key) 176 | if err != nil { 177 | return nil, err 178 | } 179 | return jwt.ParseECPublicKeyFromPEM(keyData) 180 | } 181 | 182 | // GetKeyECDSA gets the public.pem file location from env and returns eddsa.PublicKey 183 | func GetkeyEdDSA(jwt.SigningMethod) (interface{}, error) { 184 | key, err := envy.MustGet("JWT_PUBLIC_KEY") 185 | if err != nil { 186 | return nil, err 187 | } 188 | keyData, err := ioutil.ReadFile(key) 189 | if err != nil { 190 | return nil, err 191 | } 192 | return jwt.ParseEdPublicKeyFromPEM(keyData) 193 | } 194 | 195 | // getJwtToken gets the token from the Authorisation header 196 | // removes the given authorisation scheme part (e.g. Bearer) from the authorisation header value. 197 | // returns No token error if Token is not found 198 | // returns Token Invalid error if the token value cannot be obtained by removing authorisation scheme part (e.g. `Bearer `) 199 | func getJwtToken(authString, authScheme string) (string, error) { 200 | if authString == "" { 201 | return "", ErrNoToken 202 | } 203 | l := len(authScheme) 204 | if len(authString) > l+1 && authString[:l] == authScheme { 205 | return authString[l+1:], nil 206 | } 207 | return "", ErrTokenInvalid 208 | } 209 | -------------------------------------------------------------------------------- /tokenauth_test.go: -------------------------------------------------------------------------------- 1 | package tokenauth_test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "testing" 9 | "time" 10 | 11 | tokenauth "github.com/gobuffalo/mw-tokenauth" 12 | 13 | "github.com/gobuffalo/buffalo" 14 | "github.com/gobuffalo/envy" 15 | "github.com/gobuffalo/httptest" 16 | "github.com/golang-jwt/jwt/v4" 17 | "github.com/stretchr/testify/require" 18 | ) 19 | 20 | func appHMAC() *buffalo.App { 21 | h := func(c buffalo.Context) error { 22 | return c.Render(200, nil) 23 | } 24 | envy.Set("JWT_SECRET", "secret") 25 | a := buffalo.New(buffalo.Options{}) 26 | // if method not specified it will use HMAC 27 | a.Use(tokenauth.New(tokenauth.Options{ 28 | GetKey: tokenauth.GetHMACKey, 29 | })) 30 | a.GET("/", h) 31 | return a 32 | } 33 | 34 | func appRSA() *buffalo.App { 35 | h := func(c buffalo.Context) error { 36 | return c.Render(200, nil) 37 | } 38 | envy.Set("JWT_PUBLIC_KEY", "test_certs/sample_key.pub") 39 | a := buffalo.New(buffalo.Options{}) 40 | a.Use(tokenauth.New(tokenauth.Options{ 41 | SignMethod: jwt.SigningMethodRS256, 42 | })) 43 | a.GET("/", h) 44 | return a 45 | } 46 | 47 | func appRSAPSS() *buffalo.App { 48 | h := func(c buffalo.Context) error { 49 | return c.Render(200, nil) 50 | } 51 | envy.Set("JWT_PUBLIC_KEY", "test_certs/sample_key.pub") 52 | a := buffalo.New(buffalo.Options{}) 53 | a.Use(tokenauth.New(tokenauth.Options{ 54 | SignMethod: jwt.SigningMethodPS256, 55 | GetKey: tokenauth.GetKeyRSAPSS, 56 | })) 57 | a.GET("/", h) 58 | return a 59 | } 60 | 61 | func appEdDSA() *buffalo.App { 62 | h := func(c buffalo.Context) error { 63 | return c.Render(200, nil) 64 | } 65 | envy.Set("JWT_PUBLIC_KEY", "test_certs/ed25519-public.pem") 66 | 67 | a := buffalo.New(buffalo.Options{}) 68 | a.Use(tokenauth.New(tokenauth.Options{ 69 | SignMethod: jwt.SigningMethodEdDSA, 70 | })) 71 | a.GET("/", h) 72 | return a 73 | } 74 | func appECDSA() *buffalo.App { 75 | h := func(c buffalo.Context) error { 76 | return c.Render(200, nil) 77 | } 78 | envy.Set("JWT_PUBLIC_KEY", "test_certs/ec256-public.pem") 79 | 80 | a := buffalo.New(buffalo.Options{}) 81 | a.Use(tokenauth.New(tokenauth.Options{ 82 | SignMethod: jwt.SigningMethodES256, 83 | })) 84 | a.GET("/", h) 85 | return a 86 | } 87 | 88 | func appCustomAuthScheme() *buffalo.App { 89 | h := func(c buffalo.Context) error { 90 | return c.Render(200, nil) 91 | } 92 | envy.Set("JWT_SECRET", "secret") 93 | a := buffalo.New(buffalo.Options{}) 94 | a.Use(tokenauth.New(tokenauth.Options{ 95 | AuthScheme: "Token", 96 | })) 97 | a.GET("/", h) 98 | return a 99 | } 100 | 101 | // Test HMAC 102 | func TestTokenHMAC(t *testing.T) { 103 | r := require.New(t) 104 | w := httptest.New(appHMAC()) 105 | 106 | // Missing Authorization 107 | res := w.HTML("/").Get() 108 | r.Equal(http.StatusUnauthorized, res.Code) 109 | 110 | // invalid token 111 | req := w.HTML("/") 112 | req.Headers["Authorization"] = "badcreds" 113 | res = req.Get() 114 | r.Equal(http.StatusUnauthorized, res.Code) 115 | r.Contains(res.Body.String(), "token invalid") 116 | 117 | // expired token 118 | secretKey := envy.Get("JWT_SECRET", "secret") 119 | claims := jwt.MapClaims{} 120 | claims["sub"] = "1234567890" 121 | claims["exp"] = time.Now().Add(-time.Minute * 5).Unix() 122 | 123 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 124 | tokenString, _ := token.SignedString([]byte(secretKey)) 125 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 126 | res = req.Get() 127 | r.Equal(http.StatusUnauthorized, res.Code) 128 | r.Contains(res.Body.String(), "Token is expired") 129 | 130 | // valid token 131 | claims["exp"] = time.Now().Add(time.Minute * 5).Unix() 132 | token = jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 133 | tokenString, _ = token.SignedString([]byte(secretKey)) 134 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 135 | res = req.Get() 136 | r.Equal(http.StatusOK, res.Code) 137 | } 138 | 139 | // Test RSA 140 | func TestTokenRSA(t *testing.T) { 141 | r := require.New(t) 142 | w := httptest.New(appRSA()) 143 | 144 | // Missing Authorization 145 | res := w.HTML("/").Get() 146 | r.Equal(http.StatusUnauthorized, res.Code) 147 | 148 | // invalid token 149 | req := w.HTML("/") 150 | req.Headers["Authorization"] = "badcreds" 151 | res = req.Get() 152 | r.Equal(http.StatusUnauthorized, res.Code) 153 | r.Contains(res.Body.String(), "token invalid") 154 | 155 | // expired token 156 | privateKeyFile := envy.Get("JWT_PRIVATE_KEY", "test_certs/sample_key") 157 | key, err := ioutil.ReadFile(privateKeyFile) 158 | if err != nil { 159 | log.Fatal(err) 160 | } 161 | parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key) 162 | if err != nil { 163 | log.Fatal("error parsing key:", err) 164 | } 165 | claims := jwt.MapClaims{} 166 | claims["sub"] = "1234567890" 167 | claims["exp"] = time.Now().Add(-time.Minute * 5).Unix() 168 | 169 | token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) 170 | tokenString, err := token.SignedString(parsedKey) 171 | if err != nil { 172 | log.Fatal("error signing token:", err) 173 | } 174 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 175 | res = req.Get() 176 | r.Equal(http.StatusUnauthorized, res.Code) 177 | r.Contains(res.Body.String(), "Token is expired") 178 | 179 | // valid token 180 | claims["exp"] = time.Now().Add(time.Minute * 5).Unix() 181 | token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims) 182 | tokenString, _ = token.SignedString(parsedKey) 183 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 184 | res = req.Get() 185 | r.Equal(http.StatusOK, res.Code) 186 | } 187 | 188 | // Test EdDSA 189 | func TestTokenEdDSA(t *testing.T) { 190 | r := require.New(t) 191 | w := httptest.New(appEdDSA()) 192 | 193 | // Missing Authorization 194 | res := w.HTML("/").Get() 195 | r.Equal(http.StatusUnauthorized, res.Code) 196 | 197 | // invalid token 198 | req := w.HTML("/") 199 | req.Headers["Authorization"] = "badcreds" 200 | res = req.Get() 201 | r.Equal(http.StatusUnauthorized, res.Code) 202 | r.Contains(res.Body.String(), "token invalid") 203 | 204 | // expired token 205 | privateKeyFile := envy.Get("JWT_PRIVATE_KEY", "test_certs/ed25519-private.pem") 206 | key, err := ioutil.ReadFile(privateKeyFile) 207 | if err != nil { 208 | log.Fatal("error reading keyfile:", err) 209 | } 210 | parsedKey, err := jwt.ParseEdPrivateKeyFromPEM(key) 211 | if err != nil { 212 | log.Fatal("error parsing key:", err) 213 | } 214 | claims := jwt.MapClaims{} 215 | claims["sub"] = "1234567890" 216 | claims["exp"] = time.Now().Add(-time.Minute * 5).Unix() 217 | token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims) 218 | tokenString, err := token.SignedString(parsedKey) 219 | if err != nil { 220 | log.Fatal("error signing token:", err) 221 | } 222 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 223 | res = req.Get() 224 | r.Equal(http.StatusUnauthorized, res.Code) 225 | r.Contains(res.Body.String(), "Token is expired") 226 | 227 | // valid token 228 | claims["exp"] = time.Now().Add(time.Minute * 5).Unix() 229 | token = jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims) 230 | tokenString, _ = token.SignedString(parsedKey) 231 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 232 | res = req.Get() 233 | r.Equal(http.StatusOK, res.Code) 234 | } 235 | 236 | // Test ECDSA 237 | func TestTokenECDSA(t *testing.T) { 238 | r := require.New(t) 239 | w := httptest.New(appECDSA()) 240 | 241 | // Missing Authorization 242 | res := w.HTML("/").Get() 243 | r.Equal(http.StatusUnauthorized, res.Code) 244 | 245 | // invalid token 246 | req := w.HTML("/") 247 | req.Headers["Authorization"] = "badcreds" 248 | res = req.Get() 249 | r.Equal(http.StatusUnauthorized, res.Code) 250 | r.Contains(res.Body.String(), "token invalid") 251 | 252 | // expired token 253 | privateKeyFile := envy.Get("JWT_PRIVATE_KEY", "test_certs/ec256-private.pem") 254 | key, err := ioutil.ReadFile(privateKeyFile) 255 | if err != nil { 256 | log.Fatal("error reading keyfile:", err) 257 | } 258 | parsedKey, err := jwt.ParseECPrivateKeyFromPEM(key) 259 | if err != nil { 260 | log.Fatal("error parsing key:", err) 261 | } 262 | claims := jwt.MapClaims{} 263 | claims["sub"] = "1234567890" 264 | claims["exp"] = time.Now().Add(-time.Minute * 5).Unix() 265 | token := jwt.NewWithClaims(jwt.SigningMethodES256, claims) 266 | tokenString, err := token.SignedString(parsedKey) 267 | if err != nil { 268 | log.Fatal("error signing token:", err) 269 | } 270 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 271 | res = req.Get() 272 | r.Equal(http.StatusUnauthorized, res.Code) 273 | r.Contains(res.Body.String(), "Token is expired") 274 | 275 | // valid token 276 | claims["exp"] = time.Now().Add(time.Minute * 5).Unix() 277 | token = jwt.NewWithClaims(jwt.SigningMethodES256, claims) 278 | tokenString, _ = token.SignedString(parsedKey) 279 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 280 | res = req.Get() 281 | r.Equal(http.StatusOK, res.Code) 282 | } 283 | 284 | // Test RSAPSS 285 | func TestTokenRSAPSS(t *testing.T) { 286 | r := require.New(t) 287 | w := httptest.New(appRSAPSS()) 288 | 289 | // Missing Authorization 290 | res := w.HTML("/").Get() 291 | r.Equal(http.StatusUnauthorized, res.Code) 292 | 293 | // invalid token 294 | req := w.HTML("/") 295 | req.Headers["Authorization"] = "badcreds" 296 | res = req.Get() 297 | r.Equal(http.StatusUnauthorized, res.Code) 298 | r.Contains(res.Body.String(), "token invalid") 299 | 300 | // expired token 301 | privateKeyFile := envy.Get("JWT_PRIVATE_KEY", "test_certs/sample_key") 302 | key, err := ioutil.ReadFile(privateKeyFile) 303 | if err != nil { 304 | log.Fatal(err) 305 | } 306 | parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key) 307 | if err != nil { 308 | log.Fatal("error parsing key:", err) 309 | } 310 | claims := jwt.MapClaims{} 311 | claims["sub"] = "1234567890" 312 | claims["exp"] = time.Now().Add(-time.Minute * 5).Unix() 313 | 314 | token := jwt.NewWithClaims(jwt.SigningMethodPS256, claims) 315 | tokenString, err := token.SignedString(parsedKey) 316 | if err != nil { 317 | log.Fatal("error signing token:", err) 318 | } 319 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 320 | res = req.Get() 321 | r.Equal(http.StatusUnauthorized, res.Code) 322 | r.Contains(res.Body.String(), "Token is expired") 323 | 324 | // valid token 325 | claims["exp"] = time.Now().Add(time.Minute * 5).Unix() 326 | token = jwt.NewWithClaims(jwt.SigningMethodPS256, claims) 327 | tokenString, _ = token.SignedString(parsedKey) 328 | req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", tokenString) 329 | res = req.Get() 330 | r.Equal(http.StatusOK, res.Code) 331 | } 332 | 333 | func TestAuthScheme(t *testing.T) { 334 | r := require.New(t) 335 | w := httptest.New(appCustomAuthScheme()) 336 | 337 | req := w.HTML("/") 338 | claims := jwt.MapClaims{} 339 | claims["sub"] = "1234567890" 340 | claims["exp"] = time.Now().Add(time.Minute * 5).Unix() 341 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 342 | secretKey := envy.Get("JWT_SECRET", "secret") 343 | tokenString, _ := token.SignedString([]byte(secretKey)) 344 | req.Headers["Authorization"] = fmt.Sprintf("Token %s", tokenString) 345 | res := req.Get() 346 | r.Equal(http.StatusOK, res.Code) 347 | } 348 | --------------------------------------------------------------------------------