doorkeeper-openid_connect 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +90 -0
  7. data/Rakefile +7 -0
  8. data/app/controllers/doorkeeper/openid_connect/userinfo_controller.rb +19 -0
  9. data/config/locales/en.yml +20 -0
  10. data/doorkeeper-openid_connect.gemspec +27 -0
  11. data/lib/doorkeeper/openid_connect.rb +65 -0
  12. data/lib/doorkeeper/openid_connect/claims_builder.rb +24 -0
  13. data/lib/doorkeeper/openid_connect/config.rb +125 -0
  14. data/lib/doorkeeper/openid_connect/engine.rb +9 -0
  15. data/lib/doorkeeper/openid_connect/models/claims/aggregated_claim.rb +11 -0
  16. data/lib/doorkeeper/openid_connect/models/claims/claim.rb +15 -0
  17. data/lib/doorkeeper/openid_connect/models/claims/distributed_claim.rb +11 -0
  18. data/lib/doorkeeper/openid_connect/models/claims/normal_claim.rb +28 -0
  19. data/lib/doorkeeper/openid_connect/models/id_token.rb +63 -0
  20. data/lib/doorkeeper/openid_connect/models/user_info.rb +39 -0
  21. data/lib/doorkeeper/openid_connect/rails/routes.rb +50 -0
  22. data/lib/doorkeeper/openid_connect/rails/routes/mapper.rb +30 -0
  23. data/lib/doorkeeper/openid_connect/rails/routes/mapping.rb +34 -0
  24. data/lib/doorkeeper/openid_connect/version.rb +5 -0
  25. data/spec/dummy/Rakefile +7 -0
  26. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  27. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +7 -0
  28. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +12 -0
  29. data/spec/dummy/app/controllers/home_controller.rb +17 -0
  30. data/spec/dummy/app/controllers/metal_controller.rb +11 -0
  31. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +11 -0
  32. data/spec/dummy/app/helpers/application_helper.rb +5 -0
  33. data/spec/dummy/app/models/user.rb +31 -0
  34. data/spec/dummy/app/views/home/index.html.erb +0 -0
  35. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  36. data/spec/dummy/config.ru +4 -0
  37. data/spec/dummy/config/application.rb +55 -0
  38. data/spec/dummy/config/boot.rb +6 -0
  39. data/spec/dummy/config/database.yml +15 -0
  40. data/spec/dummy/config/environment.rb +5 -0
  41. data/spec/dummy/config/environments/development.rb +29 -0
  42. data/spec/dummy/config/environments/production.rb +62 -0
  43. data/spec/dummy/config/environments/test.rb +56 -0
  44. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  45. data/spec/dummy/config/initializers/doorkeeper.rb +59 -0
  46. data/spec/dummy/config/initializers/secret_token.rb +9 -0
  47. data/spec/dummy/config/initializers/session_store.rb +8 -0
  48. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  49. data/spec/dummy/config/locales/doorkeeper.en.yml +5 -0
  50. data/spec/dummy/config/mongo.yml +11 -0
  51. data/spec/dummy/config/mongoid2.yml +9 -0
  52. data/spec/dummy/config/mongoid3.yml +18 -0
  53. data/spec/dummy/config/mongoid4.yml +19 -0
  54. data/spec/dummy/config/routes.rb +52 -0
  55. data/spec/dummy/db/migrate/20111122132257_create_users.rb +9 -0
  56. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +5 -0
  57. data/spec/dummy/db/migrate/20130902165751_create_doorkeeper_tables.rb +41 -0
  58. data/spec/dummy/db/migrate/20130902175349_add_owner_to_application.rb +7 -0
  59. data/spec/dummy/db/schema.rb +65 -0
  60. data/spec/dummy/log/test.log +16605 -0
  61. data/spec/dummy/public/404.html +26 -0
  62. data/spec/dummy/public/422.html +26 -0
  63. data/spec/dummy/public/500.html +26 -0
  64. data/spec/dummy/public/favicon.ico +0 -0
  65. data/spec/dummy/script/rails +6 -0
  66. data/spec/lib/doorkeeper/openid_connect/config_spec.rb +65 -0
  67. data/spec/spec_helper.rb +2 -0
  68. data/spec/spec_helper_integration.rb +48 -0
  69. metadata +239 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4a7c3aedaa20bdd3901db1b929c3c3bb99657741
