doorkeeper-jwt 0.4.3 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +5 -0
- data/.github/workflows/changelog.yml +7 -3
- data/.github/workflows/ci.yml +37 -5
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +13 -1
- data/Gemfile +7 -1
- data/README.md +97 -6
- data/doorkeeper-jwt.gemspec +7 -3
- data/lib/doorkeeper/jwt/config.rb +2 -17
- data/lib/doorkeeper/jwt/errors.rb +12 -0
- data/lib/doorkeeper/jwt/version.rb +1 -1
- data/lib/doorkeeper/jwt.rb +61 -44
- metadata +16 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51843843df19db6ed75ac9500ba810ab9e5ccde882386ced00d0746933ccd4c7
|
|
4
|
+
data.tar.gz: 982167d24d8e64f959fde2a1b89ffa11a513a02136a4147dd64ecacca2536ffb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2dad7626a80e34c5cea0cfd69c1b48d94db47a2748eccefa9d4e3f462a054865e28e518af1da1836008d7cecbaa16ebf2c122d934f3d75f7762879300fad3e40
|
|
7
|
+
data.tar.gz: d191d7577852ae158b2c54fb4cfe530e3e0f383dea8b1bde0ccdf528dc2489b9458811709ed2e09a3e818eaeaf5ec773d192e50296d88918e9e43bb2dfb0e506
|
data/.github/dependabot.yml
CHANGED
|
@@ -7,15 +7,19 @@ on:
|
|
|
7
7
|
# as defined in the (optional) "skipLabels" property.
|
|
8
8
|
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
|
|
9
9
|
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pull-requests: read
|
|
13
|
+
|
|
10
14
|
jobs:
|
|
11
15
|
# Enforces the update of a changelog file on every pull request
|
|
12
16
|
changelog:
|
|
13
17
|
runs-on: ubuntu-latest
|
|
14
18
|
steps:
|
|
15
|
-
- uses: actions/checkout@v7
|
|
19
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
16
20
|
- name: Check changed paths
|
|
17
21
|
id: changes
|
|
18
|
-
uses: dorny/paths-filter@v4
|
|
22
|
+
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
|
|
19
23
|
with:
|
|
20
24
|
filters: |
|
|
21
25
|
gem_changes:
|
|
@@ -30,7 +34,7 @@ jobs:
|
|
|
30
34
|
- name: Enforce changelog
|
|
31
35
|
if: |
|
|
32
36
|
steps.changes.outputs.gem_changes == 'true'
|
|
33
|
-
uses: dangoslen/changelog-enforcer@v3
|
|
37
|
+
uses: dangoslen/changelog-enforcer@8b5e9dc3121363bb7c0115f8533404d92af382de # v3.7.0
|
|
34
38
|
with:
|
|
35
39
|
changeLogPath: CHANGELOG.md
|
|
36
40
|
skipLabels: "dependencies"
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -2,12 +2,16 @@ name: CI
|
|
|
2
2
|
|
|
3
3
|
on: [push, pull_request]
|
|
4
4
|
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
|
|
5
8
|
jobs:
|
|
6
9
|
build:
|
|
7
10
|
name: >-
|
|
8
|
-
Ruby ${{ matrix.ruby }}
|
|
11
|
+
Ruby ${{ matrix.ruby }} / Doorkeeper ${{ matrix.doorkeeper }}
|
|
9
12
|
env:
|
|
10
13
|
CI: true
|
|
14
|
+
DOORKEEPER_VERSION: ${{ matrix.doorkeeper }}
|
|
11
15
|
runs-on: ${{ matrix.os }}
|
|
12
16
|
continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' || matrix.experimental }}
|
|
13
17
|
if: |
|
|
@@ -19,18 +23,46 @@ jobs:
|
|
|
19
23
|
experimental: [false]
|
|
20
24
|
os: [ ubuntu-latest ]
|
|
21
25
|
ruby:
|
|
22
|
-
- '3.
|
|
23
|
-
- '3.
|
|
26
|
+
- '3.2'
|
|
27
|
+
- '3.3'
|
|
28
|
+
- '3.4'
|
|
29
|
+
- '4.0'
|
|
30
|
+
# The minimum declared in the gemspec and the latest release. 5.4 is the oldest
|
|
31
|
+
# version whose options DSL resolves against our own config builder.
|
|
32
|
+
doorkeeper:
|
|
33
|
+
- '5.4.0'
|
|
34
|
+
- 'latest'
|
|
24
35
|
steps:
|
|
25
36
|
- name: Repo checkout
|
|
26
|
-
uses: actions/checkout@
|
|
37
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
38
|
|
|
28
39
|
- name: Setup Ruby
|
|
29
|
-
uses: ruby/setup-ruby@v1
|
|
40
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
30
41
|
with:
|
|
31
42
|
ruby-version: ${{ matrix.ruby }}
|
|
32
43
|
bundler-cache: true
|
|
44
|
+
# Each Doorkeeper pin resolves to a different bundle.
|
|
45
|
+
cache-version: doorkeeper-${{ matrix.doorkeeper }}
|
|
33
46
|
|
|
34
47
|
- name: Run tests
|
|
35
48
|
timeout-minutes: 10
|
|
36
49
|
run: bundle exec rake test
|
|
50
|
+
|
|
51
|
+
- name: Upload coverage to Coveralls
|
|
52
|
+
uses: coverallsapp/github-action@8d6379e14d29928660c4ba802d8e85393440b329 # v2.3.8
|
|
53
|
+
with:
|
|
54
|
+
file: coverage/.resultset.json
|
|
55
|
+
format: simplecov
|
|
56
|
+
flag-name: ruby-${{ matrix.ruby }}-doorkeeper-${{ matrix.doorkeeper }}
|
|
57
|
+
parallel: true
|
|
58
|
+
|
|
59
|
+
coverage:
|
|
60
|
+
name: Coveralls Finished
|
|
61
|
+
needs: build
|
|
62
|
+
if: always()
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
steps:
|
|
65
|
+
- name: Coveralls Finished
|
|
66
|
+
uses: coverallsapp/github-action@8d6379e14d29928660c4ba802d8e85393440b329 # v2.3.8
|
|
67
|
+
with:
|
|
68
|
+
parallel-finished: true
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,19 @@ project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
8
8
|
|
|
9
9
|
- Add your entry here
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
# [0.4.5] - 2026-09-16
|
|
12
|
+
|
|
13
|
+
- **Security**: Raise `Doorkeeper::JWT::SigningMethodMissing` instead of issuing an unsigned (`alg: none`) JWT when a signing key is configured but `signing_method` is not. Issuing unsigned tokens with no signing key at all now warns and will become an error in a future release
|
|
14
|
+
- Add `required_ruby_version >= 3.2` to the gemspec and update the CI matrix to 3.2, 3.3, 3.4 and 4.0, aligning with doorkeeper core
|
|
15
|
+
- Allow `signing_method`, `secret_key` and `secret_key_path` to be configured with a block evaluated per token [#66](https://github.com/doorkeeper-gem/doorkeeper-jwt/pull/66)
|
|
16
|
+
- Fix broken Coveralls tests coverage [#67](https://github.com/doorkeeper-gem/doorkeeper-jwt/pull/67)
|
|
17
|
+
|
|
18
|
+
# [0.4.4] - 2026-08-21
|
|
19
|
+
|
|
20
|
+
- Fix `[DOORKEEPER] Option ... already defined and will be overridden` warnings on gem load [#61](https://github.com/doorkeeper-gem/doorkeeper-jwt/pull/61)
|
|
21
|
+
- Declare `doorkeeper >= 5.4` as a runtime dependency and verify the supported range on CI [#64](https://github.com/doorkeeper-gem/doorkeeper-jwt/pull/64)
|
|
22
|
+
|
|
23
|
+
## [0.4.3] - 2026-07-20
|
|
12
24
|
|
|
13
25
|
- Reuse Doorkeeper core config options DSL [#58](https://github.com/doorkeeper-gem/doorkeeper-jwt/pull/58)
|
|
14
26
|
- Include bin directory in gemspec correctly [#59](https://github.com/doorkeeper-gem/doorkeeper-jwt/pull/59)
|
data/Gemfile
CHANGED
|
@@ -5,6 +5,12 @@ source "https://rubygems.org"
|
|
|
5
5
|
# Specify your gem's dependencies in doorkeeper-jwt.gemspec
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
|
-
gem
|
|
8
|
+
# Pin Doorkeeper to check a specific version of it against this gem. Unset means
|
|
9
|
+
# "the newest version the gemspec allows"; CI sets it to both ends of the
|
|
10
|
+
# supported range. See .github/workflows/ci.yml.
|
|
11
|
+
doorkeeper_version = ENV.fetch("DOORKEEPER_VERSION", "latest")
|
|
12
|
+
gem "doorkeeper", doorkeeper_version unless doorkeeper_version == "latest"
|
|
13
|
+
|
|
14
|
+
gem "simplecov", "~> 1.1", require: false
|
|
9
15
|
gem "rubocop", "~> 1.8", require: false
|
|
10
16
|
gem "rubocop-rspec", "~> 3.0", require: false
|
data/README.md
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
[](https://rubygems.org/gems/doorkeeper-jwt)
|
|
2
2
|
[](https://coveralls.io/github/doorkeeper-gem/doorkeeper-jwt?branch=master)
|
|
3
|
-
[](https://github.com/doorkeeper-gem/doorkeeper-jwt/actions/workflows/ci.yml)
|
|
4
|
+
[](https://qlty.sh/gh/doorkeeper-gem/projects/doorkeeper-jwt)
|
|
5
5
|
|
|
6
6
|
# Doorkeeper::JWT
|
|
7
7
|
|
|
8
|
-
Doorkeeper JWT adds JWT token support to the Doorkeeper OAuth library.
|
|
9
|
-
Untested with later versions of Doorkeeper.
|
|
8
|
+
Doorkeeper JWT adds JWT token support to the Doorkeeper OAuth library.
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
## Compatibility
|
|
11
|
+
|
|
12
|
+
Requires Doorkeeper 5.4 or newer. Doorkeeper 5.0 and earlier cannot load this gem at all, because they ship no
|
|
13
|
+
`doorkeeper/config/option`. Doorkeeper 5.1 - 5.3 load it but misconfigure it: their options DSL defines the
|
|
14
|
+
methods on Doorkeeper's own config builder instead of this gem's.
|
|
15
|
+
|
|
16
|
+
The minimum supported version and the latest release run on every CI build - see the `doorkeeper` axis of the matrix
|
|
17
|
+
in [`.github/workflows/ci.yml`](.github/workflows/ci.yml). To check a version that is not covered there, point the
|
|
18
|
+
test suite at it:
|
|
19
|
+
|
|
20
|
+
```console
|
|
21
|
+
$ DOORKEEPER_VERSION=5.6.0 bundle install
|
|
22
|
+
$ DOORKEEPER_VERSION=5.6.0 bundle exec rake test
|
|
13
23
|
```
|
|
14
24
|
|
|
15
25
|
## Installation
|
|
@@ -93,6 +103,87 @@ Doorkeeper::JWT.configure do
|
|
|
93
103
|
end
|
|
94
104
|
```
|
|
95
105
|
|
|
106
|
+
### Authenticating more than one model
|
|
107
|
+
|
|
108
|
+
`opts[:resource_owner_id]` alone does not tell the payload block which model the token belongs to, so an application
|
|
109
|
+
with several resource owner models (say, `Driver` and `Client`) cannot look the owner up from the id.
|
|
110
|
+
|
|
111
|
+
Doorkeeper 5.4 and newer can hand the owner record itself to the generator. Run the generator, which enables
|
|
112
|
+
`use_polymorphic_resource_owner` in the `Doorkeeper.configure` block and creates the migration that adds the
|
|
113
|
+
`resource_owner_type` columns, then apply the migration:
|
|
114
|
+
|
|
115
|
+
$ rails generate doorkeeper:enable_polymorphic_resource_owner
|
|
116
|
+
$ rails db:migrate
|
|
117
|
+
|
|
118
|
+
The payload block then receives the record as `opts[:resource_owner]`, so the payload can branch on its class:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
Doorkeeper::JWT.configure do
|
|
122
|
+
token_payload do |opts|
|
|
123
|
+
owner = opts[:resource_owner]
|
|
124
|
+
|
|
125
|
+
# client credentials tokens have no resource owner
|
|
126
|
+
next { sub: opts[:application]&.uid } unless owner
|
|
127
|
+
|
|
128
|
+
# ids are only unique per model, so include the model name in `sub`
|
|
129
|
+
{
|
|
130
|
+
sub: "#{owner.class.name.underscore}:#{owner.id}",
|
|
131
|
+
owner_type: owner.class.name,
|
|
132
|
+
email: owner.email
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Tokens issued by the client credentials flow have no resource owner, so `opts[:resource_owner]` is `nil` there.
|
|
139
|
+
|
|
140
|
+
### Using more than one signing method
|
|
141
|
+
|
|
142
|
+
`signing_method`, `secret_key` and `secret_key_path` also accept a block. The block receives the same
|
|
143
|
+
options hash as `token_payload` (`resource_owner_id`, `application`, `scopes`, ...) and is evaluated
|
|
144
|
+
whenever the option is needed for the token being generated, so you can pick the algorithm and the key
|
|
145
|
+
per request — for example an RSA-signed token for clients with an `admin` scope and an HMAC-signed token
|
|
146
|
+
for everyone else:
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
Doorkeeper::JWT.configure do
|
|
150
|
+
signing_method do |opts|
|
|
151
|
+
opts[:scopes].exists?('admin') ? :rs512 : :hs256
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
secret_key do |opts|
|
|
155
|
+
opts[:scopes].exists?('admin') ? ENV['JWT_RSA_PRIVATE_KEY'] : ENV['JWT_HMAC_SECRET']
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Doorkeeper hands `opts[:scopes]` over as a `Doorkeeper::OAuth::Scopes`, so match a single scope with
|
|
161
|
+
`exists?` rather than with a substring check: `opts[:scopes].to_s.include?('admin')` would also be true for
|
|
162
|
+
an unrelated `superadmin` or `admin_readonly` scope, and picking a signing key that way is easy to get wrong.
|
|
163
|
+
|
|
164
|
+
The blocks are independent of each other, so make sure `secret_key` (or `secret_key_path`) returns a key that
|
|
165
|
+
matches the algorithm returned by `signing_method` for the same options.
|
|
166
|
+
|
|
167
|
+
Note that the key options are only resolved when they are actually used: `secret_key_path` is read for
|
|
168
|
+
`RS*` and `ES*` algorithms only, and both `secret_key` and `secret_key_path` are skipped entirely when
|
|
169
|
+
`use_application_secret` is enabled. When a path is configured for an asymmetric algorithm it takes
|
|
170
|
+
precedence, and the `secret_key` block is not evaluated at all.
|
|
171
|
+
|
|
172
|
+
Whoever verifies these tokens now has more than one key to choose from, so tell them which one was used:
|
|
173
|
+
`token_headers` receives the same options hash and is evaluated per token as well, which lets you emit a
|
|
174
|
+
matching `kid` header.
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
Doorkeeper::JWT.configure do
|
|
178
|
+
token_headers do |opts|
|
|
179
|
+
{ kid: opts[:scopes].exists?('admin') ? 'rsa-2026' : 'hmac-2026' }
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
See the `token_headers` entry in the configuration above for deriving the `kid` of an `RS*` key with
|
|
185
|
+
`JWT::JWK` instead of labelling it by hand.
|
|
186
|
+
|
|
96
187
|
## Development
|
|
97
188
|
|
|
98
189
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt
|
data/doorkeeper-jwt.gemspec
CHANGED
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
|
|
14
14
|
spec.summary = "JWT token generator for Doorkeeper"
|
|
15
15
|
spec.description = "JWT token generator extension for Doorkeeper"
|
|
16
|
-
spec.homepage = "https://github.com/
|
|
16
|
+
spec.homepage = "https://github.com/doorkeeper-gem/doorkeeper-jwt"
|
|
17
17
|
spec.license = "MIT"
|
|
18
18
|
|
|
19
19
|
spec.bindir = "exe"
|
|
@@ -24,16 +24,20 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
|
|
25
25
|
spec.metadata = {
|
|
26
26
|
"homepage_uri" => "https://github.com/doorkeeper-gem/doorkeeper-jwt",
|
|
27
|
-
"changelog_uri" => "https://github.com/doorkeeper-gem/doorkeeper-jwt/blob/
|
|
27
|
+
"changelog_uri" => "https://github.com/doorkeeper-gem/doorkeeper-jwt/blob/master/CHANGELOG.md",
|
|
28
28
|
"source_code_uri" => "https://github.com/doorkeeper-gem/doorkeeper-jwt",
|
|
29
29
|
"bug_tracker_uri" => "https://github.com/doorkeeper-gem/doorkeeper-jwt/issues",
|
|
30
30
|
"funding_uri" => "https://opencollective.com/doorkeeper-gem",
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
spec.required_ruby_version = ">= 3.2"
|
|
34
|
+
|
|
35
|
+
# Loaded at require time by lib/doorkeeper/jwt/config.rb. The options DSL it
|
|
36
|
+
# relies on only resolves against our own builder since Doorkeeper 5.4.
|
|
37
|
+
spec.add_dependency "doorkeeper", ">= 5.4"
|
|
33
38
|
spec.add_dependency "jwt", ">= 2.1"
|
|
34
39
|
|
|
35
40
|
spec.add_development_dependency "bundler", ">= 1.16"
|
|
36
|
-
spec.add_development_dependency "doorkeeper"
|
|
37
41
|
spec.add_development_dependency "pry", "~> 0"
|
|
38
42
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
39
43
|
spec.add_development_dependency "rspec", "~> 3.8"
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "doorkeeper/config/option"
|
|
4
|
+
require "doorkeeper/jwt/errors"
|
|
4
5
|
|
|
5
6
|
module Doorkeeper
|
|
6
7
|
module JWT
|
|
7
|
-
class MissingConfiguration <
|
|
8
|
+
class MissingConfiguration < Error
|
|
8
9
|
def initialize
|
|
9
10
|
super("Configuration for doorkeeper-jwt missing.")
|
|
10
11
|
end
|
|
@@ -29,27 +30,11 @@ module Doorkeeper
|
|
|
29
30
|
@config
|
|
30
31
|
end
|
|
31
32
|
|
|
32
|
-
def use_application_secret(value)
|
|
33
|
-
@config.instance_variable_set("@use_application_secret", value)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def secret_key(value)
|
|
37
|
-
@config.instance_variable_set("@secret_key", value)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def secret_key_path(value)
|
|
41
|
-
@config.instance_variable_set("@secret_key_path", value)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
33
|
# For backward compatibility. This library does not support encryption.
|
|
45
34
|
def encryption_method(value)
|
|
46
35
|
@config.instance_variable_set("@signing_method", value)
|
|
47
36
|
Kernel.warn("[DOORKEEPER-JWT]: Please use signing_method instead, this option is deprecated and will be removed soon")
|
|
48
37
|
end
|
|
49
|
-
|
|
50
|
-
def signing_method(value)
|
|
51
|
-
@config.instance_variable_set("@signing_method", value)
|
|
52
|
-
end
|
|
53
38
|
end
|
|
54
39
|
|
|
55
40
|
mattr_reader(:builder_class) { Config::Builder }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module JWT
|
|
5
|
+
# Base class for every error raised by doorkeeper-jwt.
|
|
6
|
+
class Error < StandardError; end
|
|
7
|
+
|
|
8
|
+
# Raised when a signing key is configured but `signing_method` is not, so
|
|
9
|
+
# the only token that could be issued would be an unsigned (alg: none) one.
|
|
10
|
+
class SigningMethodMissing < Error; end
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/doorkeeper/jwt.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "doorkeeper/jwt/version"
|
|
4
|
+
require "doorkeeper/jwt/errors"
|
|
4
5
|
require "doorkeeper/jwt/config"
|
|
5
6
|
require "jwt"
|
|
6
7
|
|
|
@@ -8,14 +9,22 @@ module Doorkeeper
|
|
|
8
9
|
module JWT
|
|
9
10
|
class << self
|
|
10
11
|
def generate(opts = {})
|
|
12
|
+
algorithm = signing_method(opts)
|
|
13
|
+
|
|
11
14
|
::JWT.encode(
|
|
12
15
|
token_payload(opts),
|
|
13
|
-
secret_key(opts),
|
|
14
|
-
|
|
16
|
+
secret_key(opts, algorithm),
|
|
17
|
+
algorithm,
|
|
15
18
|
token_headers(opts)
|
|
16
19
|
)
|
|
17
20
|
end
|
|
18
21
|
|
|
22
|
+
def signing_key_configured?
|
|
23
|
+
use_application_secret? ||
|
|
24
|
+
!Doorkeeper::JWT.configuration.secret_key.nil? ||
|
|
25
|
+
!Doorkeeper::JWT.configuration.secret_key_path.nil?
|
|
26
|
+
end
|
|
27
|
+
|
|
19
28
|
private
|
|
20
29
|
|
|
21
30
|
def token_payload(opts = {})
|
|
@@ -26,27 +35,61 @@ module Doorkeeper
|
|
|
26
35
|
Doorkeeper::JWT.configuration.token_headers.call(opts)
|
|
27
36
|
end
|
|
28
37
|
|
|
29
|
-
def secret_key(opts)
|
|
30
|
-
opts = { application: {} }.merge(opts)
|
|
31
|
-
|
|
38
|
+
def secret_key(opts, algorithm)
|
|
32
39
|
return application_secret(opts) if use_application_secret?
|
|
33
|
-
return secret_key_file unless secret_key_file.nil?
|
|
34
|
-
return rsa_key if rsa_signing?
|
|
35
|
-
return ecdsa_key if ecdsa_signing?
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
case algorithm
|
|
42
|
+
when /RS\d{3}/ then OpenSSL::PKey::RSA.new(asymmetric_key(opts))
|
|
43
|
+
when /ES\d{3}/ then OpenSSL::PKey::EC.new(asymmetric_key(opts))
|
|
44
|
+
else configured_secret_key(opts)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# A configured key file wins over `secret_key`, which is only resolved
|
|
49
|
+
# when no path is configured for this token.
|
|
50
|
+
def asymmetric_key(opts)
|
|
51
|
+
secret_key_file(opts) || configured_secret_key(opts)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def configured_secret_key(opts)
|
|
55
|
+
resolve_option(Doorkeeper::JWT.configuration.secret_key, opts)
|
|
38
56
|
end
|
|
39
57
|
|
|
40
|
-
def secret_key_file
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return ecdsa_key_file if ecdsa_signing?
|
|
58
|
+
def secret_key_file(opts)
|
|
59
|
+
path = resolve_option(Doorkeeper::JWT.configuration.secret_key_path, opts)
|
|
60
|
+
File.read(path) if path
|
|
44
61
|
end
|
|
45
62
|
|
|
46
|
-
def signing_method
|
|
47
|
-
|
|
63
|
+
def signing_method(opts)
|
|
64
|
+
method = resolve_option(Doorkeeper::JWT.configuration.signing_method, opts)
|
|
65
|
+
return method.to_s.upcase unless method.nil?
|
|
66
|
+
|
|
67
|
+
raise_missing_signing_method if signing_key_configured?
|
|
48
68
|
|
|
49
|
-
|
|
69
|
+
warn_unsigned_token
|
|
70
|
+
"none"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def raise_missing_signing_method
|
|
74
|
+
raise(
|
|
75
|
+
SigningMethodMissing,
|
|
76
|
+
"JWT `signing_method` is not configured, but a signing key is." \
|
|
77
|
+
" Refusing to issue an unsigned (alg: none) token. Set" \
|
|
78
|
+
" `signing_method` explicitly, e.g. `signing_method :hs512`."
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def warn_unsigned_token
|
|
83
|
+
Kernel.warn(
|
|
84
|
+
"[DOORKEEPER-JWT]: No `signing_method` configured; issuing UNSIGNED" \
|
|
85
|
+
" tokens (alg: none). This will become an error in a future release."
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Options can be configured either with a static value or with a block
|
|
90
|
+
# that receives the token generation options and returns the value.
|
|
91
|
+
def resolve_option(value, opts)
|
|
92
|
+
value.respond_to?(:call) ? value.call(opts) : value
|
|
50
93
|
end
|
|
51
94
|
|
|
52
95
|
def use_application_secret?
|
|
@@ -54,6 +97,8 @@ module Doorkeeper
|
|
|
54
97
|
end
|
|
55
98
|
|
|
56
99
|
def application_secret(opts)
|
|
100
|
+
opts = { application: {} }.merge(opts)
|
|
101
|
+
|
|
57
102
|
if opts[:application].nil?
|
|
58
103
|
raise(
|
|
59
104
|
"JWT `use_application_secret` is enabled, but application is nil." \
|
|
@@ -82,34 +127,6 @@ module Doorkeeper
|
|
|
82
127
|
|
|
83
128
|
secret
|
|
84
129
|
end
|
|
85
|
-
|
|
86
|
-
def rsa_signing?
|
|
87
|
-
/RS\d{3}/ =~ signing_method
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def ecdsa_signing?
|
|
91
|
-
/ES\d{3}/ =~ signing_method
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def rsa_key
|
|
95
|
-
OpenSSL::PKey::RSA.new(Doorkeeper::JWT.configuration.secret_key)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def ecdsa_key
|
|
99
|
-
OpenSSL::PKey::EC.new(Doorkeeper::JWT.configuration.secret_key)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def rsa_key_file
|
|
103
|
-
secret_key_file_open { |f| OpenSSL::PKey::RSA.new(f) }
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def ecdsa_key_file
|
|
107
|
-
secret_key_file_open { |f| OpenSSL::PKey::EC.new(f) }
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def secret_key_file_open(&block)
|
|
111
|
-
File.open(Doorkeeper::JWT.configuration.secret_key_path, &block)
|
|
112
|
-
end
|
|
113
130
|
end
|
|
114
131
|
end
|
|
115
132
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: doorkeeper-jwt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Warren
|
|
@@ -9,50 +9,50 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-09-16 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
|
-
name:
|
|
15
|
+
name: doorkeeper
|
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
|
17
17
|
requirements:
|
|
18
18
|
- - ">="
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
|
-
version: '
|
|
20
|
+
version: '5.4'
|
|
21
21
|
type: :runtime
|
|
22
22
|
prerelease: false
|
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
|
24
24
|
requirements:
|
|
25
25
|
- - ">="
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
|
-
version: '
|
|
27
|
+
version: '5.4'
|
|
28
28
|
- !ruby/object:Gem::Dependency
|
|
29
|
-
name:
|
|
29
|
+
name: jwt
|
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
|
31
31
|
requirements:
|
|
32
32
|
- - ">="
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '1
|
|
35
|
-
type: :
|
|
34
|
+
version: '2.1'
|
|
35
|
+
type: :runtime
|
|
36
36
|
prerelease: false
|
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
38
|
requirements:
|
|
39
39
|
- - ">="
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: '1
|
|
41
|
+
version: '2.1'
|
|
42
42
|
- !ruby/object:Gem::Dependency
|
|
43
|
-
name:
|
|
43
|
+
name: bundler
|
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
|
45
45
|
requirements:
|
|
46
46
|
- - ">="
|
|
47
47
|
- !ruby/object:Gem::Version
|
|
48
|
-
version: '
|
|
48
|
+
version: '1.16'
|
|
49
49
|
type: :development
|
|
50
50
|
prerelease: false
|
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
|
52
52
|
requirements:
|
|
53
53
|
- - ">="
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
|
-
version: '
|
|
55
|
+
version: '1.16'
|
|
56
56
|
- !ruby/object:Gem::Dependency
|
|
57
57
|
name: pry
|
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -117,13 +117,14 @@ files:
|
|
|
117
117
|
- doorkeeper-jwt.gemspec
|
|
118
118
|
- lib/doorkeeper/jwt.rb
|
|
119
119
|
- lib/doorkeeper/jwt/config.rb
|
|
120
|
+
- lib/doorkeeper/jwt/errors.rb
|
|
120
121
|
- lib/doorkeeper/jwt/version.rb
|
|
121
|
-
homepage: https://github.com/
|
|
122
|
+
homepage: https://github.com/doorkeeper-gem/doorkeeper-jwt
|
|
122
123
|
licenses:
|
|
123
124
|
- MIT
|
|
124
125
|
metadata:
|
|
125
126
|
homepage_uri: https://github.com/doorkeeper-gem/doorkeeper-jwt
|
|
126
|
-
changelog_uri: https://github.com/doorkeeper-gem/doorkeeper-jwt/blob/
|
|
127
|
+
changelog_uri: https://github.com/doorkeeper-gem/doorkeeper-jwt/blob/master/CHANGELOG.md
|
|
127
128
|
source_code_uri: https://github.com/doorkeeper-gem/doorkeeper-jwt
|
|
128
129
|
bug_tracker_uri: https://github.com/doorkeeper-gem/doorkeeper-jwt/issues
|
|
129
130
|
funding_uri: https://opencollective.com/doorkeeper-gem
|
|
@@ -135,7 +136,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
135
136
|
requirements:
|
|
136
137
|
- - ">="
|
|
137
138
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '
|
|
139
|
+
version: '3.2'
|
|
139
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
141
|
requirements:
|
|
141
142
|
- - ">="
|