Add openid connect support using doorkeeper-openid_connect gem
... as discussed in [Issue 507](https://github.com/openstreetmap/operations/issues/507) and described by @mmd-osm. To activate, set the value of `doorkeeper_signing_key` to RSA private key. Allows using openstreetmap as an identity provider. Adds `openid` scope to OAuth2 authorizations, required to login to OSM. Currently, the only claims returned are: - "openid" scope: "sub" and "preferred_username" - "read_email" scope: "email"
This commit is contained in:
parent
e996ee5dbc
commit
64bcf7652b
8 changed files with 31 additions and 54 deletions
|
@ -3,5 +3,5 @@
|
||||||
<%= f.form_group :confidential do %>
|
<%= f.form_group :confidential do %>
|
||||||
<%= f.check_box :confidential %>
|
<%= f.check_box :confidential %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= f.collection_check_boxes :scopes, Oauth.scopes(:privileged => current_user.administrator?), :name, :description %>
|
<%= f.collection_check_boxes :scopes, Oauth.scopes(:oauth2 => true, :privileged => current_user.administrator?), :name, :description %>
|
||||||
<%= f.primary %>
|
<%= f.primary %>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<%= f.hidden_field :state, :value => @pre_auth.state %>
|
<%= f.hidden_field :state, :value => @pre_auth.state %>
|
||||||
<%= f.hidden_field :response_type, :value => @pre_auth.response_type %>
|
<%= f.hidden_field :response_type, :value => @pre_auth.response_type %>
|
||||||
<%= f.hidden_field :scope, :value => @pre_auth.scope %>
|
<%= f.hidden_field :scope, :value => @pre_auth.scope %>
|
||||||
|
<%= f.hidden_field :nonce, :value => @pre_auth.nonce %>
|
||||||
<%= f.hidden_field :code_challenge, :value => @pre_auth.code_challenge %>
|
<%= f.hidden_field :code_challenge, :value => @pre_auth.code_challenge %>
|
||||||
<%= f.hidden_field :code_challenge_method, :value => @pre_auth.code_challenge_method %>
|
<%= f.hidden_field :code_challenge_method, :value => @pre_auth.code_challenge_method %>
|
||||||
<%= f.primary t(".authorize") %>
|
<%= f.primary t(".authorize") %>
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
<%= f.hidden_field :state, :value => @pre_auth.state %>
|
<%= f.hidden_field :state, :value => @pre_auth.state %>
|
||||||
<%= f.hidden_field :response_type, :value => @pre_auth.response_type %>
|
<%= f.hidden_field :response_type, :value => @pre_auth.response_type %>
|
||||||
<%= f.hidden_field :scope, :value => @pre_auth.scope %>
|
<%= f.hidden_field :scope, :value => @pre_auth.scope %>
|
||||||
|
<%= f.hidden_field :nonce, :value => @pre_auth.nonce %>
|
||||||
<%= f.hidden_field :code_challenge, :value => @pre_auth.code_challenge %>
|
<%= f.hidden_field :code_challenge, :value => @pre_auth.code_challenge %>
|
||||||
<%= f.hidden_field :code_challenge_method, :value => @pre_auth.code_challenge_method %>
|
<%= f.hidden_field :code_challenge_method, :value => @pre_auth.code_challenge_method %>
|
||||||
<%= f.submit t(".deny") %>
|
<%= f.submit t(".deny") %>
|
||||||
|
|
|
@ -225,7 +225,7 @@ Doorkeeper.configure do
|
||||||
# https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
|
# https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
|
||||||
|
|
||||||
# default_scopes :public
|
# default_scopes :public
|
||||||
optional_scopes(*Oauth::SCOPES, *Oauth::PRIVILEGED_SCOPES)
|
optional_scopes(*Oauth::SCOPES, *Oauth::PRIVILEGED_SCOPES, *Oauth::OAUTH2_SCOPES)
|
||||||
|
|
||||||
# Allows to restrict only certain scopes for grant_type.
|
# Allows to restrict only certain scopes for grant_type.
|
||||||
# By default, all the scopes will be available for all the grant types.
|
# By default, all the scopes will be available for all the grant types.
|
||||||
|
|
|
@ -2,71 +2,37 @@
|
||||||
|
|
||||||
Doorkeeper::OpenidConnect.configure do
|
Doorkeeper::OpenidConnect.configure do
|
||||||
issuer do |_resource_owner, _application|
|
issuer do |_resource_owner, _application|
|
||||||
"issuer string"
|
"#{Settings.server_protocol}://#{Settings.server_url}"
|
||||||
end
|
end
|
||||||
|
|
||||||
signing_key <<~KEY
|
signing_key Settings.doorkeeper_signing_key
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
....
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
KEY
|
|
||||||
|
|
||||||
subject_types_supported [:public]
|
subject_types_supported [:public]
|
||||||
|
|
||||||
resource_owner_from_access_token do |access_token|
|
resource_owner_from_access_token do |access_token|
|
||||||
# Example implementation:
|
User.find_by(:id => access_token.resource_owner_id)
|
||||||
# User.find_by(id: access_token.resource_owner_id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
auth_time_from_resource_owner do |resource_owner|
|
auth_time_from_resource_owner do |resource_owner|
|
||||||
# Example implementation:
|
# empty block necessary as a workaround to missing configuration
|
||||||
# resource_owner.current_sign_in_at
|
# when no auth_time claim is provided
|
||||||
end
|
end
|
||||||
|
|
||||||
reauthenticate_resource_owner do |resource_owner, return_to|
|
subject do |resource_owner, _application|
|
||||||
# Example implementation:
|
resource_owner.id
|
||||||
# store_location_for resource_owner, return_to
|
|
||||||
# sign_out resource_owner
|
|
||||||
# redirect_to new_user_session_url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Depending on your configuration, a DoubleRenderError could be raised
|
protocol do
|
||||||
# if render/redirect_to is called at some point before this callback is executed.
|
Settings.server_protocol.to_sym
|
||||||
# To avoid the DoubleRenderError, you could add these two lines at the beginning
|
|
||||||
# of this callback: (Reference: https://github.com/rails/rails/issues/25106)
|
|
||||||
# self.response_body = nil
|
|
||||||
# @_response_body = nil
|
|
||||||
select_account_for_resource_owner do |resource_owner, return_to|
|
|
||||||
# Example implementation:
|
|
||||||
# store_location_for resource_owner, return_to
|
|
||||||
# redirect_to account_select_url
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subject do |resource_owner, application|
|
claims do
|
||||||
# Example implementation:
|
claim :preferred_username, :scope => :openid do |resource_owner, _scopes, _access_token|
|
||||||
# resource_owner.id
|
resource_owner.display_name
|
||||||
|
end
|
||||||
|
|
||||||
# or if you need pairwise subject identifier, implement like below:
|
claim :email, :scope => :read_email, :response => [:id_token, :user_info] do |resource_owner, _scopes, _access_token|
|
||||||
# Digest::SHA256.hexdigest("#{resource_owner.id}#{URI.parse(application.redirect_uri).host}#{'your_secret_salt'}")
|
resource_owner.email
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Protocol to use when generating URIs for the discovery endpoint,
|
|
||||||
# for example if you also use HTTPS in development
|
|
||||||
# protocol do
|
|
||||||
# :https
|
|
||||||
# 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
|
end
|
||||||
|
|
|
@ -2549,6 +2549,7 @@ en:
|
||||||
permissions:
|
permissions:
|
||||||
missing: "You have not permitted the application access to this facility"
|
missing: "You have not permitted the application access to this facility"
|
||||||
scopes:
|
scopes:
|
||||||
|
openid: Sign-in using OpenStreetMap
|
||||||
read_prefs: Read user preferences
|
read_prefs: Read user preferences
|
||||||
write_prefs: Modify user preferences
|
write_prefs: Modify user preferences
|
||||||
write_diary: Create diary entries, comments and make friends
|
write_diary: Create diary entries, comments and make friends
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
OpenStreetMap::Application.routes.draw do
|
OpenStreetMap::Application.routes.draw do
|
||||||
use_doorkeeper_openid_connect
|
|
||||||
use_doorkeeper :scope => "oauth2" do
|
use_doorkeeper :scope => "oauth2" do
|
||||||
controllers :authorizations => "oauth2_authorizations",
|
controllers :authorizations => "oauth2_authorizations",
|
||||||
:applications => "oauth2_applications",
|
:applications => "oauth2_applications",
|
||||||
:authorized_applications => "oauth2_authorized_applications"
|
:authorized_applications => "oauth2_authorized_applications"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
use_doorkeeper_openid_connect :scope => "oauth2" if Settings.key?(:doorkeeper_signing_key)
|
||||||
|
|
||||||
# API
|
# API
|
||||||
namespace :api do
|
namespace :api do
|
||||||
get "capabilities" => "capabilities#show" # Deprecated, remove when 0.6 support is removed
|
get "capabilities" => "capabilities#show" # Deprecated, remove when 0.6 support is removed
|
||||||
|
|
|
@ -150,3 +150,8 @@ smtp_password: null
|
||||||
#signup_ip_max_burst:
|
#signup_ip_max_burst:
|
||||||
#signup_email_per_day:
|
#signup_email_per_day:
|
||||||
#signup_email_max_burst:
|
#signup_email_max_burst:
|
||||||
|
# Private key for signing id_tokens
|
||||||
|
#doorkeeper_signing_key: |
|
||||||
|
# -----BEGIN PRIVATE KEY-----
|
||||||
|
# ...
|
||||||
|
# -----END PRIVATE KEY-----
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
module Oauth
|
module Oauth
|
||||||
SCOPES = %w[read_prefs write_prefs write_diary write_api read_gpx write_gpx write_notes].freeze
|
SCOPES = %w[read_prefs write_prefs write_diary write_api read_gpx write_gpx write_notes].freeze
|
||||||
PRIVILEGED_SCOPES = %w[read_email skip_authorization].freeze
|
PRIVILEGED_SCOPES = %w[read_email skip_authorization].freeze
|
||||||
|
OAUTH2_SCOPES = %w[openid].freeze
|
||||||
|
|
||||||
class Scope
|
class Scope
|
||||||
attr_reader :name
|
attr_reader :name
|
||||||
|
@ -14,9 +15,10 @@ module Oauth
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.scopes(privileged: false)
|
def self.scopes(oauth2: false, privileged: false)
|
||||||
scopes = SCOPES
|
scopes = SCOPES
|
||||||
scopes += PRIVILEGED_SCOPES if privileged
|
scopes += PRIVILEGED_SCOPES if privileged
|
||||||
|
scopes += OAUTH2_SCOPES if oauth2
|
||||||
scopes.collect { |s| Scope.new(s) }
|
scopes.collect { |s| Scope.new(s) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue