Skip to content

Commit

Permalink
Merge pull request #18 from doorkeeper-gem/feat/nonce-tracking
Browse files Browse the repository at this point in the history
Support nonces
  • Loading branch information
toupeira authored Nov 3, 2016
2 parents c73dc98 + 80399fd commit ff423a2
Show file tree
Hide file tree
Showing 32 changed files with 532 additions and 87 deletions.
80 changes: 15 additions & 65 deletions lib/doorkeeper/openid_connect.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
require 'doorkeeper/openid_connect/version'
require 'doorkeeper'
require 'json/jwt'

require 'doorkeeper/openid_connect/claims_builder'
require 'doorkeeper/openid_connect/config'
require 'doorkeeper/openid_connect/engine'
require 'doorkeeper/openid_connect/version'

require 'doorkeeper/openid_connect/helpers/controller'

Expand All @@ -8,80 +13,25 @@
require 'doorkeeper/openid_connect/models/claims/claim'
require 'doorkeeper/openid_connect/models/claims/normal_claim'

require 'doorkeeper/openid_connect/claims_builder'
require 'doorkeeper/openid_connect/config'
require 'doorkeeper/openid_connect/oauth/authorization/code'
require 'doorkeeper/openid_connect/oauth/authorization_code_request'
require 'doorkeeper/openid_connect/oauth/password_access_token_request'
require 'doorkeeper/openid_connect/oauth/pre_authorization'
require 'doorkeeper/openid_connect/oauth/token_response'

require 'doorkeeper/openid_connect/rails/routes'
require 'doorkeeper/openid_connect/orm/active_record'

require 'doorkeeper'
require 'json/jwt'
require 'doorkeeper/openid_connect/rails/routes'

module Doorkeeper
singleton_class.send :prepend, OpenidConnect::DoorkeeperConfiguration

module OpenidConnect
# TODO: make this configurable
SIGNING_ALGORITHM = 'RS256'

def self.configured?
@config.present?
end

def self.installed?
configured?
end

def self.signing_key
JSON::JWK.new(OpenSSL::PKey.read(configuration.jws_private_key))
end
end
end

module Doorkeeper
class << self
prepend ::Doorkeeper::OpenidConnect::DoorkeeperConfiguration
end

module Helpers::Controller
prepend ::Doorkeeper::OpenidConnect::Helpers::Controller
end
end

module Doorkeeper
module OAuth
class PasswordAccessTokenRequest
private

def after_successful_response
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token)
@response.id_token = id_token
end
end
end
end

module Doorkeeper
module OAuth
class AuthorizationCodeRequest
private

def after_successful_response
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token)
@response.id_token = id_token
end
end
end
end

module Doorkeeper
module OAuth
class TokenResponse
attr_accessor :id_token
alias_method :original_body, :body

def body
original_body.
merge({:id_token => id_token.try(:as_jws_token)}).
reject { |_, value| value.blank? }
end
end
end
end
6 changes: 6 additions & 0 deletions lib/doorkeeper/openid_connect/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ module OpenidConnect
module DoorkeeperConfiguration
def configure(&block)
super(&block)

if configuration.orm != :active_record
fail ConfigurationError, 'Doorkeeper OpenID Connect currently only supports the ActiveRecord ORM adapter'
end

configuration.optional_scopes.add :openid
end
end

class ConfigurationError < StandardError; end
class MissingConfiguration < StandardError
def initialize
super('Configuration for Doorkeeper OpenID Connect missing. Do you have doorkeeper_openid_connect initializer?')
Expand Down
2 changes: 2 additions & 0 deletions lib/doorkeeper/openid_connect/helpers/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ def prompt_values
end
end
end

Helpers::Controller.send :prepend, OpenidConnect::Helpers::Controller
end
8 changes: 6 additions & 2 deletions lib/doorkeeper/openid_connect/models/id_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ module Models
class IdToken
include ActiveModel::Validations

def initialize(access_token)
attr_reader :nonce

def initialize(access_token, nonce = nil)
@access_token = access_token
@nonce = nonce
@resource_owner = access_token.instance_eval(&Doorkeeper::OpenidConnect.configuration.resource_owner_from_access_token)
@issued_at = Time.now
end
Expand All @@ -16,7 +19,8 @@ def claims
sub: subject,
aud: audience,
exp: expiration,
iat: issued_at
iat: issued_at,
nonce: nonce
}
end

Expand Down
20 changes: 20 additions & 0 deletions lib/doorkeeper/openid_connect/oauth/authorization/code.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Doorkeeper
module OpenidConnect
module OAuth
module Authorization
module Code
def issue_token
super.tap do |access_grant|
::Doorkeeper::OpenidConnect::Nonce.create!(
access_grant: access_grant,
nonce: pre_auth.nonce
)
end
end
end
end
end
end

OAuth::Authorization::Code.send :prepend, OpenidConnect::OAuth::Authorization::Code
end
17 changes: 17 additions & 0 deletions lib/doorkeeper/openid_connect/oauth/authorization_code_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Doorkeeper
module OpenidConnect
module OAuth
module AuthorizationCodeRequest
private

def after_successful_response
super
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token, grant.openid_connect_nonce.use!)
@response.id_token = id_token
end
end
end
end

OAuth::AuthorizationCodeRequest.send :prepend, OpenidConnect::OAuth::AuthorizationCodeRequest
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module Doorkeeper
module OpenidConnect
module OAuth
module PasswordAccessTokenRequest
def self.prepended(base)
base.class_eval do
attr_reader :nonce
end
end

