Doorkeeper::OpenidConnect
This library implements an OpenID Connect authentication provider for Rails applications on top of the Doorkeeper OAuth 2.0 framework.
OpenID Connect is a single-sign-on and identity layer with a growing list of server and client implementations. If you're looking for a client in Ruby check out omniauth-openid-connect.
Table of Contents
Status
The following parts of OpenID Connect Core 1.0 are currently supported:
- Authentication using the Authorization Code Flow
- Requesting Claims using Scope Values
- UserInfo Endpoint
- Normal Claims
In addition we also support most of OpenID Connect Discovery 1.0 for automatic configuration discovery.
Take a look at the DiscoveryController for more details on supported features.
Installation
Make sure your application is already set up with Doorkeeper.
Add this line to your application's Gemfile
and run bundle install
:
gem 'doorkeeper-openid_connect'
Run the installation generator to update routes and create the initializer:
rails generate doorkeeper:openid_connect:install
Generate a migration for Active Record (other ORMs are currently not supported):
rails generate doorkeeper:openid_connect:migration
rake db:migrate
If you're upgrading from an earlier version, check CHANGELOG.md for upgrade instructions.
Configuration
Make sure you've configured Doorkeeper before continuing.
Verify your settings in config/initializers/doorkeeper.rb
:
resource_owner_authenticator
-
This callback needs to returns a falsey value if the current user can't be determined:
resource_owner_authenticator do if current_user current_user else redirect_to(new_user_session_url) nil end end
-
The following settings are required in config/initializers/doorkeeper_openid_connect.rb
:
issuer
- Identifier for the issuer of the response (i.e. your application URL). The 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.
- Identifier for the issuer of the response (i.e. your application URL). The value is a case sensitive URL using the
subject
- Identifier for the resource owner (i.e. the authenticated user). A locally unique and never reassigned identifier within the issuer for the end-user, which is intended to be consumed by the client. The value is a case-sensitive string and must not exceed 255 ASCII characters in length.
- The database ID of the user is an acceptable choice if you don't mind leaking that information.
jws_private_key
- Private RSA key for JSON Web Signature.
- You can generate a private key with the
openssl
command, see e.g. Generate a keypair using OpenSSL. - You should not commit the key to your repository, but use an external file (in combination with
File.read
) and/or the dotenv-rails gem (in combination withENV[...]
).
resource_owner_from_access_token
- Defines how to translate the Doorkeeper access token to a resource owner model.
The following settings are optional, but recommended for better client compatibility:
auth_time_from_resource_owner
- Returns the time of the user's last login, this can be a
Time
,DateTime
, or any other class that responds toto_i
- Required to support the
max_age
parameter and theauth_time
claim.
- Returns the time of the user's last login, this can be a
reauthenticate_resource_owner
- Defines how to trigger reauthentication for the current user (e.g. display a password prompt, or sign-out the user and redirect to the login form).
- Required to support the
max_age
andprompt=login
parameters. - The block is executed in the controller's scope, so you have access to methods like
params
,redirect_to
etc.
The following settings are optional:
-
expiration
- Expiration time after which the ID Token must not be accepted for processing by clients.
- The default is 120 seconds
-
protocol
- The protocol to use when generating URIs for the discovery endpoints.
- The default is
https
for production, andhttp
for all other environments - Note that the OIC specification mandates HTTPS, so you shouldn't change this for production environments unless you have a really good reason!
Scopes
To perform authentication over OpenID Connect, an OAuth client needs to request the openid
scope. This scope needs to be enabled using either optional_scopes
in the global Doorkeeper configuration in config/initializers/doorkeeper.rb
, or by adding it to any OAuth application's scope
attribute.
Note that any application defining its own scopes won't inherit the scopes defined in the initializer, so you might have to update existing applications as well.
See Using Scopes in the Doorkeeper wiki for more information.
Claims
Claims can be defined in a claims
block inside config/initializers/doorkeeper_openid_connect.rb
:
Doorkeeper::OpenidConnect.configure do
claims do
claim :email do |resource_owner|
resource_owner.email
end
claim :full_name do |resource_owner|
"#{resource_owner.first_name} #{resource_owner.last_name}"
end
claim :preferred_username, scope: :openid do |resource_owner, application_scopes|
# Pass the resource_owner's preferred_username if the application has
# `profile` scope access. Otherwise, provide a more generic alternative.
application_scopes.exists?(:profile) ? resource_owner.preferred_username : "summer-sun-9449"
end
end
end
You can pass a scope:
keyword argument on each claim to specify which OAuth scope should be required to access the claim. If you define any of the defined Standard Claims they will by default use their corresponding scopes (profile
, email
, address
and phone
), and any other claims will by default use the profile
scope. Again, to use any of these scopes you need to enable them as described above.
Routes
The installation generator will update your config/routes.rb
to define all required routes:
Rails.application.routes.draw do
use_doorkeeper_openid_connect
# your routes
end
This will mount the following routes:
GET /oauth/userinfo
POST /oauth/userinfo
GET /oauth/discovery/keys
GET /.well-known/openid-configuration
GET /.well-known/webfinger
With the exception of the hard-coded /.well-known
paths (see RFC 5785) you can customize routes in the same way as with Doorkeeper, please refer to this page on their wiki.
Nonces
To support clients who send nonces you have to tweak Doorkeeper's authorization view so the parameter is passed on.
If you don't already have custom templates, run this generator in your Rails application to add them:
rails generate doorkeeper:views
Then tweak the template as follows:
--- i/app/views/doorkeeper/authorizations/new.html.erb
+++ w/app/views/doorkeeper/authorizations/new.html.erb
@@ -26,6 +26,7 @@
<%= hidden_field_tag :state, @pre_auth.state %>
<%= hidden_field_tag :response_type, @pre_auth.response_type %>
<%= hidden_field_tag :scope, @pre_auth.scope %>
+ <%= hidden_field_tag :nonce, @pre_auth.nonce %>
<%= submit_tag t('doorkeeper.authorizations.buttons.authorize'), class: "btn btn-success btn-lg btn-block" %>
<% end %>
<%= form_tag oauth_authorization_path, method: :delete do %>
@@ -34,6 +35,7 @@
<%= hidden_field_tag :state, @pre_auth.state %>
<%= hidden_field_tag :response_type, @pre_auth.response_type %>
<%= hidden_field_tag :scope, @pre_auth.scope %>
+ <%= hidden_field_tag :nonce, @pre_auth.nonce %>
<%= submit_tag t('doorkeeper.authorizations.buttons.deny'), class: "btn btn-danger btn-lg btn-block" %>
<% end %>
</div>
Internationalization (I18n)
We use Rails locale files for error messages and scope descriptions, see config/locales/en.yml. You can override these by adding them to your own translations in config/locale
.
Development
Run bundle install
to setup all development dependencies.
To run all specs:
bundle exec rspec
To run the local engine server:
cd spec/dummy
bundle exec rails server
By default, the latest Rails version is used. To use a specific version run:
rails=4.2.0 bundle update
License
Doorkeeper::OpenidConnect is released under the MIT License.
Sponsors
Initial development of this project was sponsored by PlayOn! Sports.