oidc_provider 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|