def initialize(server, client, resource_owner, parameters = {})
super
@nonce = parameters[:nonce]
end

private

def after_successful_response
super
id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token, nonce)
@response.id_token = id_token
end
end
end
end

OAuth::PasswordAccessTokenRequest.send :prepend, OpenidConnect::OAuth::PasswordAccessTokenRequest
end
20 changes: 20 additions & 0 deletions lib/doorkeeper/openid_connect/oauth/pre_authorization.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Doorkeeper
module OpenidConnect
module OAuth
module PreAuthorization
def self.prepended(base)
base.class_eval do
attr_reader :nonce
end
end

def initialize(server, client, attrs = {})
super
@nonce = attrs[:nonce]
end
end
end
end

OAuth::PreAuthorization.send :prepend, OpenidConnect::OAuth::PreAuthorization
end
25 changes: 25 additions & 0 deletions lib/doorkeeper/openid_connect/oauth/token_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Doorkeeper
module OpenidConnect
module OAuth
module TokenResponse
def self.prepended(base)
base.class_eval do
attr_accessor :id_token
end
end

def body
if token.includes_scope? 'openid'
super.
merge({:id_token => id_token.try(:as_jws_token)}).
reject { |_, value| value.blank? }
else
super
end
end
end
end
end

OAuth::TokenResponse.send :prepend, OpenidConnect::OAuth::TokenResponse
end
21 changes: 21 additions & 0 deletions lib/doorkeeper/openid_connect/orm/active_record.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Doorkeeper
module OpenidConnect
module Orm
module ActiveRecord
def initialize_models!
super
require 'doorkeeper/openid_connect/orm/active_record/access_grant'
require 'doorkeeper/openid_connect/orm/active_record/nonce'

if Doorkeeper.configuration.active_record_options[:establish_connection]
[Doorkeeper::OpenidConnect::Nonce].each do |c|
c.send :establish_connection, Doorkeeper.configuration.active_record_options[:establish_connection]
end
end
end
end
end
end

Orm::ActiveRecord.singleton_class.send :prepend, OpenidConnect::Orm::ActiveRecord
end
16 changes: 16 additions & 0 deletions lib/doorkeeper/openid_connect/orm/active_record/access_grant.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Doorkeeper
module OpenidConnect
module AccessGrant
def self.prepended(base)
base.class_eval do
has_one :openid_connect_nonce,
class_name: 'Doorkeeper::OpenidConnect::Nonce',
inverse_of: :access_grant,
dependent: :delete
end
end
end
end

AccessGrant.send :prepend, OpenidConnect::AccessGrant
end
17 changes: 17 additions & 0 deletions lib/doorkeeper/openid_connect/orm/active_record/nonce.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Doorkeeper
module OpenidConnect
class Nonce < ActiveRecord::Base
self.table_name = "#{table_name_prefix}oauth_openid_connect_nonces#{table_name_suffix}".to_sym

validates :access_grant_id, :nonce, presence: true
belongs_to :access_grant,
class_name: 'Doorkeeper::AccessGrant',
inverse_of: :openid_connect_nonce

def use!
destroy!
nonce
end
end
end
end
11 changes: 11 additions & 0 deletions lib/generators/doorkeeper/openid_connect/install_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Doorkeeper::OpenidConnect::InstallGenerator < ::Rails::Generators::Base
include Rails::Generators::Migration
source_root File.expand_path('../templates', __FILE__)
desc 'Installs Doorkeeper OpenID Connect.'

def install
template 'initializer.rb', 'config/initializers/doorkeeper_openid_connect.rb'
copy_file File.expand_path('../../../../../config/locales/en.yml', __FILE__), 'config/locales/doorkeeper_openid_connect.en.yml'
route 'use_doorkeeper_openid_connect'
end
end
15 changes: 15 additions & 0 deletions lib/generators/doorkeeper/openid_connect/migration_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'rails/generators/active_record'

class Doorkeeper::OpenidConnect::MigrationGenerator < ::Rails::Generators::Base
include Rails::Generators::Migration
source_root File.expand_path('../templates', __FILE__)
desc 'Installs Doorkeeper OpenID Connect migration file.'

def install
migration_template 'migration.rb', 'db/migrate/create_doorkeeper_openid_connect_tables.rb'
end

def self.next_migration_number(dirname)
ActiveRecord::Generators::Base.next_migration_number(dirname)
end
end
41 changes: 41 additions & 0 deletions lib/generators/doorkeeper/openid_connect/templates/initializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Doorkeeper::OpenidConnect.configure do

issuer 'issuer string'

jws_private_key <<-EOL
-----BEGIN RSA PRIVATE KEY-----
....
-----END RSA PRIVATE KEY-----
EOL

jws_public_key <<-EOL
-----BEGIN RSA PUBLIC KEY-----
....
-----END RSA PUBLIC KEY-----
EOL

resource_owner_from_access_token do |access_token|
# Example implementation:
# User.find_by(id: access_token.resource_owner_id)
end

subject do |resource_owner|
# Example implementation:
# resource_owner.key
end

# Expiration time on or after which the ID Token MUST NOT be accepted for processing. (default 120 seconds).
# expiration 600

# Example claims:
# claims do
# normal_claim :_foo_ do |resource_owner|
# resource_owner.foo
# end

# normal_claim :_bar_ do |resource_owner|
# resource_owner.bar
# end
# end
end

Loading

0 comments on commit ff423a2

Please sign in to comment.