Skip to content

Commit d28ca8c

Browse files
committed
feature: Store and return nonces in IdToken responses
- Add migration generator for nonce table - Abort if Doorkeeper ORM isn't set to ActiveRecord - Use prepend to extend Doorkeeper classes
1 parent c73dc98 commit d28ca8c

30 files changed

+480
-87
lines changed

lib/doorkeeper/openid_connect.rb

+15-65
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
require 'doorkeeper/openid_connect/version'
1+
require 'doorkeeper'
2+
require 'json/jwt'
3+
4+
require 'doorkeeper/openid_connect/claims_builder'
5+
require 'doorkeeper/openid_connect/config'
26
require 'doorkeeper/openid_connect/engine'
7+
require 'doorkeeper/openid_connect/version'
38

49
require 'doorkeeper/openid_connect/helpers/controller'
510

@@ -8,80 +13,25 @@
813
require 'doorkeeper/openid_connect/models/claims/claim'
914
require 'doorkeeper/openid_connect/models/claims/normal_claim'
1015

11-
require 'doorkeeper/openid_connect/claims_builder'
12-
require 'doorkeeper/openid_connect/config'
16+
require 'doorkeeper/openid_connect/oauth/authorization/code'
17+
require 'doorkeeper/openid_connect/oauth/authorization_code_request'
18+
require 'doorkeeper/openid_connect/oauth/password_access_token_request'
19+
require 'doorkeeper/openid_connect/oauth/pre_authorization'
20+
require 'doorkeeper/openid_connect/oauth/token_response'
1321

14-
require 'doorkeeper/openid_connect/rails/routes'
22+
require 'doorkeeper/openid_connect/orm/active_record'
1523

16-
require 'doorkeeper'
17-
require 'json/jwt'
24+
require 'doorkeeper/openid_connect/rails/routes'
1825

1926
module Doorkeeper
27+
singleton_class.send :prepend, OpenidConnect::DoorkeeperConfiguration
28+
2029
module OpenidConnect
2130
# TODO: make this configurable
2231
SIGNING_ALGORITHM = 'RS256'
2332

24-
def self.configured?
25-
@config.present?
26-
end
27-
28-
def self.installed?
29-
configured?
30-
end
31-
3233
def self.signing_key
3334
JSON::JWK.new(OpenSSL::PKey.read(configuration.jws_private_key))
3435
end
3536
end
3637
end
37-
38-
module Doorkeeper
39-
class << self
40-
prepend ::Doorkeeper::OpenidConnect::DoorkeeperConfiguration
41-
end
42-
43-
module Helpers::Controller
44-
prepend ::Doorkeeper::OpenidConnect::Helpers::Controller
45-
end
46-
end
47-
48-
module Doorkeeper
49-
module OAuth
50-
class PasswordAccessTokenRequest
51-
private
52-
53-
def after_successful_response
54-
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token)
55-
@response.id_token = id_token
56-
end
57-
end
58-
end
59-
end
60-
61-
module Doorkeeper
62-
module OAuth
63-
class AuthorizationCodeRequest
64-
private
65-
66-
def after_successful_response
67-
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token)
68-
@response.id_token = id_token
69-
end
70-
end
71-
end
72-
end
73-
74-
module Doorkeeper
75-
module OAuth
76-
class TokenResponse
77-
attr_accessor :id_token
78-
alias_method :original_body, :body
79-
80-
def body
81-
original_body.
82-
merge({:id_token => id_token.try(:as_jws_token)}).
83-
reject { |_, value| value.blank? }
84-
end
85-
end
86-
end
87-
end

lib/doorkeeper/openid_connect/config.rb

+6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ module OpenidConnect
33
module DoorkeeperConfiguration
44
def configure(&block)
55
super(&block)
6+
7+
if configuration.orm != :active_record
8+
fail ConfigurationError, 'Doorkeeper OpenID Connect currently only supports the ActiveRecord ORM adapter'
9+
end
10+
611
configuration.optional_scopes.add :openid
712
end
813
end
914

15+
class ConfigurationError < StandardError; end
1016
class MissingConfiguration < StandardError
1117
def initialize
1218
super('Configuration for Doorkeeper OpenID Connect missing. Do you have doorkeeper_openid_connect initializer?')

lib/doorkeeper/openid_connect/helpers/controller.rb

+2
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ def prompt_values
2727
end
2828
end
2929
end
30+
31+
Helpers::Controller.send :prepend, OpenidConnect::Helpers::Controller
3032
end

lib/doorkeeper/openid_connect/models/id_token.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ module Models
44
class IdToken
55
include ActiveModel::Validations
66

