{"id":37136142,"url":"https://github.com/kaancfidan/bouncer","last_synced_at":"2026-01-14T15:52:19.991Z","repository":{"id":44660149,"uuid":"265274368","full_name":"kaancfidan/bouncer","owner":"kaancfidan","description":"JWT-based authentication and authorization service","archived":false,"fork":false,"pushed_at":"2022-08-28T18:09:42.000Z","size":152,"stargazers_count":9,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-12T00:47:47.930Z","etag":null,"topics":["api-gateway","api-gateway-custom-authorizer","authentication","authorization","claims","jwt","sidecar-proxy"],"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/kaancfidan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-05-19T14:42:47.000Z","updated_at":"2023-02-09T04:22:22.000Z","dependencies_parsed_at":"2022-09-23T18:02:40.475Z","dependency_job_id":null,"html_url":"https://github.com/kaancfidan/bouncer","commit_stats":null,"previous_names":["kaancfidan/jwt-bouncer"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/kaancfidan/bouncer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaancfidan%2Fbouncer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaancfidan%2Fbouncer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaancfidan%2Fbouncer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaancfidan%2Fbouncer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kaancfidan","download_url":"https://codeload.github.com/kaancfidan/bouncer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kaancfidan%2Fbouncer/sbom","scorecard":{"id":546704,"data":{"date":"2025-08-11","repo":{"name":"github.com/kaancfidan/bouncer","commit":"dd386ee22869e1c3fc2a9b983bd701d052bad2a3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.1,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":2,"reason":"dependency not pinned by hash detected -- score normalized to 2","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/kaancfidan/bouncer/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/kaancfidan/bouncer/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/kaancfidan/bouncer/go.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/go.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/kaancfidan/bouncer/go.yml/master?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1","Warn: downloadThenRun not pinned by hash: .github/workflows/go.yml:32","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   1 out of   1 goCommand dependencies pinned","Info:   0 out of   1 downloadThenRun dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":2,"reason":"8 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2023-1859 / GHSA-rm8v-mxj3-5rmq","Warn: Project is vulnerable to: GO-2023-2379 / GHSA-7f9x-gw85-8grf","Warn: Project is vulnerable to: GO-2024-2454 / GHSA-pvcr-v8j8-j5q3","Warn: Project is vulnerable to: GO-2024-2632 / GHSA-hj3v-m684-v259","Warn: Project is vulnerable to: GO-2024-2961","Warn: Project is vulnerable to: GO-2023-2402 / GHSA-45x7-px36-x8w8","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 17 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T09:43:33.798Z","repository_id":44660149,"created_at":"2025-08-20T09:43:33.798Z","updated_at":"2025-08-20T09:43:33.798Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28425386,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T15:24:48.085Z","status":"ssl_error","status_checked_at":"2026-01-14T15:23:41.940Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["api-gateway","api-gateway-custom-authorizer","authentication","authorization","claims","jwt","sidecar-proxy"],"created_at":"2026-01-14T15:52:19.110Z","updated_at":"2026-01-14T15:52:19.981Z","avatar_url":"https://github.com/kaancfidan.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bouncer [![bouncer](https://raw.githubusercontent.com/kaancfidan/bouncer/master/gopher.png)](https://gopherize.me/gopher/c9a63ec34e1f313f408fc4aa378666cead40a271)\n[![Go](https://github.com/kaancfidan/bouncer/workflows/Go/badge.svg)](https://github.com/kaancfidan/bouncer/actions?query=workflow%3AGo) [![Docker Pulls](https://img.shields.io/docker/pulls/kaancfidan/bouncer)](https://hub.docker.com/r/kaancfidan/bouncer) [![Docker Image Size (latest semver)](https://img.shields.io/docker/image-size/kaancfidan/bouncer?sort=semver)](https://hub.docker.com/r/kaancfidan/bouncer) [![Go Report Card](https://goreportcard.com/badge/github.com/kaancfidan/bouncer)](https://goreportcard.com/report/github.com/kaancfidan/bouncer) [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/kaancfidan/bouncer)](https://codeclimate.com/github/kaancfidan/bouncer/maintainability) [![codecov](https://img.shields.io/codecov/c/github/kaancfidan/bouncer)](https://codecov.io/gh/kaancfidan/bouncer)\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkaancfidan%2Fbouncer.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fkaancfidan%2Fbouncer?ref=badge_shield)\n\nBouncer is a [JWT]-based authentication and authorization service. \n\n## Purpose\n**Bouncer** aims to move authentication- and authorization-related logic out of application codebases. \n\nAlthough it can be used alongside monolithic apps, it is much more relevant to distributed architectures where changing and redeploying tens or hundreds of services just for an added authorization policy (e.g. a new role) is not feasible. \n\n**Bouncer** has 2 main modes of operation:\n- Deployed as an authorization extension for an [API gateway]\n- Deployed as a [sidecar] reverse proxy alongside each application instance separately\n\n### Authorization extension\nWidely-used reverse proxy solutions, like [nginx], [traefik] and [envoy], all integrate with external authorization services to check authorization status of each request. \n\n[Open Policy Agent] is an excellent tool that provides authorization using [Rego] scripts and if you are in the market for a flexible solution, and think that investing time is worthwhile in your case, you should stop reading this text and check it out.\n\nAs always though, flexibility of **OPA** comes at a cost:\n- You need to invest time to understand Rego as a new (although relatively easy) scripting language.\n- Each deployment is a new implementation of probably very similar policies \u0026 logic (e.g. jwt expiration check). \n- Although it can be integrated with **envoy**[*](https://www.openpolicyagent.org/docs/latest/envoy-authorization/) via configuration, **nginx**[*](https://github.com/summerwind/opa-nginx-rbac) and **traefik**[*](https://github.com/containous/traefik/issues/4894) currently require development to integrate.\n\nAs an example, [this blog post](https://medium.com/etermax-technology/api-authorization-with-kubernetes-traefik-and-open-policy-agent-23647fc384a1) demonstrates an authorization service implemented using **OPA** as a dependency and integrating it to **traefik**. \n\n**Bouncer** is the easier-to-use alternative to **OPA** in this scenario for the following reasons:\n- It is configured with a simple [YAML].\n- It is more opinionated, so expect less flexibility.\n- It aims to be out-of-the-box compatible with **nginx**, **traefik** and **envoy** without any development effort.\n\n#### How it works\n- The **API gateway** receives an HTTP request from the client and forwards it (usually without including the body) to **Bouncer**.\n- **Bouncer** matches the request method and path (i.e. GET /stuff/) to configured **route policies**.\n- If the most specific\u003csup\u003e1\u003c/sup\u003e **route policy** that matches the request explicitly allows anonymous requests, a response with status code **200(OK)** is returned.  \n- **Bouncer** extracts the [Bearer] token and validates it for authentication. If authentication fails, a response with status code **401(Unauthorized)** is returned.\n- **Bouncer** extracts claims from the validated token and checks if all **claim policies** corresponding to the matched **route policies** are fulfilled. If not, a response with status code **403(Forbidden)** is returned.\n- After all these challenges are passed, a response with status code **200(OK)** is returned.\n- If the authorization response is successful, the **API gateway** forwards the request to the appropriate backend service. \n\n\u003csup\u003e1\u003c/sup\u003e The most specific route is the one that has the deepest path, the least number of wildcards and as a tie-breaker the one that specifies the request's method.\n\n### Sidecar reverse proxy\n**Bouncer** can also be deployed as a reverse proxy to intercept requests to your application and perform authentication \u0026 authorization challenges before forwarding them.\n\nThis deployment strategy can be seen as a convenience feature as not all HTTP servers are deployed to cloud clusters with API gateways.\n\n#### How it works\nWhen given an **upstream URL**, **Bouncer** performs the same checks as in the API gateway scenario, but rather than returning a response with **200(OK)**, it calls the upstream server. \n\n## Configuration \n**Bouncer** mostly borrows its design from [claims-based authorization in .NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-3.1). Comparing it to the original design: \n- **Bouncer** is more flexible in route configuration, because it uses standard wildcard patterns to match paths.\n- **Bouncer** is less flexible in claim policy configuration, because claim requirements can only be expressed in equality comparisons (and \"contains\" checks in case of array claims).\n\n### Examples\n#### Allow anonymous example\nThe following configuration depicts a system in which all requests are allowed in without any authentication, except DELETEs and the ones with intentions to destroy the server.\n\n```yaml\nclaimPolicies: {} \n\nroutePolicies: \n - path: /** \n   allowAnonymous: true \n - path: /** \n   methods: [DELETE] \n   allowAnonymous: false \n - path: /destroy/server \n   allowAnonymous: false\n```\n\n#### User management example\nThe following is a mock user management system in which:\n- The users are allowed to register themselves (anonymous requests allowed)\n- Every other route requires authentication\n- Deleting users also requires a special `permission` claim that:\n  - either has a value equal to `DeleteUser` as in `\"permission\": \"DeleteUser\"`\n  - or is an array that contains the `DeleteUser` value as in `\"permission\": [\"AddUser\", \"ModifyUser\", \"DeleteUser\"]`\n\n```yaml\nclaimPolicies: \n CanDeleteUsers: \n  - claim: permission\n    values: [DeleteUser] \n\nroutePolicies: \n - path: /users/* \n   methods: [DELETE] \n   policyName: CanDeleteUsers \n - path: /users/register \n   methods: [POST] \n   allowAnonymous: true\n```\n\n#### Employee example\nThe following configuration example is loosely based on the example provided in the [.NET Core docs](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-3.1):\n\n```yaml\nclaimPolicies:\n EmployeeOnly:\n  - claim: employee_number\n Founders:\n  - claim: employee_number\n    values: [1,2,3,4,5]\n HumanResources:\n  - claim: department\n    values: [HumanResources]\n\nroutePolicies:\n - path: /vacation/**\n   policyName: EmployeeOnly\n - path: /vacation/policy\n   allowAnonymous: true\n - path: /vacation/*/\n   methods: [PUT, PATCH]\n   policyName: Founders\n - path: /salary/**\n   policyName: EmployeeOnly\n - path: /salary/*/\n   methods: [PUT, PATCH]\n   policyName: HumanResources\n```\n\n## Current status\nBouncer has been used in production for a few months now in few undisclosed enterprise systems as an API gateway access control solution. It has been tested with [nginx], [envoy] and [traefik], and proven to support all of them. It seems stable, but needs different use-cases to be tested thoroughly.\n\n## Usage\n### Docker image\nCreate a volume directory:\n```zsh\n➜  ~ mkdir bouncer\n```\nPut a config YAML:\n```zsh\n➜  ~ echo \"claimPolicies: {}\\nroutePolicies: []\\n\" \u003e bouncer/config.yaml\n```\nRun bouncer:\n```zsh\n➜  ~ docker run \\\n--name bouncer \\\n-d \\\n--restart always \\\n-e BOUNCER_SIGNING_METHOD=HS512 \\\n-e BOUNCER_SIGNING_KEY=ThisIsSupposedToBeALongStringOfBytesLikeSixtyFourCharactersLong. \\\n-v `pwd`/bouncer:/etc/bouncer \\\nkaancfidan/bouncer:latest\n```\n\n### Environment variables and command line flags\nEvery startup setting has an environment variable and a CLI flag counterpart. \n\n| Environment Variable   | CLI Flag | Description                                                                                                                                           |\n|------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|\n| BOUNCER_SIGNING_KEY    | -k       | Signing key to be used to validate tokens. Consider setting this variable through a file for multiline keys. e.g. `BOUNCER_SIGNING_KEY=$(cat rsa.pub)` |\n| BOUNCER_SIGNING_ALG    | -a       | Signing algorithm. See accepted algorithms below.                                                                                                     |\n| BOUNCER_CONFIG_PATH    | -p       | Config YAML path. **default = /etc/bouncer/config.yaml**                                                                                              |\n| BOUNCER_LISTEN_ADDRESS | -l       | TCP listen address. **default = :3512**                                                                                                               |\n| BOUNCER_UPSTREAM_URL   | --url    | Upstream URL to be used in reverse proxy mode. If not set, Bouncer runs in pure auth server mode.                                                     |\n\n#### Accepted signature algorithms\n- ES256, ES256K, ES384, ES512, EdDSA\n- HS256, HS384, HS512\n- PS256, PS384, PS512\n- RS256, RS384, RS512\n\n## License\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkaancfidan%2Fbouncer.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fkaancfidan%2Fbouncer?ref=badge_large)\n\n\n[JWT]: http://jwt.io/introduction\n[sidecar]: https://docs.microsoft.com/en-us/azure/architecture/patterns/sidecar\n[API gateway]: https://microservices.io/patterns/apigateway.html\n[nginx]: https://www.nginx.com/\n[traefik]: https://containo.us/traefik/\n[envoy]: https://www.envoyproxy.io/\n[Open Policy Agent]: https://www.openpolicyagent.org/\n[OPA]: https://www.openpolicyagent.org/\n[Rego]: https://www.openpolicyagent.org/docs/latest/#rego\n[YAML]: https://yaml.org/\n[Bearer]: https://swagger.io/docs/specification/authentication/bearer-authentication/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaancfidan%2Fbouncer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkaancfidan%2Fbouncer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkaancfidan%2Fbouncer/lists"}