4
+ data.tar.gz: 87ac0f39c66b33ddbbf37507f3aed1d1982077b7
5
+ SHA512:
6
+ metadata.gz: 0a9d2fc390e85c552b1cfe7bd316864b5d86973044233f276e46dd442ce4fc566ff55e7b5132c671b04c045c761ffcedbe35419c5f3d70b1e30852300c1ab13b
7
+ data.tar.gz: ae08481f8d98779741c9cd7daaceaea8825b178c8591e3d33423f9cb6bcfc399fa0331e899e776c4481a71bf8b384e7af7dbb77f980140d0d4c199aa342f585b
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ ENV['rails'] ||= ENV['orm'] == "mongoid4" ? '4.0.2' : '3.2.13'
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Define Rails version
6
+ gem 'rails', ENV['rails']
7
+
8
+ gem 'doorkeeper'
9
+
10
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sam Dengler
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Doorkeeper::OpenidConnect
2
+
3
+ This library is a plugin to the Doorkeeper OAuth Ruby framework that implements the OpenID Connect specification incompletely (http://openid.net/specs/openid-connect-core-1_0.html).
4
+
5
+ ## Version 1.x
6
+
7
+ This library is still pretty raw, but the latest changes are not backwards compatible with the 0.x version of the gem, so the version has been bumped to 1.x according to Semantic Versioning (http://semver.org/) conventions.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'doorkeeper-openid_connect', '~> 1.0.0'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install doorkeeper-openid_connect -v '~> 1.0.0'
22
+
23
+ ## Usage
24
+
25
+ Add the following to your config/routes.rb:
26
+
27
+ use_doorkeeper_openid_connect
28
+
29
+ Add the following to your config/initializers/doorkeeper_openid_connect.rb:
30
+
31
+ Doorkeeper::OpenidConnect.configure do
32
+
33
+ jws_private_key <<eol
34
+ -----BEGIN RSA PRIVATE KEY-----
35
+ ....
36
+ -----END RSA PRIVATE KEY-----
37
+ eol
38
+
39
+ jws_public_key <<eol
40
+ -----BEGIN RSA PUBLIC KEY-----
41
+ ....
42
+ -----END RSA PUBLIC KEY-----
43
+ eol
44
+
45
+ issuer 'issuer string'
46
+
47
+ subject do |resource_owner|
48
+ resource_owner.key
49
+ end
50
+
51
+ claims do
52
+ claim :_foo_ do |resource_owner|
53
+ resource_owner.foo
54
+ end
55
+
56
+ claim :_bar_ do |resource_owner|
57
+ resource_owner.bar
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ where:
64
+
65
+ The following configurations are required:
66
+
67
+ * jws_private_key - private key for JSON Web Signature(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31)
68
+ * jws_public_key - public key for JSON Web Signature(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31)
69
+ * issuer - Issuer Identifier for the Issuer of the response. The iss value is a case sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components.
70
+ * resource_owner_from_access_token - defines how to translate the doorkeeper access_token to a resource owner model
71
+
72
+ Given a resource owner, the following claims are required:
73
+
74
+ * subject - REQUIRED. Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. It MUST NOT exceed 255 ASCII characters in length. The sub value is a case sensitive string.
75
+
76
+ Custom claims can optionally be specified in a `claims` block. The following claim types are currently supported:
77
+
78
+ * normal_claim - Normal claims (http://openid.net/specs/openid-connect-core-1_0.html#NormalClaims) - specify claim name and a block using resource_owner to determine the claim value.
79
+
80
+ ## TODO
81
+
82
+ 1. Move jws_private_key and jws_public_key to a lamba expression to avoid committing keys to code
83
+
84
+ ## Contributing
85
+
86
+ 1. Fork it ( http://github.com/<my-github-username>/doorkeeper-openid_connect/fork )
87
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
88
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
89
+ 4. Push to the branch (`git push origin my-new-feature`)
90
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task default: :spec
7
+ task test: :spec
@@ -0,0 +1,19 @@
1
+ module Doorkeeper
2
+ module OpenidConnect
3
+ class UserinfoController < ::Doorkeeper::ApplicationController
4
+ include Doorkeeper::Helpers::Controller
5
+
6
+ def show
7
+ if doorkeeper_token && doorkeeper_token.accessible?
8
+ resource_owner = doorkeeper_token.instance_eval(&Doorkeeper::OpenidConnect.configuration.resource_owner_from_access_token)
9
+ user_info = Doorkeeper::OpenidConnect::Models::UserInfo.new(resource_owner)
10
+ render json: user_info, status: :ok
11
+ else
12
+ error = OAuth::ErrorResponse.new(name: :invalid_request)
13
+ response.headers.merge!(error.headers)
14
+ render json: error.body, status: error.status
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ en:
2
+ doorkeeper:
3
+ openid_connect:
4
+ errors:
5
+ messages:
6
+ # Common error messages
7
+ invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
8
+ invalid_redirect_uri: 'The redirect uri included is not valid.'
9
+ unauthorized_client: 'The client is not authorized to perform this request using this method.'
10
+ access_denied: 'The resource owner or authorization server denied the request.'
11
+ invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
12
+ server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
13
+ temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
14
+
15
+ #configuration error messages
16
+ jwt_private_key_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.jwt_private_key missing configuration.'
17
+ jwt_public_key_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.jwt_public_key missing configuration.'
18
+ issuer: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.issuer missing configuration.'
19
+ resource_owner_from_access_token_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.resource_owner_from_access_token missing configuration.'
20
+ subject_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.subject missing configuration.'
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'doorkeeper/openid_connect/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'doorkeeper-openid_connect'
7
+ spec.version = Doorkeeper::OpenidConnect::VERSION
8
+ spec.authors = ['Sam Dengler']
9
+ spec.email = ['sam.dengler@playonsports.com']
10
+ spec.homepage = 'https://github.com/playon/doorkeeper-openid_connect'
11
+ spec.summary = %q{OpenID Connect extension to Doorkeeper.}
12
+ spec.description = %q{OpenID Connect extension to Doorkeeper.}
13
+ spec.license = %q{MIT}
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'railties', '>= 3.1'
21
+ spec.add_dependency 'doorkeeper', '>= 1.3'
22
+ spec.add_dependency 'sandal', '~> 0.6.0'
23
+
24
+ spec.add_development_dependency 'rspec-rails', '~> 2.99.0'
25
+ spec.add_development_dependency 'generator_spec', '~> 0.9.0'
26
+ spec.add_development_dependency 'factory_girl', '~> 2.6.4'
27
+ end
@@ -0,0 +1,65 @@
1
+ require 'doorkeeper/openid_connect/version'
2
+ require 'doorkeeper/openid_connect/engine'
3
+
4
+ require 'doorkeeper/openid_connect/models/id_token'
5
+ require 'doorkeeper/openid_connect/models/user_info'
6
+ require 'doorkeeper/openid_connect/models/claims/claim'
7
+ require 'doorkeeper/openid_connect/models/claims/normal_claim'
8
+
9
+ require 'doorkeeper/openid_connect/claims_builder'
10
+ require 'doorkeeper/openid_connect/config'
11
+
12
+ require 'doorkeeper/openid_connect/rails/routes'
13
+
14
+ module Doorkeeper
15
+ module OpenidConnect
16
+ def self.configured?
17
+ @config.present?
18
+ end
19
+
20
+ def self.installed?
21
+ configured?
22
+ end
23
+ end
24
+ end
25
+
26
+ module Doorkeeper
27
+ module OAuth
28
+ class PasswordAccessTokenRequest
29
+ private
30
+
31
+ def after_successful_response
32
+ id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token)
33
+ @response.id_token = id_token
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ module Doorkeeper
40
+ module OAuth
41
+ class AuthorizationCodeRequest
42
+ private
43
+
44
+ def after_successful_response
45
+ id_token = Doorkeeper::OpenidConnect::Models::IdToken.new(access_token)
46
+ @response.id_token = id_token
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ module Doorkeeper
53
+ module OAuth
54
+ class TokenResponse
55
+ attr_accessor :id_token
56
+ alias_method :original_body, :body
57
+
58
+ def body
59
+ original_body.
60
+ merge({:id_token => id_token.as_jws_token}).
61
+ reject { |_, value| value.blank? }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,24 @@
1
+ require 'ostruct'
2
+
3
+ module Doorkeeper
4
+ module OpenidConnect
5
+ class ClaimsBuilder
6
+ def initialize(&block)
7
+ @claims = OpenStruct.new
8
+ instance_eval(&block)
9
+ end
10
+
11
+ def build
12
+ @claims
13
+ end
14
+
15
+ def normal_claim(name, &block)
16
+ @claims[name] =
17
+ Doorkeeper::OpenidConnect::Models::Claims::NormalClaim.new(
18
+ name: name,
19
+ value: block
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,125 @@
1
+ module Doorkeeper
2
+ module OpenidConnect
3
+ class MissingConfiguration < StandardError
4
+ def initialize
5
+ super('Configuration for Doorkeeper OpenID Connect missing. Do you have doorkeeper_openid_connect initializer?')
6
+ end
7
+ end
8
+
9
+ def self.configure(&block)
10
+ @config = Config::Builder.new(&block).build
11
+ end
12
+
13
+ def self.configuration
14
+ @config || (fail MissingConfiguration.new)
15
+ end
16
+
17
+ class Config
18
+ class Builder
19
+ def initialize(&block)
20
+ @config = Config.new
21
+ instance_eval(&block)
22
+ end
23
+
24
+ def build
25
+ @config
26
+ end
27
+ end
28
+
29
+ module Option
30
+ # Defines configuration option
31
+ #
32
+ # When you call option, it defines two methods. One method will take place
33
+ # in the +Config+ class and the other method will take place in the
34
+ # +Builder+ class.
35
+ #
36
+ # The +name+ parameter will set both builder method and config attribute.
37
+ # If the +:as+ option is defined, the builder method will be the specified
38
+ # option while the config attribute will be the +name+ parameter.
39
+ #
40
+ # If you want to introduce another level of config DSL you can
41
+ # define +builder_class+ parameter.
42
+ # Builder should take a block as the initializer parameter and respond to function +build+
43
+ # that returns the value of the config attribute.
44
+ #
45
+ # ==== Options
46
+ #
47
+ # * [:+as+] Set the builder method that goes inside +configure+ block
48
+ # * [+:default+] The default value in case no option was set
49
+ #
50
+ # ==== Examples
51
+ #
52
+ # option :name
53
+ # option :name, as: :set_name
54
+ # option :name, default: 'My Name'
55
+ # option :scopes builder_class: ScopesBuilder
56
+ #
57
+ def option(name, options = {})
58
+ attribute = options[:as] || name
59
+ attribute_builder = options[:builder_class]
60
+
61
+ Builder.instance_eval do
62
+ define_method name do |*args, &block|
63
+ # TODO: is builder_class option being used?
64
+ value = unless attribute_builder
65
+ block ? block : args.first
66
+ else
67
+ attribute_builder.new(&block).build
68
+ end
69
+
70
+ @config.instance_variable_set(:"@#{attribute}", value)
71
+ end
72
+ end
73
+
74
+ define_method attribute do |*args|
75
+ if instance_variable_defined?(:"@#{attribute}")
76
+ instance_variable_get(:"@#{attribute}")
77
+ else
78
+ options[:default]
79
+ end
80
+ end
81
+
82
+ public attribute
83
+ end
84
+
85
+ def extended(base)
86
+ base.send(:private, :option)
87
+ end
88
+ end
89
+
90
+ extend Option
91
+
92
+ option :jws_private_key,
93
+ default: (lambda do
94
+ logger.warn(I18n.translate('doorkeeper.openid_connect.errors.messages.jws_private_key_configured'))
95
+ nil
96
+ end)
97
+
98
+ option :jws_public_key,
99
+ default: (lambda do
100
+ logger.warn(I18n.translate('doorkeeper.openid_connect.errors.messages.jws_public_key_configured'))
101
+ nil
102
+ end)
103
+
104
+ option :issuer,
105
+ default: (lambda do
106
+ logger.warn(I18n.translate('doorkeeper.openid_connect.errors.messages.issuer_configured'))
107
+ nil
108
+ end)
109
+
110
+ option :resource_owner_from_access_token,
111
+ default: (lambda do
112
+ logger.warn(I18n.translate('doorkeeper.openid_connect.errors.messages.resource_owner_from_access_token_configured'))
113
+ nil
114
+ end)
115
+
116
+ option :subject,
117
+ default: (lambda do
118
+ logger.warn(I18n.translate('doorkeeper.openid_connect.errors.messages.subject_configured'))
119
+ nil
120
+ end)
121
+
122
+ option :claims, builder_class: ClaimsBuilder
123
+ end
124
+ end
125
+ end