{"id":19206796,"url":"https://github.com/hausgold/grape-jwt-authentication","last_synced_at":"2025-04-05T05:06:39.303Z","repository":{"id":26891477,"uuid":"111461339","full_name":"hausgold/grape-jwt-authentication","owner":"hausgold","description":"A reusable Grape JWT authentication concern","archived":false,"fork":false,"pushed_at":"2025-03-06T06:48:08.000Z","size":178,"stargazers_count":33,"open_issues_count":1,"forks_count":9,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-29T04:08:14.757Z","etag":null,"topics":["authentification","concern","gem","grape","grape-api","jwt","middleware","oss","rack","ruby-gem"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/grape-jwt-authentication","language":"Ruby","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/hausgold.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-20T20:49:56.000Z","updated_at":"2025-03-06T06:48:11.000Z","dependencies_parsed_at":"2024-01-16T13:48:17.294Z","dependency_job_id":"1b9b06e6-fa1f-4a89-aef6-db7883f15dd1","html_url":"https://github.com/hausgold/grape-jwt-authentication","commit_stats":{"total_commits":65,"total_committers":5,"mean_commits":13.0,"dds":"0.19999999999999996","last_synced_commit":"4716642b367b636bcec1925e7e29468e7349295f"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fgrape-jwt-authentication","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fgrape-jwt-authentication/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fgrape-jwt-authentication/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Fgrape-jwt-authentication/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hausgold","download_url":"https://codeload.github.com/hausgold/grape-jwt-authentication/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289427,"owners_count":20914464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentification","concern","gem","grape","grape-api","jwt","middleware","oss","rack","ruby-gem"],"created_at":"2024-11-09T13:17:00.848Z","updated_at":"2025-04-05T05:06:39.283Z","avatar_url":"https://github.com/hausgold.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"![grape-jwt-authentication](doc/assets/project.svg)\n\n[![Continuous Integration](https://github.com/hausgold/grape-jwt-authentication/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/hausgold/grape-jwt-authentication/actions/workflows/test.yml)\n[![Gem Version](https://badge.fury.io/rb/grape-jwt-authentication.svg)](https://badge.fury.io/rb/grape-jwt-authentication)\n[![Test Coverage](https://automate-api.hausgold.de/v1/coverage_reports/grape-jwt-authentication/coverage.svg)](https://knowledge.hausgold.de/coverage)\n[![Test Ratio](https://automate-api.hausgold.de/v1/coverage_reports/grape-jwt-authentication/ratio.svg)](https://knowledge.hausgold.de/coverage)\n[![API docs](https://automate-api.hausgold.de/v1/coverage_reports/grape-jwt-authentication/documentation.svg)](https://www.rubydoc.info/gems/grape-jwt-authentication)\n\nThis gem is dedicated to easily integrate a JWT authentication to your\n[Grape](https://github.com/ruby-grape/grape) API. The real authentication\nfunctionality must be provided by the user and this makes this gem highly\nflexible on the JWT verification level.\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Grape API](#grape-api)\n    - [Helpers](#helpers)\n  - [Configuration](#configuration)\n    - [Authenticator](#authenticator)\n    - [Malformed token handling](#malformed-token-handling)\n    - [Failed authentication handling](#failed-authentication-handling)\n    - [RSA public key helper](#rsa-public-key-helper)\n      - [RSA public key location (URL)](#rsa-public-key-location-url)\n      - [RSA public key caching](#rsa-public-key-caching)\n      - [RSA public key cache expiration](#rsa-public-key-cache-expiration)\n    - [JWT instance helper](#jwt-instance-helper)\n      - [Issuer verification](#issuer-verification)\n      - [Beholder (audience) verification](#beholder-audience-verification)\n      - [Custom JWT verification options](#custom-jwt-verification-options)\n      - [Custom JWT verification key](#custom-jwt-verification-key)\n  - [Per-API configuration](#per-api-configuration)\n  - [Full RSA256 example](#full-rsa256-example)\n- [Development](#development)\n- [Code of Conduct](#code-of-conduct)\n- [Contributing](#contributing)\n- [Releasing](#releasing)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'grape-jwt-authentication'\n```\n\nAnd then execute:\n\n```bash\n$ bundle\n```\n\nOr install it yourself as:\n\n```bash\n$ gem install grape-jwt-authentication\n```\n\n## Usage\n\n### Grape API\n\nYou can enable the JWT authentication on any Grape API you like. This includes\nspecific endpoints or a whole API. Just include the\n`Grape::Jwt::Authentication` module and configure it the way you like.\n\n```ruby\nmodule UserApi\n  class ApiV1 \u003c Grape::API\n    # All your fancy Grape API stuff [..]\n    version 'v1', using: :path\n\n    # Enable JWT authentication on this API\n    include Grape::Jwt::Authentication\n    auth :jwt\n  end\nend\n```\n\n#### Helpers\n\nThe inclusion of the `Grape::Jwt::Authentication` inserts some helpers to\naccess the parsed and original JWT. This can be handy when you need to work\nwith the JWT payload or perform some extra calculations with the expiration\ndate of it.  The following example demonstrated the usage of the helpers.\n\n```ruby\nmodule UserApi\n  class ApiV1 \u003c Grape::API\n    # All your fancy Grape API stuff [..]\n    version 'v1', using: :path\n\n    resource :payload do\n      desc 'A JWT payload echo service.'\n      get do\n        # The parsed JWT which has an accessible payload (RecursiveOpenStruct)\n        { payload: request_jwt.payload.to_h }\n      end\n    end\n\n    resource :token do\n      desc 'A JWT echo service.'\n      get do\n        # The original JWT parsed from the HTTP authorization header\n        { token: original_request_jwt }\n      end\n    end\n\n    # Enable JWT authentication on this API\n    include Grape::Jwt::Authentication\n    auth :jwt\n  end\nend\n```\n\n### Configuration\n\nThis gem is quite customizable and flexible to fulfill your needs. You can make\nuse of some parts and leave other if you do not care about them. We are not\ngoing to force the way how to verify JWT or work with them. Here comes a\noverview of the configurations you can do.\n\n#### Authenticator\n\nThe authenticator function which must be defined by the user to verify the\ngiven JSON Web Token. Here comes all your logic to lookup the related user on\nyour database, the token claim verification and/or the token cryptographic\nsigning. The function must return true or false to indicate the validity of the\ntoken.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.authenticator = proc do |token|\n    # Verify the token the way you like. (true, false)\n  end\nend\n```\n\n#### Malformed token handling\n\nWhenever the given value on the `Authorization` header is not a valid Bearer\nauthentication scheme or the token itself is not a valid JSON Web Token, this\nuser defined function will be called. You can add custom handling of this\nsituations, like responding a different HTTP status code, or a more detailed\nresponse body. By default the Rack stack will be interrupted and a response\nwith the `400 Bad Request` status code will be send to the client. The raw\ntoken (value of the `Authorization` header) and the Rack app will be injected\nto your function for maximum flexibility.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.malformed_auth_handler = proc do |raw_token, app|\n    # Do your own error handling. (Rack interface)\n  end\nend\n```\n\n#### Failed authentication handling\n\nWhen the client sends a correctly formatted JSON Web Token with the Bearer\nauthentication scheme within the `Authorization` header and your authenticator\nfails for some reason (token claims, wrong audience, bad subject, expired\ntoken, wrong cryptographic signing etc), this function is called to handle the\nbad authentication. By default the Rack stack will be interrupted and a\nresponse with the `401 Unauthorized` status code will be send to the client.\nYou can customize this the way you like and send different error codes, or\nhandle the error completely different. The parsed JSON Web Token and the Rack\napp will be injected to your function to allow any customized error handling.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.failed_auth_handler = proc do |token, app|\n    # Do your own error handling. (Rack interface)\n  end\nend\n```\n\n#### RSA public key helper\n\nWe provide a straightforward solution to deal with the provision of RSA public\nkeys.  Sometimes you want to distribute them by file to each machine and have\na local access, and sometimes you provide an endpoint on your identity\nprovider to fetch the RSA public key via HTTP/HTTPS.  The `RsaPublicKey` class\nhelps you to fulfill this task easily.\n\n**Heads up!** You can skip this if you do not care about RSA verification or\nhave your own mechanism.\n\n```ruby\n# Get your public key, by using the global configuration\npublic_key = Keyless::RsaPublicKey.fetch\n# =\u003e OpenSSL::PKey::RSA\n\n# Using a local configuration\nfetcher = Keyless::RsaPublicKey.instance\nfetcher.url = 'https://your.identity.provider/rsa_public_key'\npublic_key = fetcher.fetch\n# =\u003e OpenSSL::PKey::RSA\n```\n\nThe following examples show you how to configure the\n`Keyless::RsaPublicKey` class the global way. This is useful\nfor a shared initializer place.\n\n##### RSA public key location (URL)\n\nWhenever you want to use the `RsaPublicKey` class you configure the default URL\non the singleton instance, or use the gem configure method and set it up\naccordingly.  We allow the fetch of the public key from a remote server\n(HTTP/HTTPS) or from a local file which is accessible by the ruby process.\nSpecify the URL or the local path here. Not specified by default.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  # Local file\n  conf.rsa_public_key_url = '/tmp/jwt_rsa.pub'\n  # Remote URL\n  conf.rsa_public_key_url = 'https://your.identity.provider/rsa_public_key'\nend\n```\n\n##### RSA public key caching\n\nYou can configure the `RsaPublickey` class to enable/disable caching. For a\nremote public key location it is handy to cache the result for some time to\nkeep the traffic low to the resource server.  For a local file you can skip\nthis. Disabled by default.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.rsa_public_key_caching = true\nend\n```\n\n##### RSA public key cache expiration\n\nWhen you make use of the cache of the `RsaPublicKey` class you can fine tune\nthe expiration time. The RSA public key from your identity\nprovider should not change this frequent, so a cache for at least one hour is\nfine. You should not set it lower than one minute. Keep this setting in mind\nwhen you change keys. Your infrastructure could be inoperable for this\nconfigured time.  One hour by default.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.rsa_public_key_expiration = 1.hour\nend\n```\n\n#### JWT instance helper\n\nWe ship a little wrapper class to ease the validation of JSON Web Tokens with\nthe help of the great [ruby-jwt](https://github.com/jwt/ruby-jwt) library. This\nwrapper class provides some helpers like `#access_token?`, `#refresh_token?` or\n`#expires_at` which returns a ActiveSupport time-zoned representation of the\ntoken expiration timestamp. It is initially opinionated to RSA verification,\nbut can be tuned to verify HMAC or ECDSA signed tokens. It integrated well with\nthe `RsaPublicKey` fetcher class. (by default)\n\n**Heads up!** You can skip this if you have your own JWT verification mechanism.\n\n```ruby\n# A raw JWT (no signing, payload: {test: true})\nraw_token = 'eyJ0eXAiOiJKV1QifQ.eyJ0ZXN0Ijp0cnVlfQ.'\n\n# Parse the raw token and create a instance of it\ntoken = Keyless::Jwt.new(raw_token)\n\n# Access the payload easily (recursive-open-struct)\ntoken.payload.test\n# =\u003e true\n\n# Validate the token (we assume you configured the verification key, an/or\n# you own custom JWT verification options here)\ntoken.valid?\n# =\u003e true\n```\n\nThe following examples show you how to configure the\n`Keyless::Jwt` class the global way. This is useful for a\nshared initializer place.\n\n##### Issuer verification\n\nThe JSON Web Token issuer which should be used for verification. When `nil` we\nalso turn off the verification by default. (See the default JWT options)\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.jwt_issuer = 'your-identity-provider'\nend\n```\n\n##### Beholder (audience) verification\n\nThe resource server (namely the one which configures this right now)\nwhich MUST be present on the JSON Web Token audience claim. When `nil` we\nalso turn off the verification by default. (See the default JWT options)\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.jwt_beholder = 'your-resource-server'\nend\n```\n\n##### Custom JWT verification options\n\nYou can configure a different JSON Web Token verification option hash if your\nalgorithm differs or you want some extra/different options.  Just watch out\nthat you have to pass a proc to this configuration property. On the\n`Keyless::Jwt` class it has to be a simple hash. The default\nis here the `RS256` algorithm with enabled expiration check, and issuer+audience\ncheck when the `jwt_issuer` / `jwt_beholder` are configured accordingly.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.jwt_options = proc do\n    # See: https://github.com/jwt/ruby-jwt\n    { algorithm: 'HS256' }\n  end\nend\n```\n\n##### Custom JWT verification key\n\nYou can configure your own verification key on the `Jwt` wrapper class.  This\nway you can pass your HMAC secret or your ECDSA public key to the JSON Web\nToken validation method. Here you need to pass a proc, on the\n`Keyless::Jwt` class it has to be a scalar value. By default\nwe use the `RsaPublicKey` class to retrieve the RSA public key.\n\n```ruby\nGrape::Jwt::Authentication.configure do |conf|\n  conf.jwt_verification_key = proc do\n    # Retrieve your verification key (RSA, ECDSA, HMAC secret)\n    # the way you like, and pass it back here.\n  end\nend\n```\n\n### Per-API configuration\n\nImagine the migration of your API (say v2) and also the JSON Web Token payload\nchanges in a way you need to handle. Maybe you want to be more strict on\nversion 2 than on your old version 1. For this you can make use of the local\nconfiguration of the JWT authenticator, on your specific Grape API declaration.\nHere comes an example usage:\n\n```ruby\nmodule UserApi\n  class ApiV2 \u003c Grape::API\n    v2_auth_malformed = proc { |raw_token, app| [400, {}, ['Malformed!']] }\n    v2_auth_failed = proc { |token, app| [401, {}, ['Go away!']] }\n\n    # Enable JWT authentication on this API\n    include Grape::Jwt::Authentication\n    auth(:jwt, malformed: v2_auth_malformed,\n               failed: v2_auth_failed) do |token|\n      # Your new stricter v2 authenticator.\n      false\n    end\n  end\nend\n```\n\n### Full RSA256 example\n\nHere comes a full example of the opinionated `RSA256` algorithm usage with a\nremote RSA public key location, enabled caching and a full token payload\nverification.\n\n```ruby\n# On an initializer ..\nGrape::Jwt::Authentication.configure do |conf|\n  # The remote RSA public key location and enabled caching to limit the\n  # traffic on the remote server.\n  conf.rsa_public_key_url = 'https://your.identity.provider/rsa_public_key'\n  conf.rsa_public_key_caching = true\n  conf.rsa_public_key_expiration = 10.minutes\n\n  # Configure the JWT wrapper.\n  conf.jwt_issuer = 'The Identity Provider'\n  conf.jwt_beholder = 'example-api'\n\n  # Let Grape handle the malformed error with correct response formatting.\n  # (XML, JSON)\n  conf.malformed_auth_handler = proc do |raw_token, app|\n    raise ArgumentError, 'Authorization header is malformed.'\n  end\n\n  # The same procedure for failed verifications. (XML, JSON formatting handled\n  # external by Grape)\n  conf.failed_auth_handler = proc do |token, app|\n    raise ArgumentError, 'Access denied.'\n  end\n\n  # Custom verification logic.\n  conf.authenticator = proc do |token|\n    # Parse and instantiate a JWT verification instance\n    jwt = Keyless::Jwt.new(token)\n\n    # We just allow valid access tokens\n    jwt.access_token? \u0026\u0026 jwt.valid?\n  end\nend\n\n# On your Grape API ..\nmodule UserApi\n  class ApiV1 \u003c Grape::API\n    # Enable JWT authentication on this API\n    include Grape::Jwt::Authentication\n    auth :jwt\n  end\nend\n```\n\n## Development\n\nAfter checking out the repo, run `make install` to install dependencies. Then,\nrun `make test` to run the tests. You can also run `make shell-irb` for an\ninteractive prompt that will allow you to experiment.\n\n## Code of Conduct\n\nEveryone interacting in the project codebase, issue tracker, chat\nrooms and mailing lists is expected to follow the [code of\nconduct](./CODE_OF_CONDUCT.md).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.com/hausgold/grape-jwt-authentication. Make sure that every pull request adds\na bullet point to the [changelog](./CHANGELOG.md) file with a reference to the\nactual pull request.\n\n## Releasing\n\nThe release process of this Gem is fully automated. You just need to open the\nGithub Actions [Release\nWorkflow](https://github.com/hausgold/grape-jwt-authentication/actions/workflows/release.yml)\nand trigger a new run via the **Run workflow** button. Insert the new version\nnumber (check the [changelog](./CHANGELOG.md) first for the latest release) and\nyou're done.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhausgold%2Fgrape-jwt-authentication","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhausgold%2Fgrape-jwt-authentication","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhausgold%2Fgrape-jwt-authentication/lists"}