oidc_provider 0.1.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +75 -0
- data/Rakefile +32 -0
- data/app/controllers/oidc_provider/application_controller.rb +5 -0
- data/app/controllers/oidc_provider/authorizations_controller.rb +47 -0
- data/app/controllers/oidc_provider/concerns/authentication.rb +23 -0
- data/app/controllers/oidc_provider/concerns/connect_endpoint.rb +41 -0
- data/app/controllers/oidc_provider/discovery_controller.rb +46 -0
- data/app/controllers/oidc_provider/user_infos_controller.rb +9 -0
- data/app/models/oidc_provider/access_token.rb +17 -0
- data/app/models/oidc_provider/application_record.rb +5 -0
- data/app/models/oidc_provider/authorization.rb +41 -0
- data/app/models/oidc_provider/id_token.rb +47 -0
- data/config/initializers/inflections.rb +3 -0
- data/config/routes.rb +7 -0
- data/lib/generators/oidc_provider/install_generator.rb +35 -0
- data/lib/generators/oidc_provider/orm_helpers.rb +40 -0
- data/lib/generators/oidc_provider/templates/initializer.rb +30 -0
- data/lib/oidc_provider/account_to_user_info.rb +12 -0
- data/lib/oidc_provider/client/builder.rb +21 -0
- data/lib/oidc_provider/client.rb +14 -0
- data/lib/oidc_provider/client_store.rb +13 -0
- data/lib/oidc_provider/engine.rb +13 -0
- data/lib/oidc_provider/scope.rb +10 -0
- data/lib/oidc_provider/token_endpoint.rb +33 -0
- data/lib/oidc_provider/user_info_builder.rb +18 -0
- data/lib/oidc_provider/version.rb +3 -0
- data/lib/oidc_provider.rb +48 -0
- data/lib/tasks/openid/connect/provider_tasks.rake +4 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f9ecb70ab0d1be5f3dae4837a3e073a1360e0bba
|
4
|
+
data.tar.gz: 0e1f83398394d4441377dd28e48401ccfcd891f9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05b71caa7fe69bd7162beb297d509e586f2e5a37f1520b5362549e8a31aeb0a654269f80fb24bac832fd4991a0634d59a481cd58fa36b62a50e65ddfc1d3df2f
|
7
|
+
data.tar.gz: '088d8b1ceb4d00dbc3de928187017e9f72505f73e15564a519083addb9409e9b814f31199b7c32bb1d20494bb5932bea3f477a9f204de4572576ab3d16c2f90e'
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# OIDCProvider
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
Use your application as an Open ID provider.
|
6
|
+
|
7
|
+
You'll need some kind of user authentication to exist on you application first. Normally we use Devise.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'oidc_provider'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
```bash
|
18
|
+
$ bundle
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
```bash
|
23
|
+
$ gem install oidc_provider
|
24
|
+
```
|
25
|
+
|
26
|
+
Install the support stuffs:
|
27
|
+
|
28
|
+
* an initializer
|
29
|
+
* default routes to your routes file
|
30
|
+
|
31
|
+
```bash
|
32
|
+
$ rails generate oidc_provider:install
|
33
|
+
```
|
34
|
+
|
35
|
+
Migrations next:
|
36
|
+
|
37
|
+
```bash
|
38
|
+
$ rails oidc_provider:install:migrations
|
39
|
+
$ rails db:migrate
|
40
|
+
```
|
41
|
+
|
42
|
+
### Private Key
|
43
|
+
|
44
|
+
You will need to generate a unique private key per application.
|
45
|
+
|
46
|
+
```bash
|
47
|
+
$ ssh-keygen
|
48
|
+
```
|
49
|
+
|
50
|
+
Due to Docker Composes' lack of support for multiline `.env` variables, put a passphrase on it. Then add the key to your application at `lib/oidc_provider_key.pem` and add the passphrase as an environment variables in your application: `ENV["OIDC_PROVIDER_KEY_PASSPHRASE"]`.
|
51
|
+
|
52
|
+
# Testing configuration
|
53
|
+
|
54
|
+
Visit: https://demo.c2id.com/oidc-client/
|
55
|
+
|
56
|
+
Click "OpenID provider details"
|
57
|
+
|
58
|
+
Put in your website as the issuer and click "Query"
|
59
|
+
|
60
|
+
You should see values generated for all 4 endpoints below.
|
61
|
+
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
Contribution directions go here.
|
65
|
+
|
66
|
+
## License
|
67
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
68
|
+
|
69
|
+
## How to build this gem
|
70
|
+
|
71
|
+
```
|
72
|
+
gem build oidc_provider.gemspec
|
73
|
+
gem push channel_research_stationery-2.10.gem
|
74
|
+
gem yank -v 2.10 channel_research_stationery
|
75
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Openid::Connect::Provider'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'test'
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
29
|
+
t.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
task default: :test
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class AuthorizationsController < ApplicationController
|
3
|
+
include Concerns::ConnectEndpoint
|
4
|
+
|
5
|
+
before_action :require_oauth_request
|
6
|
+
before_action :require_response_type_code
|
7
|
+
before_action :require_client
|
8
|
+
before_action :require_authentication
|
9
|
+
|
10
|
+
def create
|
11
|
+
puts "scopes: #{requested_scopes}"
|
12
|
+
authorization = Authorization.create(
|
13
|
+
client_id: @client.identifier,
|
14
|
+
nonce: oauth_request.nonce,
|
15
|
+
scopes: requested_scopes,
|
16
|
+
account: current_account
|
17
|
+
)
|
18
|
+
|
19
|
+
oauth_response.code = authorization.code
|
20
|
+
oauth_response.redirect_uri = @redirect_uri
|
21
|
+
oauth_response.approve!
|
22
|
+
redirect_to oauth_response.location
|
23
|
+
|
24
|
+
# If we ever need to support denied authorizations that is done by:
|
25
|
+
# oauth_request.access_denied!
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def require_client
|
31
|
+
@client = ClientStore.new.find_by(identifier: oauth_request.client_id) or oauth_request.invalid_request! 'not a valid client'
|
32
|
+
@redirect_uri = oauth_request.verify_redirect_uri! [oauth_request.redirect_uri, @client.redirect_uri]
|
33
|
+
end
|
34
|
+
|
35
|
+
def requested_scopes
|
36
|
+
@requested_scopes ||= (["openid"] + OIDCProvider.supported_scopes.map(&:name)) & oauth_request.scope
|
37
|
+
end
|
38
|
+
helper_method :requested_scopes
|
39
|
+
|
40
|
+
def require_response_type_code
|
41
|
+
unless oauth_request.response_type == :code
|
42
|
+
oauth_request.unsupported_response_type!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
module Concerns
|
3
|
+
module Authentication
|
4
|
+
def current_account
|
5
|
+
send(OIDCProvider.current_account_method)
|
6
|
+
end
|
7
|
+
|
8
|
+
def current_token
|
9
|
+
@current_token ||= request.env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN]
|
10
|
+
end
|
11
|
+
|
12
|
+
def require_authentication
|
13
|
+
authenticate_user!
|
14
|
+
end
|
15
|
+
|
16
|
+
def require_access_token
|
17
|
+
unless current_token
|
18
|
+
raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
module Concerns
|
3
|
+
module ConnectEndpoint
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
helper_method :oauth_request
|
8
|
+
rescue_from Rack::OAuth2::Server::Authorize::BadRequest, with: :handle_oauth_error!
|
9
|
+
end
|
10
|
+
|
11
|
+
def oauth_request
|
12
|
+
request.env[Rack::OAuth2::Server::Rails::REQUEST]
|
13
|
+
end
|
14
|
+
|
15
|
+
def oauth_response
|
16
|
+
request.env[Rack::OAuth2::Server::Rails::RESPONSE]
|
17
|
+
end
|
18
|
+
|
19
|
+
def oauth_error
|
20
|
+
request.env[Rack::OAuth2::Server::Rails::ERROR]
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_oauth_error!(e)
|
24
|
+
if e.redirect?
|
25
|
+
raise e # NOTE: rack middleware should handle this error.
|
26
|
+
else
|
27
|
+
render plain: e.message, status: e.status
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def require_oauth_request
|
32
|
+
if oauth_error
|
33
|
+
raise oauth_error
|
34
|
+
end
|
35
|
+
unless oauth_request && oauth_response
|
36
|
+
raise 'should\'t happen'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class DiscoveryController < ApplicationController
|
3
|
+
def show
|
4
|
+
case params[:id]
|
5
|
+
when 'webfinger'
|
6
|
+
webfinger_discovery
|
7
|
+
when 'openid-configuration'
|
8
|
+
openid_configuration
|
9
|
+
else
|
10
|
+
render plain: "Not found", status: :not_found
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def webfinger_discovery
|
17
|
+
jrd = {
|
18
|
+
links: [{
|
19
|
+
rel: OpenIDConnect::Discovery::Provider::Issuer::REL_VALUE,
|
20
|
+
href: OIDCProvider.issuer
|
21
|
+
}]
|
22
|
+
}
|
23
|
+
jrd[:subject] = params[:resource] if params[:resource].present?
|
24
|
+
render json: jrd, content_type: Mime::JRD
|
25
|
+
end
|
26
|
+
|
27
|
+
def openid_configuration
|
28
|
+
config = OpenIDConnect::Discovery::Provider::Config::Response.new(
|
29
|
+
issuer: OIDCProvider.issuer,
|
30
|
+
authorization_endpoint: authorizations_url,
|
31
|
+
token_endpoint: tokens_url,
|
32
|
+
userinfo_endpoint: user_info_url,
|
33
|
+
jwks_uri: jwks_url,
|
34
|
+
scopes_supported: ["openid"] + OIDCProvider.supported_scopes.map(&:name),
|
35
|
+
response_types_supported: [:code],
|
36
|
+
grant_types_supported: [:authorization_code],
|
37
|
+
subject_types_supported: [:public],
|
38
|
+
id_token_signing_alg_values_supported: [:RS256],
|
39
|
+
token_endpoint_auth_methods_supported: ['client_secret_basic', 'client_secret_post'],
|
40
|
+
claims_supported: ['sub', 'iss', 'name', 'email']
|
41
|
+
)
|
42
|
+
render json: config
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class AccessToken < ApplicationRecord
|
3
|
+
belongs_to :authorization
|
4
|
+
|
5
|
+
scope :valid, -> { where(arel_table[:expires_at].gteq(Time.now.utc)) }
|
6
|
+
|
7
|
+
attribute :token, :string, default: -> { SecureRandom.hex 32 }
|
8
|
+
attribute :expires_at, :datetime, default: -> { 1.hours.from_now }
|
9
|
+
|
10
|
+
def to_bearer_token
|
11
|
+
Rack::OAuth2::AccessToken::Bearer.new(
|
12
|
+
access_token: token,
|
13
|
+
expires_in: (expires_at - Time.now).to_i
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class Authorization < ApplicationRecord
|
3
|
+
belongs_to :account, class_name: OIDCProvider.account_class
|
4
|
+
has_one :access_token
|
5
|
+
has_one :id_token
|
6
|
+
|
7
|
+
scope :valid, -> { where(arel_table[:expires_at].gteq(Time.now.utc)) }
|
8
|
+
|
9
|
+
attribute :code, :string, default: -> { SecureRandom.hex 32 }
|
10
|
+
attribute :expires_at, :datetime, default: -> { 5.minutes.from_now }
|
11
|
+
|
12
|
+
serialize :scopes, JSON
|
13
|
+
|
14
|
+
def expire!
|
15
|
+
self.expires_at = Time.now
|
16
|
+
self.save!
|
17
|
+
end
|
18
|
+
|
19
|
+
def access_token
|
20
|
+
super || expire! && generate_access_token!
|
21
|
+
end
|
22
|
+
|
23
|
+
def id_token
|
24
|
+
super || generate_id_token!
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def generate_access_token!
|
30
|
+
token = create_access_token!
|
31
|
+
token
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate_id_token!
|
35
|
+
token = build_id_token
|
36
|
+
token.nonce = nonce
|
37
|
+
token.save!
|
38
|
+
token
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class IdToken < ApplicationRecord
|
3
|
+
belongs_to :authorization
|
4
|
+
|
5
|
+
attribute :expires_at, :datetime, default: -> { 1.hour.from_now }
|
6
|
+
|
7
|
+
delegate :account, to: :authorization
|
8
|
+
|
9
|
+
def to_response_object
|
10
|
+
OpenIDConnect::ResponseObject::IdToken.new(
|
11
|
+
iss: OIDCProvider.issuer,
|
12
|
+
sub: account.send(OIDCProvider.account_identifier),
|
13
|
+
aud: authorization.client_id,
|
14
|
+
nonce: nonce,
|
15
|
+
exp: expires_at.to_i,
|
16
|
+
iat: created_at.to_i
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_jwt
|
21
|
+
to_response_object.to_jwt(self.class.private_jwk)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def key_pair
|
28
|
+
@key_pair ||= OpenSSL::PKey::RSA.new(File.read(Rails.root.join("lib/oidc_provider_key.pem")), ENV["OIDC_PROVIDER_KEY_PASSPHRASE"])
|
29
|
+
end
|
30
|
+
|
31
|
+
def private_jwk
|
32
|
+
JSON::JWK.new key_pair
|
33
|
+
end
|
34
|
+
|
35
|
+
def public_jwk
|
36
|
+
JSON::JWK.new key_pair.public_key
|
37
|
+
end
|
38
|
+
|
39
|
+
def config
|
40
|
+
{
|
41
|
+
issuer: OIDCProvider.issuer,
|
42
|
+
jwk_set: JSON::JWK::Set.new(public_jwk)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
OIDCProvider::Engine.routes.draw do
|
2
|
+
match 'authorizations' => 'authorizations#create', via: [:get, :post]
|
3
|
+
resource :user_info, only: :show
|
4
|
+
|
5
|
+
post 'tokens', to: proc { |env| OIDCProvider::TokenEndpoint.new.call(env) }
|
6
|
+
get 'jwks.json', as: :jwks, to: proc { |env| [200, {'Content-Type' => 'application/json'}, [OIDCProvider::IdToken.config[:jwk_set].to_json]] }
|
7
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'rails/generators/base'
|
4
|
+
# require 'securerandom'
|
5
|
+
|
6
|
+
module OidcProvider
|
7
|
+
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
9
|
+
source_root File.expand_path('templates', __dir__)
|
10
|
+
|
11
|
+
desc "Creates a OIDCProvider initializer."
|
12
|
+
|
13
|
+
def copy_initializer_file
|
14
|
+
copy_file "initializer.rb", "config/initializers/oidc_provider.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_routes
|
18
|
+
route "get '/.well-known/:id', to: 'oidc_provider/discovery#show'"
|
19
|
+
route "mount OIDCProvider::Engine, at: '/accounts/oauth'"
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# def copy_locale
|
24
|
+
# copy_file "../../../config/locales/en.yml", "config/locales/devise.en.yml"
|
25
|
+
# end
|
26
|
+
|
27
|
+
# def show_readme
|
28
|
+
# readme "README" if behavior == :invoke
|
29
|
+
# end
|
30
|
+
|
31
|
+
# def rails_4?
|
32
|
+
# Rails::VERSION::MAJOR == 4
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OIDCProvider
|
4
|
+
module Generators
|
5
|
+
module OrmHelpers
|
6
|
+
# def model_contents
|
7
|
+
# buffer = <<-CONTENT
|
8
|
+
# # Include default devise modules. Others available are:
|
9
|
+
# # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
10
|
+
# devise :database_authenticatable, :registerable,
|
11
|
+
# :recoverable, :rememberable, :validatable
|
12
|
+
|
13
|
+
# CONTENT
|
14
|
+
# buffer
|
15
|
+
# end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# def model_exists?
|
20
|
+
# File.exist?(File.join(destination_root, model_path))
|
21
|
+
# end
|
22
|
+
|
23
|
+
# def migration_exists?(table_name)
|
24
|
+
# Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_devise_to_#{table_name}.rb$/).first
|
25
|
+
# end
|
26
|
+
|
27
|
+
def migration_path
|
28
|
+
if Rails.version >= '5.0.3'
|
29
|
+
db_migrate_path
|
30
|
+
else
|
31
|
+
@migration_path ||= File.join("db", "migrate")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# def model_path
|
36
|
+
# @model_path ||= File.join("app", "models", "#{file_path}.rb")
|
37
|
+
# end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
OIDCProvider.configure do |config|
|
2
|
+
config.issuer = "https://myoidcprovider.org"
|
3
|
+
# config.account_class = "Administrator"
|
4
|
+
|
5
|
+
config.add_client do
|
6
|
+
name "Test Client"
|
7
|
+
identifier '0001'
|
8
|
+
secret '27ffb...'
|
9
|
+
redirect_uri 'https://demo.c2id.com/oidc-client/cb'
|
10
|
+
end
|
11
|
+
|
12
|
+
config.add_client do
|
13
|
+
name "Client 1"
|
14
|
+
identifier "client_01"
|
15
|
+
secret "6f14c..."
|
16
|
+
redirect_uri "https://clientone.com/TDE/openid_connect_login"
|
17
|
+
end
|
18
|
+
|
19
|
+
config.add_scope OIDCProvider::Scopes::Profile do |user|
|
20
|
+
name user.full_name
|
21
|
+
given_name user.first_name
|
22
|
+
family_name user.last_name
|
23
|
+
end
|
24
|
+
|
25
|
+
config.add_scope OIDCProvider::Scopes::Email do |user|
|
26
|
+
email user.email
|
27
|
+
email_verified false
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class AccountToUserInfo
|
3
|
+
def call(account, scope_names)
|
4
|
+
scopes = scope_names.map { |name| OIDCProvider.supported_scopes.detect { |scope| scope.name == name } }.compact
|
5
|
+
OpenIDConnect::ResponseObject::UserInfo.new(sub: account.send(OIDCProvider.account_identifier)).tap do |user_info|
|
6
|
+
scopes.each do |scope|
|
7
|
+
UserInfoBuilder.new(user_info, account).run(&scope.work)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class Client
|
3
|
+
class Builder
|
4
|
+
def initialize(&block)
|
5
|
+
@client = Client.new
|
6
|
+
@operation = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
instance_exec(&@operation)
|
11
|
+
@client
|
12
|
+
end
|
13
|
+
|
14
|
+
[:identifier, :secret, :redirect_uri, :name].each do |attr|
|
15
|
+
define_method attr do |val|
|
16
|
+
@client.send("#{attr}=", val)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class Client
|
3
|
+
attr_accessor :identifier, :secret, :redirect_uri, :name
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@identifier = options[:identifier]
|
7
|
+
@secret = options[:secret]
|
8
|
+
@redirect_uri = options[:redirect_uri]
|
9
|
+
@name = options[:name]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'oidc_provider/client/builder'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class ClientStore
|
3
|
+
attr_reader :clients
|
4
|
+
|
5
|
+
def initialize(clients = OIDCProvider.clients)
|
6
|
+
@clients = clients
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_by(attrs)
|
10
|
+
clients.detect { |client| attrs.all? { |key, value| client.send(key) == value } }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rack/oauth2'
|
2
|
+
|
3
|
+
module OIDCProvider
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace OIDCProvider
|
6
|
+
|
7
|
+
config.middleware.use Rack::OAuth2::Server::Rails::Authorize
|
8
|
+
config.middleware.use Rack::OAuth2::Server::Resource::Bearer, 'OpenID Connect' do |req|
|
9
|
+
AccessToken.valid.find_by(token: req.access_token) ||
|
10
|
+
req.invalid_token!
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class TokenEndpoint
|
3
|
+
attr_accessor :app
|
4
|
+
delegate :call, to: :app
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@app = Rack::OAuth2::Server::Token.new do |req, res|
|
8
|
+
Rails.logger.info "Client ID: #{req.client_id}"
|
9
|
+
Rails.logger.info "Client secret: #{req.client_secret}"
|
10
|
+
Rails.logger.info "Redirect URI: #{req.redirect_uri}"
|
11
|
+
client = ClientStore.new.find_by(
|
12
|
+
identifier: req.client_id,
|
13
|
+
secret: req.client_secret,
|
14
|
+
redirect_uri: req.redirect_uri
|
15
|
+
) || req.invalid_client!
|
16
|
+
|
17
|
+
Rails.logger.info "Found a client!"
|
18
|
+
|
19
|
+
case req.grant_type
|
20
|
+
when :authorization_code
|
21
|
+
Rails.logger.info "Grant type was an authorization code. Correct!"
|
22
|
+
authorization = Authorization.valid.where(client_id: client.identifier, code: req.code).first || req.invalid_grant!
|
23
|
+
Rails.logger.info "We found an authorization matching this code!"
|
24
|
+
res.access_token = authorization.access_token.to_bearer_token
|
25
|
+
res.id_token = authorization.id_token.to_jwt if authorization.scopes.include?("openid")
|
26
|
+
else
|
27
|
+
Rails.logger.info "Unsupported grant type"
|
28
|
+
req.unsupported_grant_type!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OIDCProvider
|
2
|
+
class UserInfoBuilder
|
3
|
+
attr_reader :user_info
|
4
|
+
|
5
|
+
def initialize(user_info, account)
|
6
|
+
@user_info = user_info
|
7
|
+
@account = account
|
8
|
+
end
|
9
|
+
|
10
|
+
def run(&block)
|
11
|
+
instance_exec(@account, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(sym, *args)
|
15
|
+
@user_info.send("#{sym}=", *args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "openid_connect"
|
2
|
+
require "oidc_provider/engine"
|
3
|
+
|
4
|
+
module OIDCProvider
|
5
|
+
|
6
|
+
module Scopes
|
7
|
+
OpenID = "openid"
|
8
|
+
Profile = "profile"
|
9
|
+
Email = "email"
|
10
|
+
Address = "address"
|
11
|
+
end
|
12
|
+
|
13
|
+
autoload :TokenEndpoint, 'oidc_provider/token_endpoint'
|
14
|
+
autoload :ClientStore, 'oidc_provider/client_store'
|
15
|
+
autoload :Client, 'oidc_provider/client'
|
16
|
+
autoload :AccountToUserInfo, 'oidc_provider/account_to_user_info'
|
17
|
+
autoload :Scope, 'oidc_provider/scope'
|
18
|
+
autoload :UserInfoBuilder, 'oidc_provider/user_info_builder'
|
19
|
+
|
20
|
+
mattr_accessor :issuer
|
21
|
+
|
22
|
+
mattr_accessor :supported_scopes
|
23
|
+
@@supported_scopes = []
|
24
|
+
|
25
|
+
mattr_accessor :clients
|
26
|
+
@@clients = []
|
27
|
+
|
28
|
+
mattr_accessor :account_class
|
29
|
+
@@account_class = "User"
|
30
|
+
|
31
|
+
mattr_accessor :current_account_method
|
32
|
+
@@current_account_method = :current_user
|
33
|
+
|
34
|
+
mattr_accessor :account_identifier
|
35
|
+
@@account_identifier = :id
|
36
|
+
|
37
|
+
def self.add_client(&block)
|
38
|
+
@@clients << Client::Builder.new(&block).build
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.add_scope(name, &block)
|
42
|
+
@@supported_scopes << Scope.new(name, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.configure
|
46
|
+
yield self
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oidc_provider
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- William Carey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: openid_connect
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.3
|
41
|
+
description: A Rails engine for providing OpenID Connect authorization.
|
42
|
+
email:
|
43
|
+
- willtcarey@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- MIT-LICENSE
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- app/controllers/oidc_provider/application_controller.rb
|
52
|
+
- app/controllers/oidc_provider/authorizations_controller.rb
|
53
|
+
- app/controllers/oidc_provider/concerns/authentication.rb
|
54
|
+
- app/controllers/oidc_provider/concerns/connect_endpoint.rb
|
55
|
+
- app/controllers/oidc_provider/discovery_controller.rb
|
56
|
+
- app/controllers/oidc_provider/user_infos_controller.rb
|
57
|
+
- app/models/oidc_provider/access_token.rb
|
58
|
+
- app/models/oidc_provider/application_record.rb
|
59
|
+
- app/models/oidc_provider/authorization.rb
|
60
|
+
- app/models/oidc_provider/id_token.rb
|
61
|
+
- config/initializers/inflections.rb
|
62
|
+
- config/routes.rb
|
63
|
+
- lib/generators/oidc_provider/install_generator.rb
|
64
|
+
- lib/generators/oidc_provider/orm_helpers.rb
|
65
|
+
- lib/generators/oidc_provider/templates/initializer.rb
|
66
|
+
- lib/oidc_provider.rb
|
67
|
+
- lib/oidc_provider/account_to_user_info.rb
|
68
|
+
- lib/oidc_provider/client.rb
|
69
|
+
- lib/oidc_provider/client/builder.rb
|
70
|
+
- lib/oidc_provider/client_store.rb
|
71
|
+
- lib/oidc_provider/engine.rb
|
72
|
+
- lib/oidc_provider/scope.rb
|
73
|
+
- lib/oidc_provider/token_endpoint.rb
|
74
|
+
- lib/oidc_provider/user_info_builder.rb
|
75
|
+
- lib/oidc_provider/version.rb
|
76
|
+
- lib/tasks/openid/connect/provider_tasks.rake
|
77
|
+
homepage: http://brandnewbox.com
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.5.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Uses the openid_connect gem to turn a Rails app into an OpenID Connect provider.
|
101
|
+
test_files: []
|