0.01
Low commit activity in last 3 years
A long-lived project that still receives updates
Adds JWT issuance and authentication to the Sorcery authentication library, for API-only Rails apps.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 13.0
~> 3.0
~> 8.0
~> 2.0

Runtime

>= 1.0, < 3.0
>= 0.16.5, < 0.19
 Project Readme

Sorcery::Jwt

CI Gem Version

Jwt extension for the Sorcery authentication library, for API-only Rails apps.

NOTE: Sorcery v1 is being developed and JWT is planned as a core plugin. See Sorcery/sorcery-rework#9. This gem targets the current (0.16–0.18) sorcery line.

Installation

Add this line to your application's Gemfile:

gem "sorcery-jwt"

And then execute:

$ bundle

Usage

First, include the :jwt submodule in your list of configured Sorcery submodules:

Rails.application.config.sorcery.submodules = [:jwt, ...]

Next, in the Sorcery user_config, set the secret and algorithm used to sign tokens, the token lifetime in seconds, and (optionally) clock-skew tolerance:

Rails.application.config.sorcery.configure do |config|
  # ...
  config.user_config do |user|
    user.jwt_secret     = Rails.application.secrets.secret_key_base
    user.jwt_algorithm  = "HS256"                  # default; see jwt/ruby-jwt for options
    user.session_expiry = 60 * 60 * 24 * 7 * 2      # default: 2 weeks (seconds)
    user.exp_leeway     = 30                        # optional; default nil = no leeway
  end
end

With the submodule included, each request checks the Authorization header for a bearer JWT. If the token is validly signed, unexpired, and its claims match a user, current_user is set. Handling invalid tokens (401s, etc.) is up to your application.

To log a user in and issue a token:

class SessionsController < ApplicationController
  def create
    token = login_and_issue_token(params[:email], params[:password])

    if token
      render json: { token: token }, status: :created
    else
      render json: { error: "invalid credentials" }, status: :unauthorized
    end
  end
end

Claim contract

Tokens issued by login_and_issue_token carry the user's id and email, plus the standard exp and iat claims. Authentication via login_from_jwt requires all of the following, evaluated in one place:

  1. a valid signature under the configured secret and algorithm
  2. an unexpired exp (within exp_leeway, if configured)
  3. id and email claims are present
  4. the claims match an existing user

A validly-signed token that fails any check authenticates nobody — notably, a token missing its identity claims can never fall through to an unfiltered lookup.

Token issuance API

You can also issue tokens directly from the user model:

User.issue_token(id: user.id, email: user.email)  # => signed JWT string
User.decode_token(token)                           # => [payload, header]; raises JWT::DecodeError
User.token_valid?(token)                           # => boolean; never raises

Security notes

  • No revocation is built in. Tokens are valid until exp. If you need logout, invalidation on password change, or single-session enforcement, see JWT revocation strategies — any denylist/token-version approach can be layered on top by your app.
  • The algorithm is pinned on decode: alg: none and key-confusion attacks are rejected.
  • The signing secret should be at least as strong as secret_key_base rotated from your credentials store.

Upgrading from 0.1.x

No API changes: issue_token, decode_token, token_valid?, login_and_issue_token, and the login_from_jwt login source all behave as before, and tokens issued by 0.1.x remain valid — no forced re-login. Behavior fixes in the 0.2+ line:

  • tokens with no id/email claims are rejected (previously authenticated the first user in the table)
  • the email claim must match the looked-up user
  • failed JWT login sets current_user to nil (sorcery convention)
  • the model submodule actually registers on modern Rubies (see CHANGELOG — before 0.2, sorcery's rescue NameError could silently skip it, leaving issue_token undefined at runtime)

Compatibility

Tested matrix (CI): Ruby 3.2 / 3.3 / 3.4 × sorcery 0.16.5 / 0.17 / 0.18.

Contributing

Bug reports and pull requests are welcome at https://github.com/hayfever/sorcery-jwt.

License

The gem is available as open source under the terms of the MIT License.