├── .github ├── dependabot.yml ├── labeler.yml ├── release-drafter.yml └── workflows │ ├── auto-labeler.yml │ ├── dependabot_automerge.yml │ ├── gotidy.yml │ ├── linter.yml │ ├── release-drafter.yml │ ├── security.yml │ └── test.yml ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── main.go └── main_test.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "gomod" 6 | directory: "/" # Location of package manifests 7 | labels: 8 | - "🤖 Dependencies" 9 | schedule: 10 | interval: daily 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: daily 15 | labels: 16 | - "🤖 Dependencies" 17 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | version: v1 2 | labels: 3 | - label: '📒 Documentation' 4 | matcher: 5 | title: '\b(docs|doc:|\[doc\]|README|typos|comment|documentation)\b' 6 | - label: '☢️ Bug' 7 | matcher: 8 | title: '\b(fix|race|bug|missing|correct)\b' 9 | - label: '🧹 Updates' 10 | matcher: 11 | title: '\b(improve|update|refactor|deprecated|remove|unused|test)\b' 12 | - label: '🤖 Dependencies' 13 | matcher: 14 | title: '\b(bumb|bdependencies)\b' 15 | - label: '✏️ Feature' 16 | matcher: 17 | title: '\b(feature|feat|create|implement|add)\b' 18 | - label: '🤔 Question' 19 | matcher: 20 | title: '\b(question|how)\b' 21 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: '🚀 New' 5 | labels: 6 | - '✏️ Feature' 7 | - title: '🧹 Updates' 8 | labels: 9 | - '🧹 Updates' 10 | - '🤖 Dependencies' 11 | - title: '🐛 Fixes' 12 | labels: 13 | - '☢️ Bug' 14 | - title: '📚 Documentation' 15 | labels: 16 | - '📒 Documentation' 17 | change-template: '- $TITLE (#$NUMBER)' 18 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 19 | version-resolver: 20 | major: 21 | labels: 22 | - 'major' 23 | minor: 24 | labels: 25 | - 'minor' 26 | - '✏️ Feature' 27 | patch: 28 | labels: 29 | - 'patch' 30 | - '📒 Documentation' 31 | - '☢️ Bug' 32 | - '🤖 Dependencies' 33 | - '🧹 Updates' 34 | default: patch 35 | template: | 36 | $CHANGES 37 | 38 | **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION 39 | 40 | Thank you $CONTRIBUTORS for making this update possible. 41 | -------------------------------------------------------------------------------- /.github/workflows/auto-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Auto labeler 2 | on: 3 | issues: 4 | types: [ opened, edited, milestoned ] 5 | pull_request_target: 6 | types: [ opened ] 7 | permissions: 8 | contents: read 9 | issues: write 10 | pull-requests: write 11 | statuses: write 12 | checks: write 13 | jobs: 14 | labeler: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Check Labels 18 | id: labeler 19 | uses: fuxingloh/multi-labeler@v2 20 | with: 21 | github-token: ${{secrets.GITHUB_TOKEN}} 22 | -------------------------------------------------------------------------------- /.github/workflows/dependabot_automerge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: 3 | pull_request 4 | 5 | permissions: 6 | contents: write 7 | pull-requests: write 8 | 9 | jobs: 10 | wait_for_checks: 11 | runs-on: ubuntu-latest 12 | if: ${{ github.actor == 'dependabot[bot]' }} 13 | steps: 14 | - name: Wait for check is finished 15 | uses: lewagon/wait-on-check-action@v1.3.1 16 | id: wait_for_checks 17 | with: 18 | ref: ${{ github.event.pull_request.head.sha || github.sha }} 19 | running-workflow-name: wait_for_checks 20 | check-regexp: Tests 21 | repo-token: ${{ secrets.PR_TOKEN }} 22 | wait-interval: 10 23 | dependabot: 24 | needs: [wait_for_checks] 25 | name: Dependabot auto-merge 26 | runs-on: ubuntu-latest 27 | if: ${{ github.actor == 'dependabot[bot]' }} 28 | steps: 29 | - name: Dependabot metadata 30 | id: metadata 31 | uses: dependabot/fetch-metadata@v1.4.0 32 | with: 33 | github-token: "${{ secrets.PR_TOKEN }}" 34 | - name: Enable auto-merge for Dependabot PRs 35 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 36 | run: | 37 | gh pr review --approve "$PR_URL" 38 | gh pr merge --auto --merge "$PR_URL" 39 | env: 40 | PR_URL: ${{github.event.pull_request.html_url}} 41 | GITHUB_TOKEN: ${{secrets.PR_TOKEN}} 42 | -------------------------------------------------------------------------------- /.github/workflows/gotidy.yml: -------------------------------------------------------------------------------- 1 | name: Tidy 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | paths: 8 | - '.github/workflows/gotidy.yml' 9 | - 'go.mod' 10 | - 'go.sum' 11 | 12 | jobs: 13 | fix: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - 17 | name: Checkout 18 | uses: actions/checkout@v3.5.2 19 | - 20 | name: Set up Go 21 | uses: actions/setup-go@v4 22 | with: 23 | go-version: 1.17 24 | - 25 | name: Tidy 26 | run: | 27 | rm -f go.sum 28 | go mod tidy 29 | - 30 | name: Set up Git 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | run: | 34 | git config user.name GitHub 35 | git config user.email noreply@github.com 36 | git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git 37 | - 38 | name: Commit and push changes 39 | run: | 40 | git add . 41 | if output=$(git status --porcelain) && [ ! -z "$output" ]; then 42 | git commit --author "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" --message "Fix go modules" 43 | git push 44 | fi 45 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | - main 6 | pull_request: 7 | name: Linter 8 | jobs: 9 | Golint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Fetch Repository 13 | uses: actions/checkout@v3.5.2 14 | - name: Run Golint 15 | uses: reviewdog/action-golangci-lint@v2 16 | with: 17 | golangci_lint_flags: "--tests=false" 18 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | - main 9 | 10 | jobs: 11 | update_release_draft: 12 | runs-on: ubuntu-latest 13 | steps: 14 | # (Optional) GitHub Enterprise requires GHE_HOST variable set 15 | #- name: Set GHE_HOST 16 | # run: | 17 | # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV 18 | 19 | # Drafts your next Release notes as Pull Requests are merged into "master" 20 | - uses: release-drafter/release-drafter@v5 21 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 22 | # with: 23 | # config-name: my-config.yml 24 | # disable-autolabeler: true 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | -------------------------------------------------------------------------------- /.github/workflows/security.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | - main 6 | pull_request: 7 | name: Security 8 | jobs: 9 | Gosec: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Fetch Repository 13 | uses: actions/checkout@v3.5.2 14 | - name: Run Gosec 15 | uses: securego/gosec@master 16 | with: 17 | args: ./... 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | name: Tests 7 | jobs: 8 | Tests: 9 | name: Tests 10 | strategy: 11 | matrix: 12 | go-version: 13 | - 1.17.x 14 | - 1.18.x 15 | - 1.19.x 16 | - 1.20.x 17 | platform: 18 | - ubuntu-latest 19 | - windows-latest 20 | - macos-latest 21 | runs-on: '${{ matrix.platform }}' 22 | steps: 23 | - name: Install Go 24 | uses: actions/setup-go@v4 25 | with: 26 | go-version: ${{ matrix.go-version }} 27 | - name: Setup Golang caches 28 | uses: actions/cache@v3 29 | with: 30 | # In order: 31 | # * Module download cache 32 | # * Build cache (Linux) 33 | # * Build cache (Mac) 34 | # * Build cache (Windows) 35 | path: | 36 | ~/go/pkg/mod 37 | ~/.cache/go-build 38 | ~/Library/Caches/go-build 39 | ~\AppData\Local\go-build 40 | key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }} 41 | restore-keys: | 42 | ${{ runner.os }}-go-${{ matrix.go-version }}- 43 | - name: Fetch Repository 44 | uses: actions/checkout@v3.5.2 45 | - name: Run Test 46 | run: go test ./... -v -race 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Fiber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ Deprecated repository 2 | 3 | This middleware is no longer maintained, it is available within [Fiber v2](https://github.com/gofiber/fiber/tree/master/middleware/keyauth). 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // Deprecated: Now part of the fiber core repo: 2 | // github.com/gofiber/fiber/v2/middleware/keyauth 3 | module github.com/gofiber/keyauth/v2 4 | 5 | go 1.17 6 | 7 | require github.com/gofiber/fiber/v2 v2.45.0 8 | 9 | require ( 10 | github.com/andybalholm/brotli v1.0.5 // indirect 11 | github.com/google/uuid v1.3.0 // indirect 12 | github.com/klauspost/compress v1.16.3 // indirect 13 | github.com/mattn/go-colorable v0.1.13 // indirect 14 | github.com/mattn/go-isatty v0.0.18 // indirect 15 | github.com/mattn/go-runewidth v0.0.14 // indirect 16 | github.com/philhofer/fwd v1.1.2 // indirect 17 | github.com/rivo/uniseg v0.2.0 // indirect 18 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect 19 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect 20 | github.com/tinylib/msgp v1.1.8 // indirect 21 | github.com/valyala/bytebufferpool v1.0.0 // indirect 22 | github.com/valyala/fasthttp v1.47.0 // indirect 23 | github.com/valyala/tcplisten v1.0.0 // indirect 24 | golang.org/x/sys v0.8.0 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= 2 | github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 3 | github.com/gofiber/fiber/v2 v2.45.0 h1:p4RpkJT9GAW6parBSbcNFH2ApnAuW3OzaQzbOCoDu+s= 4 | github.com/gofiber/fiber/v2 v2.45.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc= 5 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 6 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= 8 | github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 9 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 10 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 11 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 12 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= 13 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 14 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 15 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 16 | github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 17 | github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= 18 | github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= 19 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 20 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 21 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= 22 | github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= 23 | github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= 24 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= 25 | github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= 26 | github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= 27 | github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= 28 | github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= 29 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 30 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 31 | github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= 32 | github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= 33 | github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= 34 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 35 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 36 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 37 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 38 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 39 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 40 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 41 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 42 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 43 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 44 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 45 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 46 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 47 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 48 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 49 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 50 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 51 | golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 52 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 53 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 54 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 55 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 56 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 57 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 58 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 59 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 60 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 61 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 62 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 63 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 64 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 65 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 66 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 67 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 68 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 69 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 70 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 72 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 73 | golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= 74 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 75 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 76 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 77 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 78 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 79 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 80 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 81 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 82 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 83 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 84 | golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 85 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 86 | golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= 87 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 88 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 89 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 90 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 91 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // 🚀 Fiber is an Express inspired web framework written in Go with 💖 2 | // 📌 API Documentation: https://fiber.wiki 3 | // 📝 Github Repository: https://github.com/gofiber/fiber 4 | // Special thanks to Echo: https://github.com/labstack/echo/blob/master/middleware/key_auth.go 5 | package keyauth 6 | 7 | import ( 8 | "errors" 9 | "net/url" 10 | "strings" 11 | 12 | "github.com/gofiber/fiber/v2" 13 | ) 14 | 15 | var ( 16 | // When there is no request of the key thrown ErrMissingOrMalformedAPIKey 17 | ErrMissingOrMalformedAPIKey = errors.New("missing or malformed API Key") 18 | ) 19 | 20 | type Config struct { 21 | // Filter defines a function to skip middleware. 22 | // Optional. Default: nil 23 | Filter func(*fiber.Ctx) bool 24 | 25 | // SuccessHandler defines a function which is executed for a valid key. 26 | // Optional. Default: nil 27 | SuccessHandler fiber.Handler 28 | 29 | // ErrorHandler defines a function which is executed for an invalid key. 30 | // It may be used to define a custom error. 31 | // Optional. Default: 401 Invalid or expired key 32 | ErrorHandler fiber.ErrorHandler 33 | 34 | // KeyLookup is a string in the form of ":" that is used 35 | // to extract key from the request. 36 | // Optional. Default value "header:Authorization". 37 | // Possible values: 38 | // - "header:" 39 | // - "query:" 40 | // - "form:" 41 | // - "param:" 42 | // - "cookie:" 43 | KeyLookup string 44 | 45 | // AuthScheme to be used in the Authorization header. 46 | // Optional. Default value "Bearer". 47 | AuthScheme string 48 | 49 | // Validator is a function to validate key. 50 | Validator func(*fiber.Ctx, string) (bool, error) 51 | 52 | // Context key to store the bearertoken from the token into context. 53 | // Optional. Default: "token". 54 | ContextKey string 55 | } 56 | 57 | // New ... 58 | func New(config ...Config) fiber.Handler { 59 | // Init config 60 | var cfg Config 61 | if len(config) > 0 { 62 | cfg = config[0] 63 | } 64 | 65 | if cfg.SuccessHandler == nil { 66 | cfg.SuccessHandler = func(c *fiber.Ctx) error { 67 | return c.Next() 68 | } 69 | } 70 | if cfg.ErrorHandler == nil { 71 | cfg.ErrorHandler = func(c *fiber.Ctx, err error) error { 72 | if err == ErrMissingOrMalformedAPIKey { 73 | return c.Status(fiber.StatusUnauthorized).SendString(err.Error()) 74 | } 75 | return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired API Key") 76 | } 77 | } 78 | if cfg.KeyLookup == "" { 79 | cfg.KeyLookup = "header:" + fiber.HeaderAuthorization 80 | // set AuthScheme as "Bearer" only if KeyLookup is set to default. 81 | if cfg.AuthScheme == "" { 82 | cfg.AuthScheme = "Bearer" 83 | } 84 | } 85 | if cfg.Validator == nil { 86 | panic("fiber: keyauth middleware requires a validator function") 87 | } 88 | if cfg.ContextKey == "" { 89 | cfg.ContextKey = "token" 90 | } 91 | 92 | // Initialize 93 | parts := strings.Split(cfg.KeyLookup, ":") 94 | extractor := keyFromHeader(parts[1], cfg.AuthScheme) 95 | switch parts[0] { 96 | case "query": 97 | extractor = keyFromQuery(parts[1]) 98 | case "form": 99 | extractor = keyFromForm(parts[1]) 100 | case "param": 101 | extractor = keyFromParam(parts[1]) 102 | case "cookie": 103 | extractor = keyFromCookie(parts[1]) 104 | } 105 | 106 | // Return middleware handler 107 | return func(c *fiber.Ctx) error { 108 | // Filter request to skip middleware 109 | if cfg.Filter != nil && cfg.Filter(c) { 110 | return c.Next() 111 | } 112 | 113 | // Extract and verify key 114 | key, err := extractor(c) 115 | if err != nil { 116 | return cfg.ErrorHandler(c, err) 117 | } 118 | 119 | valid, err := cfg.Validator(c, key) 120 | 121 | if err == nil && valid { 122 | c.Locals(cfg.ContextKey, key) 123 | return cfg.SuccessHandler(c) 124 | } 125 | return cfg.ErrorHandler(c, err) 126 | } 127 | } 128 | 129 | // keyFromHeader returns a function that extracts api key from the request header. 130 | func keyFromHeader(header string, authScheme string) func(c *fiber.Ctx) (string, error) { 131 | return func(c *fiber.Ctx) (string, error) { 132 | auth := c.Get(header) 133 | l := len(authScheme) 134 | if len(auth) > 0 && l == 0 { 135 | return auth, nil 136 | } 137 | if len(auth) > l+1 && auth[:l] == authScheme { 138 | return auth[l+1:], nil 139 | } 140 | return "", ErrMissingOrMalformedAPIKey 141 | } 142 | } 143 | 144 | // keyFromQuery returns a function that extracts api key from the query string. 145 | func keyFromQuery(param string) func(c *fiber.Ctx) (string, error) { 146 | return func(c *fiber.Ctx) (string, error) { 147 | key := c.Query(param) 148 | if key == "" { 149 | return "", ErrMissingOrMalformedAPIKey 150 | } 151 | return key, nil 152 | } 153 | } 154 | 155 | // keyFromForm returns a function that extracts api key from the form. 156 | func keyFromForm(param string) func(c *fiber.Ctx) (string, error) { 157 | return func(c *fiber.Ctx) (string, error) { 158 | key := c.FormValue(param) 159 | if key == "" { 160 | return "", ErrMissingOrMalformedAPIKey 161 | } 162 | return key, nil 163 | } 164 | } 165 | 166 | // keyFromParam returns a function that extracts api key from the url param string. 167 | func keyFromParam(param string) func(c *fiber.Ctx) (string, error) { 168 | return func(c *fiber.Ctx) (string, error) { 169 | key, err := url.PathUnescape(c.Params(param)) 170 | if err != nil { 171 | return "", ErrMissingOrMalformedAPIKey 172 | } 173 | return key, nil 174 | } 175 | } 176 | 177 | // keyFromCookie returns a function that extracts api key from the named cookie. 178 | func keyFromCookie(name string) func(c *fiber.Ctx) (string, error) { 179 | return func(c *fiber.Ctx) (string, error) { 180 | key := c.Cookies(name) 181 | if key == "" { 182 | return "", ErrMissingOrMalformedAPIKey 183 | } 184 | return key, nil 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | // 🚀 Fiber is an Express inspired web framework written in Go with 💖 2 | // 📌 API Documentation: https://fiber.wiki 3 | // 📝 Github Repository: https://github.com/gofiber/fiber 4 | 5 | package keyauth 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "net/http" 11 | "net/http/httptest" 12 | "net/url" 13 | "testing" 14 | 15 | "github.com/gofiber/fiber/v2" 16 | "github.com/gofiber/fiber/v2/utils" 17 | ) 18 | 19 | const CorrectKey = "specials: !$%,.#\"!?~`<>@$^*(){}[]|/\\123" 20 | 21 | func TestAuthSources(t *testing.T) { 22 | // define test cases 23 | testSources := []string{"header", "cookie", "query", "param", "form"} 24 | 25 | tests := []struct { 26 | route string 27 | authTokenName string 28 | description string 29 | APIKey string 30 | expectedCode int 31 | expectedBody string 32 | }{ 33 | { 34 | route: "/", 35 | authTokenName: "access_token", 36 | description: "auth with correct key", 37 | APIKey: CorrectKey, 38 | expectedCode: 200, 39 | expectedBody: "Success!", 40 | }, 41 | { 42 | route: "/", 43 | authTokenName: "access_token", 44 | description: "auth with no key", 45 | APIKey: "", 46 | expectedCode: 401, // 404 in case of param authentication 47 | expectedBody: "missing or malformed API Key", 48 | }, 49 | { 50 | route: "/", 51 | authTokenName: "access_token", 52 | description: "auth with wrong key", 53 | APIKey: "WRONGKEY", 54 | expectedCode: 401, 55 | expectedBody: "missing or malformed API Key", 56 | }, 57 | } 58 | 59 | for _, authSource := range testSources { 60 | t.Run(authSource, func(t *testing.T) { 61 | for _, test := range tests { 62 | // setup the fiber endpoint 63 | // note that if UnescapePath: false (the default) 64 | // escaped characters (such as `\"`) will not be handled correctly in the tests 65 | app := fiber.New(fiber.Config{UnescapePath: true}) 66 | 67 | authMiddleware := New(Config{ 68 | KeyLookup: authSource + ":" + test.authTokenName, 69 | Validator: func(c *fiber.Ctx, key string) (bool, error) { 70 | if key == CorrectKey { 71 | return true, nil 72 | } 73 | return false, ErrMissingOrMalformedAPIKey 74 | }, 75 | }) 76 | 77 | var route string 78 | if authSource == "param" { 79 | route = test.route + ":" + test.authTokenName 80 | app.Use(route, authMiddleware) 81 | } else { 82 | route = test.route 83 | app.Use(authMiddleware) 84 | } 85 | 86 | app.Get(route, func(c *fiber.Ctx) error { 87 | return c.SendString("Success!") 88 | }) 89 | 90 | // construct the test HTTP request 91 | var req *http.Request 92 | req, _ = http.NewRequest("GET", test.route, nil) 93 | 94 | // setup the apikey for the different auth schemes 95 | if authSource == "header" { 96 | 97 | req.Header.Set(test.authTokenName, test.APIKey) 98 | 99 | } else if authSource == "cookie" { 100 | 101 | req.Header.Set("Cookie", test.authTokenName+"="+test.APIKey) 102 | 103 | } else if authSource == "query" || authSource == "form" { 104 | 105 | q := req.URL.Query() 106 | q.Add(test.authTokenName, test.APIKey) 107 | req.URL.RawQuery = q.Encode() 108 | 109 | } else if authSource == "param" { 110 | 111 | r := req.URL.Path 112 | r = r + url.PathEscape(test.APIKey) 113 | req.URL.Path = r 114 | 115 | } 116 | 117 | res, err := app.Test(req, -1) 118 | 119 | utils.AssertEqual(t, nil, err, test.description) 120 | 121 | // test the body of the request 122 | body, err := io.ReadAll(res.Body) 123 | // for param authentication, the route would be /:access_token 124 | // when the access_token is empty, it leads to a 404 (not found) 125 | // not a 401 (auth error) 126 | if authSource == "param" && test.APIKey == "" { 127 | test.expectedCode = 404 128 | test.expectedBody = "Cannot GET /" 129 | } 130 | utils.AssertEqual(t, test.expectedCode, res.StatusCode, test.description) 131 | 132 | // body 133 | utils.AssertEqual(t, nil, err, test.description) 134 | utils.AssertEqual(t, test.expectedBody, string(body), test.description) 135 | } 136 | }) 137 | } 138 | } 139 | 140 | func TestMultipleKeyAuth(t *testing.T) { 141 | // setup the fiber endpoint 142 | app := fiber.New() 143 | 144 | // setup keyauth for /auth1 145 | app.Use(New(Config{ 146 | Filter: func(c *fiber.Ctx) bool { 147 | return c.OriginalURL() != "/auth1" 148 | }, 149 | KeyLookup: "header:key", 150 | Validator: func(c *fiber.Ctx, key string) (bool, error) { 151 | if key == "password1" { 152 | return true, nil 153 | } 154 | return false, ErrMissingOrMalformedAPIKey 155 | }, 156 | })) 157 | 158 | // setup keyauth for /auth2 159 | app.Use(New(Config{ 160 | Filter: func(c *fiber.Ctx) bool { 161 | return c.OriginalURL() != "/auth2" 162 | }, 163 | KeyLookup: "header:key", 164 | Validator: func(c *fiber.Ctx, key string) (bool, error) { 165 | if key == "password2" { 166 | return true, nil 167 | } 168 | return false, ErrMissingOrMalformedAPIKey 169 | }, 170 | })) 171 | 172 | app.Get("/", func(c *fiber.Ctx) error { 173 | return c.SendString("No auth needed!") 174 | }) 175 | 176 | app.Get("/auth1", func(c *fiber.Ctx) error { 177 | return c.SendString("Successfully authenticated for auth1!") 178 | }) 179 | 180 | app.Get("/auth2", func(c *fiber.Ctx) error { 181 | return c.SendString("Successfully authenticated for auth2!") 182 | }) 183 | 184 | // define test cases 185 | tests := []struct { 186 | route string 187 | description string 188 | APIKey string 189 | expectedCode int 190 | expectedBody string 191 | }{ 192 | // No auth needed for / 193 | { 194 | route: "/", 195 | description: "No password needed", 196 | APIKey: "", 197 | expectedCode: 200, 198 | expectedBody: "No auth needed!", 199 | }, 200 | 201 | // auth needed for auth1 202 | { 203 | route: "/auth1", 204 | description: "Normal Authentication Case", 205 | APIKey: "password1", 206 | expectedCode: 200, 207 | expectedBody: "Successfully authenticated for auth1!", 208 | }, 209 | { 210 | route: "/auth1", 211 | description: "Wrong API Key", 212 | APIKey: "WRONG KEY", 213 | expectedCode: 401, 214 | expectedBody: "missing or malformed API Key", 215 | }, 216 | { 217 | route: "/auth1", 218 | description: "Wrong API Key", 219 | APIKey: "", // NO KEY 220 | expectedCode: 401, 221 | expectedBody: "missing or malformed API Key", 222 | }, 223 | 224 | // Auth 2 has a different password 225 | { 226 | route: "/auth2", 227 | description: "Normal Authentication Case for auth2", 228 | APIKey: "password2", 229 | expectedCode: 200, 230 | expectedBody: "Successfully authenticated for auth2!", 231 | }, 232 | { 233 | route: "/auth2", 234 | description: "Wrong API Key", 235 | APIKey: "WRONG KEY", 236 | expectedCode: 401, 237 | expectedBody: "missing or malformed API Key", 238 | }, 239 | { 240 | route: "/auth2", 241 | description: "Wrong API Key", 242 | APIKey: "", // NO KEY 243 | expectedCode: 401, 244 | expectedBody: "missing or malformed API Key", 245 | }, 246 | } 247 | 248 | // run the tests 249 | for _, test := range tests { 250 | var req *http.Request 251 | req, _ = http.NewRequest("GET", test.route, nil) 252 | if test.APIKey != "" { 253 | req.Header.Set("key", test.APIKey) 254 | } 255 | 256 | res, err := app.Test(req, -1) 257 | 258 | utils.AssertEqual(t, nil, err, test.description) 259 | 260 | // test the body of the request 261 | body, err := io.ReadAll(res.Body) 262 | utils.AssertEqual(t, test.expectedCode, res.StatusCode, test.description) 263 | 264 | // body 265 | utils.AssertEqual(t, nil, err, test.description) 266 | utils.AssertEqual(t, test.expectedBody, string(body), test.description) 267 | } 268 | } 269 | 270 | func TestCustomSuccessAndFailureHandlers(t *testing.T) { 271 | app := fiber.New() 272 | 273 | app.Use(New(Config{ 274 | SuccessHandler: func(c *fiber.Ctx) error { 275 | return c.Status(fiber.StatusOK).SendString("API key is valid and request was handled by custom success handler") 276 | }, 277 | ErrorHandler: func(c *fiber.Ctx, err error) error { 278 | return c.Status(fiber.StatusUnauthorized).SendString("API key is invalid and request was handled by custom error handler") 279 | }, 280 | Validator: func(c *fiber.Ctx, key string) (bool, error) { 281 | if key == CorrectKey { 282 | return true, nil 283 | } 284 | return false, ErrMissingOrMalformedAPIKey 285 | }, 286 | })) 287 | 288 | // Define a test handler that should not be called 289 | app.Get("/", func(c *fiber.Ctx) error { 290 | t.Error("Test handler should not be called") 291 | return nil 292 | }) 293 | 294 | // Create a request without an API key and send it to the app 295 | res, err := app.Test(httptest.NewRequest("GET", "/", nil)) 296 | if err != nil { 297 | t.Error(err) 298 | } 299 | 300 | // Read the response body into a string 301 | body, _ := io.ReadAll(res.Body) 302 | 303 | // Check that the response has the expected status code and body 304 | utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) 305 | utils.AssertEqual(t, string(body), "API key is invalid and request was handled by custom error handler") 306 | 307 | // Create a request with a valid API key in the Authorization header 308 | req := httptest.NewRequest("GET", "/", nil) 309 | req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", CorrectKey)) 310 | 311 | // Send the request to the app 312 | res, err = app.Test(req) 313 | if err != nil { 314 | t.Error(err) 315 | } 316 | 317 | // Read the response body into a string 318 | body, _ = io.ReadAll(res.Body) 319 | 320 | // Check that the response has the expected status code and body 321 | utils.AssertEqual(t, res.StatusCode, http.StatusOK) 322 | utils.AssertEqual(t, string(body), "API key is valid and request was handled by custom success handler") 323 | } 324 | 325 | func TestCustomFilterFunc(t *testing.T) { 326 | app := fiber.New() 327 | 328 | app.Use(New(Config{ 329 | Filter: func(c *fiber.Ctx) bool { 330 | return c.Path() == "/allowed" 331 | }, 332 | Validator: func(c *fiber.Ctx, key string) (bool, error) { 333 | if key == CorrectKey { 334 | return true, nil 335 | } 336 | return false, ErrMissingOrMalformedAPIKey 337 | }, 338 | })) 339 | 340 | // Define a test handler 341 | app.Get("/allowed", func(c *fiber.Ctx) error { 342 | return c.SendString("API key is valid and request was allowed by custom filter") 343 | }) 344 | 345 | // Create a request with the "/allowed" path and send it to the app 346 | req := httptest.NewRequest("GET", "/allowed", nil) 347 | res, err := app.Test(req) 348 | if err != nil { 349 | t.Error(err) 350 | } 351 | 352 | // Read the response body into a string 353 | body, _ := io.ReadAll(res.Body) 354 | 355 | // Check that the response has the expected status code and body 356 | utils.AssertEqual(t, res.StatusCode, http.StatusOK) 357 | utils.AssertEqual(t, string(body), "API key is valid and request was allowed by custom filter") 358 | 359 | // Create a request with a different path and send it to the app without correct key 360 | req = httptest.NewRequest("GET", "/not-allowed", nil) 361 | res, err = app.Test(req) 362 | if err != nil { 363 | t.Error(err) 364 | } 365 | 366 | // Read the response body into a string 367 | body, _ = io.ReadAll(res.Body) 368 | 369 | // Check that the response has the expected status code and body 370 | utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) 371 | utils.AssertEqual(t, string(body), ErrMissingOrMalformedAPIKey.Error()) 372 | 373 | // Create a request with a different path and send it to the app with correct key 374 | req = httptest.NewRequest("GET", "/not-allowed", nil) 375 | req.Header.Add("Authorization", fmt.Sprintf("Basic %s", CorrectKey)) 376 | 377 | res, err = app.Test(req) 378 | if err != nil { 379 | t.Error(err) 380 | } 381 | 382 | // Read the response body into a string 383 | body, _ = io.ReadAll(res.Body) 384 | 385 | // Check that the response has the expected status code and body 386 | utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) 387 | utils.AssertEqual(t, string(body), ErrMissingOrMalformedAPIKey.Error()) 388 | } 389 | 390 | func TestAuthSchemeToken(t *testing.T) { 391 | app := fiber.New() 392 | 393 | app.Use(New(Config{ 394 | AuthScheme: "Token", 395 | Validator: func(c *fiber.Ctx, key string) (bool, error) { 396 | if key == CorrectKey { 397 | return true, nil 398 | } 399 | return false, ErrMissingOrMalformedAPIKey 400 | }, 401 | })) 402 | 403 | // Define a test handler 404 | app.Get("/", func(c *fiber.Ctx) error { 405 | return c.SendString("API key is valid") 406 | }) 407 | 408 | // Create a request with a valid API key in the "Token" Authorization header 409 | req := httptest.NewRequest("GET", "/", nil) 410 | req.Header.Add("Authorization", fmt.Sprintf("Token %s", CorrectKey)) 411 | 412 | // Send the request to the app 413 | res, err := app.Test(req) 414 | if err != nil { 415 | t.Error(err) 416 | } 417 | 418 | // Read the response body into a string 419 | body, _ := io.ReadAll(res.Body) 420 | 421 | // Check that the response has the expected status code and body 422 | utils.AssertEqual(t, res.StatusCode, http.StatusOK) 423 | utils.AssertEqual(t, string(body), "API key is valid") 424 | } 425 | 426 | func TestAuthSchemeBasic(t *testing.T) { 427 | app := fiber.New() 428 | 429 | app.Use(New(Config{ 430 | KeyLookup: "header:Authorization", 431 | AuthScheme: "Basic", 432 | Validator: func(c *fiber.Ctx, key string) (bool, error) { 433 | if key == CorrectKey { 434 | return true, nil 435 | } 436 | return false, ErrMissingOrMalformedAPIKey 437 | }, 438 | })) 439 | 440 | // Define a test handler 441 | app.Get("/", func(c *fiber.Ctx) error { 442 | return c.SendString("API key is valid") 443 | }) 444 | 445 | // Create a request without an API key and Send the request to the app 446 | res, err := app.Test(httptest.NewRequest("GET", "/", nil)) 447 | if err != nil { 448 | t.Error(err) 449 | } 450 | 451 | // Read the response body into a string 452 | body, _ := io.ReadAll(res.Body) 453 | 454 | // Check that the response has the expected status code and body 455 | utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) 456 | utils.AssertEqual(t, string(body), ErrMissingOrMalformedAPIKey.Error()) 457 | 458 | // Create a request with a valid API key in the "Authorization" header using the "Basic" scheme 459 | req := httptest.NewRequest("GET", "/", nil) 460 | req.Header.Add("Authorization", fmt.Sprintf("Basic %s", CorrectKey)) 461 | 462 | // Send the request to the app 463 | res, err = app.Test(req) 464 | if err != nil { 465 | t.Error(err) 466 | } 467 | 468 | // Read the response body into a string 469 | body, _ = io.ReadAll(res.Body) 470 | 471 | // Check that the response has the expected status code and body 472 | utils.AssertEqual(t, res.StatusCode, http.StatusOK) 473 | utils.AssertEqual(t, string(body), "API key is valid") 474 | } 475 | --------------------------------------------------------------------------------