... 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"
<%= f.form_group :confidential do %>
<%= f.check_box :confidential %>
<% 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.hidden_field :state, :value => @pre_auth.state %>
<%= f.hidden_field :response_type, :value => @pre_auth.response_type %>
<%= 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_method, :value => @pre_auth.code_challenge_method %>
<%= f.primary t(".authorize") %>
<%= f.hidden_field :state, :value => @pre_auth.state %>
<%= f.hidden_field :response_type, :value => @pre_auth.response_type %>
<%= 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_method, :value => @pre_auth.code_challenge_method %>
<%= f.submit t(".deny") %>
# https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
# 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.
# By default, all the scopes will be available for all the grant types.
Doorkeeper::OpenidConnect.configure do
issuer do |_resource_owner, _application|
- "issuer string"
+ "#{Settings.server_protocol}://#{Settings.server_url}"
end
- signing_key <<~KEY
- -----BEGIN RSA PRIVATE KEY-----
- ....
- -----END RSA PRIVATE KEY-----
- KEY
+ signing_key Settings.doorkeeper_signing_key
subject_types_supported [:public]
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
auth_time_from_resource_owner do |resource_owner|
- # Example implementation:
- # resource_owner.current_sign_in_at
+ # empty block necessary as a workaround to missing configuration
+ # when no auth_time claim is provided
end
- reauthenticate_resource_owner do |resource_owner, return_to|
- # Example implementation:
- # store_location_for resource_owner, return_to
- # sign_out resource_owner
- # redirect_to new_user_session_url
+ subject do |resource_owner, _application|
+ resource_owner.id
end
- # Depending on your configuration, a DoubleRenderError could be raised
- # if render/redirect_to is called at some point before this callback is executed.
- # 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
+ protocol do
+ Settings.server_protocol.to_sym
end
- subject do |resource_owner, application|
- # Example implementation:
- # resource_owner.id
+ claims do
+ claim :preferred_username, :scope => :openid do |resource_owner, _scopes, _access_token|
+ resource_owner.display_name
+ end
- # or if you need pairwise subject identifier, implement like below:
- # Digest::SHA256.hexdigest("#{resource_owner.id}#{URI.parse(application.redirect_uri).host}#{'your_secret_salt'}")
+ claim :email, :scope => :read_email, :response => [:id_token, :user_info] do |resource_owner, _scopes, _access_token|
+ resource_owner.email
+ 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
permissions:
missing: "You have not permitted the application access to this facility"
scopes:
+ openid: Sign-in using OpenStreetMap
read_prefs: Read user preferences
write_prefs: Modify user preferences
write_diary: Create diary entries, comments and make friends
OpenStreetMap::Application.routes.draw do
- use_doorkeeper_openid_connect
use_doorkeeper :scope => "oauth2" do
controllers :authorizations => "oauth2_authorizations",
:applications => "oauth2_applications",
:authorized_applications => "oauth2_authorized_applications"
end
+ use_doorkeeper_openid_connect :scope => "oauth2" if Settings.key?(:doorkeeper_signing_key)
+
# API
namespace :api do
get "capabilities" => "capabilities#show" # Deprecated, remove when 0.6 support is removed
#signup_ip_max_burst:
#signup_email_per_day:
#signup_email_max_burst:
+# Private key for signing id_tokens
+#doorkeeper_signing_key: |
+# -----BEGIN PRIVATE KEY-----
+# ...
+# -----END PRIVATE KEY-----
module Oauth
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
+ OAUTH2_SCOPES = %w[openid].freeze
class Scope
attr_reader :name
end
end
- def self.scopes(privileged: false)
+ def self.scopes(oauth2: false, privileged: false)
scopes = SCOPES
scopes += PRIVILEGED_SCOPES if privileged
+ scopes += OAUTH2_SCOPES if oauth2
scopes.collect { |s| Scope.new(s) }
end
end