7-
def initialize(access_token)
7+
attr_reader :nonce
8+
9+
def initialize(access_token, nonce = nil)
810
@access_token = access_token
11+
@nonce = nonce
912
@resource_owner = access_token.instance_eval(&Doorkeeper::OpenidConnect.configuration.resource_owner_from_access_token)
1013
@issued_at = Time.now
1114
end
@@ -16,7 +19,8 @@ def claims
1619
sub: subject,
1720
aud: audience,
1821
exp: expiration,
19-
iat: issued_at
22+
iat: issued_at,
23+
nonce: nonce
2024
}
2125
end
2226

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
module OAuth
4+
module Authorization
5+
module Code
6+
def issue_token
7+
super.tap do |access_grant|
8+
::Doorkeeper::OpenidConnect::Nonce.create!(
9+
access_grant: access_grant,
10+
nonce: pre_auth.nonce
11+
)
12+
end
13+
end
14+
end
15+
end
16+
end
17+
end
18+
19+
OAuth::Authorization::Code.send :prepend, OpenidConnect::OAuth::Authorization::Code
20+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
module OAuth
4+
module AuthorizationCodeRequest
5+
private
6+
7+
def after_successful_response
8+
super
9+
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token, grant.openid_connect_nonce.use!)
10+
@response.id_token = id_token
11+
end
12+
end
13+
end
14+
end
15+
16+
OAuth::AuthorizationCodeRequest.send :prepend, OpenidConnect::OAuth::AuthorizationCodeRequest
17+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
module OAuth
4+
module PasswordAccessTokenRequest
5+
def self.prepended(base)
6+
base.class_eval do
7+
attr_reader :nonce
8+
end
9+
end
10+
11+
def initialize(server, client, resource_owner, parameters = {})
12+
super
13+
@nonce = parameters[:nonce]
14+
end
15+
16+
private
17+
18+
def after_successful_response
19+
super
20+
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token, nonce)
21+
@response.id_token = id_token
22+
end
23+
end
24+
end
25+
end
26+
27+
OAuth::PasswordAccessTokenRequest.send :prepend, OpenidConnect::OAuth::PasswordAccessTokenRequest
28+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
module OAuth
4+
module PreAuthorization
5+
def self.prepended(base)
6+
base.class_eval do
7+
attr_reader :nonce
8+
end
9+
end
10+
11+
def initialize(server, client, attrs = {})
12+
super
13+
@nonce = attrs[:nonce]
14+
end
15+
end
16+
end
17+
end
18+
19+
OAuth::PreAuthorization.send :prepend, OpenidConnect::OAuth::PreAuthorization
20+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
module OAuth
4+
module TokenResponse
5+
def self.prepended(base)
6+
base.class_eval do
7+
attr_accessor :id_token
8+
end
9+
end
10+
11+
def body
12+
if token.includes_scope? 'openid'
13+
super.
14+
merge({:id_token => id_token.try(:as_jws_token)}).
15+
reject { |_, value| value.blank? }
16+
else
17+
super
18+
end
19+
end
20+
end
21+
end
22+
end
23+
24+
OAuth::TokenResponse.send :prepend, OpenidConnect::OAuth::TokenResponse
25+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
module Orm
4+
module ActiveRecord
5+
def initialize_models!
6+
super
7+
require 'doorkeeper/openid_connect/orm/active_record/access_grant'
8+
require 'doorkeeper/openid_connect/orm/active_record/nonce'
9+
10+
if Doorkeeper.configuration.active_record_options[:establish_connection]
11+
[Doorkeeper::OpenidConnect::Nonce].each do |c|
12+
c.send :establish_connection, Doorkeeper.configuration.active_record_options[:establish_connection]
13+
end
14+
end
15+
end
16+
end
17+
end
18+
end
19+
20+
Orm::ActiveRecord.singleton_class.send :prepend, OpenidConnect::Orm::ActiveRecord
21+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
module AccessGrant
4+
def self.prepended(base)
5+
base.class_eval do
6+
has_one :openid_connect_nonce,
7+
class_name: 'Doorkeeper::OpenidConnect::Nonce',
8+
inverse_of: :access_grant,
9+
dependent: :delete
10+
end
11+
end
12+
end
13+
end
14+
15+
AccessGrant.send :prepend, OpenidConnect::AccessGrant
16+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Doorkeeper
2+
module OpenidConnect
3+
class Nonce < ActiveRecord::Base
4+
self.table_name = "#{table_name_prefix}oauth_openid_connect_nonces#{table_name_suffix}".to_sym
5+
6+
validates :access_grant_id, :nonce, presence: true
7+
belongs_to :access_grant,
8+
class_name: 'Doorkeeper::AccessGrant',
9+
inverse_of: :openid_connect_nonce
10+
11+
def use!
12+
destroy!
13+
nonce
14+
end
15+
end
16+
end
17+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
require 'rails/generators/active_record'
2+
3+
class Doorkeeper::OpenidConnect::MigrationGenerator < ::Rails::Generators::Base
4+
include Rails::Generators::Migration
5+
source_root File.expand_path('../templates', __FILE__)
6+
desc 'Installs Doorkeeper OpenID Connect migration file.'
7+
8+
def install
9+
migration_template 'migration.rb', 'db/migrate/create_doorkeeper_openid_connect_tables.rb'
10+
end
11+
12+
def self.next_migration_number(dirname)
13+
ActiveRecord::Generators::Base.next_migration_number(dirname)
14+
end
15+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class CreateDoorkeeperOpenidConnectTables < ActiveRecord::Migration
2+
def change
3+
create_table :oauth_openid_connect_nonces do |t|
4+
t.integer :access_grant_id, null: false
5+
t.string :nonce, null: false
6+
end
7+
8+
add_foreign_key(
9+
:oauth_openid_connect_nonces,
10+
:oauth_access_grants,
11+
column: :access_grant_id
12+
)
13+
end
14+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class CreateDoorkeeperOpenidConnectTables < ActiveRecord::Migration
2+
def change
3+
create_table :oauth_openid_connect_nonces do |t|
4+
t.integer :access_grant_id, null: false
5+
t.string :nonce, null: false
6+
end
7+
8+
add_foreign_key(
9+
:oauth_openid_connect_nonces,
10+
:oauth_access_grants,
11+
column: :access_grant_id
12+
)
13+
end
14+
end

0 commit comments

Comments
 (0)