operations icon indicating copy to clipboard operation
operations copied to clipboard

Add osm.org authentication to Wiki

Open lectrician1 opened this issue 3 years ago • 32 comments

Talk:Wiki discussion

PluggableAuth looks like a suitable extension authenticating users using OAuth from osm.org to the Wiki.

You're probably going to have to read into the specifics on your own since the various options for how users can log in can be a bit confusing.

Here is an example of a Wiki that uses it.

lectrician1 avatar Feb 13 '21 23:02 lectrician1

The users on osm.org and wiki are not 1:1 matched. Any proposal to resolve the discrepancy?

Firefishy avatar Feb 13 '21 23:02 Firefishy

That is basically why the idea has been deemed a non-starter in the past.

tomhughes avatar Feb 13 '21 23:02 tomhughes

The extension matches users that have the same username/email on osm.org and the wiki (not sure if both or just 1).

If users want to "link" their accounts, they're going to have to make sure these are the same.

Fortunately users can change their username and email on the wiki and osm.org, so they get to decide what email/account they want to link.

lectrician1 avatar Feb 13 '21 23:02 lectrician1

So how does it stop me taking over a wiki account by creating an OSM account with the same name?

tomhughes avatar Feb 13 '21 23:02 tomhughes

That's why I think both have to be the same.

lectrician1 avatar Feb 13 '21 23:02 lectrician1

That was my point, if just have the same name is all it takes then I can create an account call JohnBrown on OSM and then login to the wiki as JohnBrown even if there is already an account called that which isn't mine?

tomhughes avatar Feb 13 '21 23:02 tomhughes

Oh no, what I meant is that both the osm.org and wiki email and username have to be the same.

lectrician1 avatar Feb 14 '21 00:02 lectrician1

I wonder if there’s an option for an already logged on wiki user to associate their account with an external identity provider, such as the osm.org website. Of course the user would also have to log on to osm.org to establish that link.

I think user names don’t have to match in that case, as long as a wiki user has an external alias identity. This might also include other trustworthy identity providers.

mmd-osm avatar Feb 14 '21 07:02 mmd-osm

What if I wanted to create a bot account on Wiki - will I have to create an OSM account for it first?

maro-21 avatar Feb 14 '21 11:02 maro-21

What if I wanted to create a bot account on Wiki - will I have to create an OSM account for it first?

No. You can continue to create individual accounts on both osm.org and the Wiki.

lectrician1 avatar Feb 14 '21 15:02 lectrician1

I wonder if there’s an option for an already logged on wiki user to associate their account with an external identity provider, such as the osm.org website.

That's what this extension does.

I think user names don’t have to match in that case, as long as a wiki user has an external alias identity. This might also include other trustworthy identity providers.

What's an external alias identity? Just any account?

lectrician1 avatar Feb 14 '21 15:02 lectrician1

He's talking about a system like osm.org uses, where if you setup login with Google or Facebook or whatever we store the identity of that account so that you can login with it without your email and/or username having to match.

In other words the user has to explicitly associate the accounts and cause a link to be stored rather than an implicit link based on name and email.

tomhughes avatar Feb 14 '21 15:02 tomhughes

What really happens is that a button below the normal "log in" button on the wiki will display with the label "Log in with OpenStreetMap.org"

The user can then click that and the external identification OAuth window (idk entirely how OAuth works) appears.

Whatever the user logins as via that window, the OAuth client returns the username and email of that account to the wiki. If an account with that username and email exists on the Wiki, they are logged-in.

Users can still log in with their normal Wiki username and password in the current input boxes on the login page on the Wiki if they want to.

lectrician1 avatar Feb 14 '21 15:02 lectrician1

So what you're describing here is essentially the same thing I've demoed for Discourse: https://user-images.githubusercontent.com/5842757/96035123-e1a1e700-0e62-11eb-9f05-fa92e8751ad0.gif I've tried the same thing on a test wiki, and it wasn't all that smooth sailing.

Still this doesn't address the issue of associating osm and wiki accounts, when we can't rely on a matching name/email.

mmd-osm avatar Feb 15 '21 08:02 mmd-osm

Well he is suggesting that we do rely on matching and that people can change their names on one or both systems to achieve that is necessary.

I would very much prefer an explicit link though I have to say.

tomhughes avatar Feb 15 '21 08:02 tomhughes

First question i would have is: Does mediawiki support all usernames we have in OSM. OSM has been very relaxed with user naming so there may be pitfalls with non-ascii, utf8-emoji, control character etc names.

flohoff avatar Feb 23 '21 11:02 flohoff

@flohoff : it doesn't matter, we won't be matching by user names anyway. That's the idea of having explicit links.

Example: Wiki user xyz with wiki user id 5678 has an explicit association to their OSM account with uid = 123456 (which corresponds to display_name abc).

mmd-osm avatar Feb 23 '21 11:02 mmd-osm

Wikimedia: For your plan to synergize OSM.org and wiki.OSM.org it would be great to consider Wikimedia-accounts too :-) (Wikipedia, Commons, Wikidata, ... as global Wikimedia user account) This would be a great synergy for both projects!

By script you can ask:

  • New OSM users, if he has a Wikipedia account and would like to use it for OSM and the OSM-Wiki too?
  • Existing OSM users, if he would like to merge his OSM account to the Wikipedia account? or if he prefer to merge his Wikipedia account to the OSM account? (if 2 times "no", he will keep different logins) (if "yes" but the other account is already used by an other user, he can create an new account for OSM and Wikipedia)

Nautic avatar Mar 04 '21 19:03 Nautic

So now that OAuth2 is established and Discourse is almost up and running, it might be the best time look into this issue again.

Mashin6 avatar Oct 21 '21 01:10 Mashin6

Added the "help-wanted" label. I can support any tests anyone wants to run.

Firefishy avatar Aug 04 '22 14:08 Firefishy

Challenges we would likely need to solve to implement this ticket:

  1. osm.org "Display Names" (aka Usernames) are not fixed, we deal with this on community.osm.org by setting the name on community.osm.org on each user login and use the UserID from osm.org as the unique key. The oauth is specially blessed for community.osm.org to be able to access user email address.
  2. Many usernames for wiki.osm.org do not match the same user on osm.org, how do we workaround / resolve this?
  3. How would the process to migrate wiki users to osm.org based logins work? (Technically and practically)
  4. Maintainability going forward, if custom code is used, it needs to be maintained going forward.

Maybe we could have a brainstorming video call sometime to draw out a plan?

Firefishy avatar Aug 05 '22 12:08 Firefishy

You can if you want just allow the names to be different, as help.osm.org does and as osm.org does if you use Google or Facebook to login.

You just store the identity on the remote system as an attribute of the account, and when someone proves control of that identity you let them login to that account.

tomhughes avatar Aug 05 '22 12:08 tomhughes

Last time I looked for some mediawiki plugins, I couldn't find anything similar to the approach we're using for Discourse.

I wonder if some development effort on the Rails port would be acceptable, if that's eliminating the need for a custom wiki plugin. We might check if https://www.mediawiki.org/wiki/Extension:OpenID_Connect could be an option for us.

mmd-osm avatar Aug 05 '22 13:08 mmd-osm

I played a bit with https://github.com/doorkeeper-gem/doorkeeper-openid_connect to enable Openid connect support on the Rails port. Most of the installation worked out of the box (as far as you can call this working). I'm documenting a few changes I did to the config. For sure this needs some more work. I believe this could be used a starting point to enable OpenID Connect mediawiki extension. I haven't tried this out end-to-end yet, though.

Setting up doorkeeper-openid_conect gem

After rails generate doorkeeper:openid_connect:install, I added Rails.application.reloader.to_prepare do to the doorkeeper_openid_connect.rb file, otherwise the migration step would bail out.

SAFETY_ASSURED=1 was used to silence an otherwise unhappy strong_migration.

config/initializers/doorkeeper_openid_connect.rb:

# frozen_string_literal: true

Rails.application.reloader.to_prepare do

Doorkeeper::OpenidConnect.configure do
  issuer do |resource_owner, application|
     'http://localhost:3000'
  end

  signing_key <<~KEY
    -----BEGIN RSA PRIVATE KEY-----
    
[add private key...]

    -----END RSA PRIVATE KEY-----
  KEY

  subject_types_supported [:public]

  resource_owner_from_access_token do |access_token|
    # Example implementation:
    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
  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
  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
  end

  subject do |resource_owner, application|
    # Example implementation:
    resource_owner.id

    # 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'}")
  end
 
  claims do
    claim :preferred_username, scope: :openid do |resource_owner, scopes, access_token|
      # Pass the resource_owner's preferred_username if the application has
      # `profile` scope access. Otherwise, provide a more generic alternative.
      resource_owner.display_name
    end

  end  
  
end

end

I added a new privileged scope 'openid' (translation for en.oauth.scopes.openid should also be added):

diff --git a/lib/oauth.rb b/lib/oauth.rb
index 7ff2ba8b4..1c7b38636 100644
--- a/lib/oauth.rb
+++ b/lib/oauth.rb
@@ -1,6 +1,6 @@
 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
+  PRIVILEGED_SCOPES = %w[read_email skip_authorization openid].freeze
 
   class Scope
     attr_reader :name

Skipping the suggested view generation (rails generate doorkeeper:views) and added the nonces field here instead:

diff --git a/app/views/oauth2_authorizations/new.html.erb b/app/views/oauth2_authorizations/new.html.erb
index 971e0e20a..ac9c7c6c5 100644
--- a/app/views/oauth2_authorizations/new.html.erb
+++ b/app/views/oauth2_authorizations/new.html.erb
@@ -18,6 +18,7 @@
       <%= 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") %>
@@ -30,6 +31,7 @@
       <%= 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") %>

Adjusted routes to point to /oauth2 instead

diff --git a/config/routes.rb b/config/routes.rb
index e45d56701..b1e69dce3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,5 @@
 OpenStreetMap::Application.routes.draw do
+  use_doorkeeper_openid_connect :scope => "oauth2"
   use_doorkeeper :scope => "oauth2" do
     controllers :authorizations => "oauth2_authorizations",
                 :applications => "oauth2_applications",

Result

In the end, I used some demo OpenId connect client, and got the following result on http://localhost:3000/oauth2/userinfo

{
    "sub": "2",
    "preferred_username": "mmd3"
}

http://localhost:3000/.well-known/openid-configuration contents:

{
  "issuer": "http://localhost:3000",
  "authorization_endpoint": "http://localhost:3000/oauth2/authorize",
  "token_endpoint": "http://localhost:3000/oauth2/token",
  "revocation_endpoint": "http://localhost:3000/oauth2/revoke",
  "introspection_endpoint": "http://localhost:3000/oauth2/introspect",
  "userinfo_endpoint": "http://localhost:3000/oauth2/userinfo",
  "jwks_uri": "http://localhost:3000/oauth2/discovery/keys",
  "scopes_supported": [
    "read_prefs",
    "write_prefs",
    "write_diary",
    "write_api",
    "read_gpx",
    "write_gpx",
    "write_notes",
    "read_email",
    "skip_authorization",
    "openid"
  ],
  "response_types_supported": [
    "code"
  ],
  "response_modes_supported": [
    "query",
    "fragment",
    "form_post"
  ],
  "grant_types_supported": [
    "authorization_code"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic",
    "client_secret_post"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "claim_types_supported": [
    "normal"
  ],
  "claims_supported": [
    "iss",
    "sub",
    "aud",
    "exp",
    "iat",
    "preferred_username"
  ]
}

Mediawiki plugin seems to support user migration by email address, although I'm not sure if that's opening up some loophole. Migrating by username makes absolutely no sense.

As the plugin supports both realname, and email, we could add some more claims to the doorkeeper_openid_connect config:

    normal_claim :email, scope: :read_email  do |resource_owner, scopes, access_token|
      resource_owner.email
    end
    
    normal_claim :name, scope: :openid  do |resource_owner, scopes, access_token|
      resource_owner.display_name
    end   

User details will now include those two additional claims as:

{
    "sub": "1",
    "email": "some email address @ xyz . abc",
    "name": "mmd2",
    "preferred_username": "mmd2"
}

I believe this now includes all details the mediawiki extension is asking for.

mmd-osm avatar Aug 07 '22 13:08 mmd-osm

I put together a small video highlighting the end-to-end process. I believe there's a good chance that this might actually work...

🎥 Demo time (screencast)

mmd2 has an already existing wiki user with a matching email address.

wiki_logon


LocalSettings.php additions (demo only!):
$wgPluggableAuth_EnableLocalProperties = true;

$wgPluggableAuth_EnableLocalLogin = true;

$wgPluggableAuth_ButtonLabel = 'Log in via OpenStreetMap';

$wgOpenIDConnect_Config['http://localhost:3000/'] = array( 
        'clientID' => '...',
        'clientsecret' => '...',
        'scope' => [ 'read_prefs', 'openid', 'read_email' ],
        'verifyHost' => false,
        'verifyPeer' => false
);        

$wgOpenIDConnect_MigrateUsersByEmail  = true;

wfLoadExtension( 'PluggableAuth' );
wfLoadExtension( 'OpenIDConnect' );

OAuth2 application - Redirect URIs on Rails port also included http://localhost/mediawiki/index.php?title=Special:PluggableAuthLogin


mediawiki.openid_connect table stores a link between wiki user id, and the uid we use on the rails port:

select * from mediawiki.openid_connect;
 oidc_user | oidc_subject |      oidc_issuer       
-----------+--------------+------------------------
         1 | 1            | http://localhost:3000/
         3 | 2            | http://localhost:3000/
         4 | 3            | http://localhost:3000/
         5 | 6            | http://localhost:3000/
         7 | 7            | http://localhost:3000/
         8 | 8            | http://localhost:3000/
        10 | 9            | http://localhost:3000/

mmd-osm avatar Aug 09 '22 20:08 mmd-osm

There has been much wiki discussion about removing barriers from the wiki recently. I also would like to see a "Log in via openstreetmap.org" on the OSM wiki as soon as possible. Since it seemed like getting the Rails port to support OpenID Connect wasn't going anywhere (no reply here on if that's acceptable and no issue over at the website repo), I looked into getting it to work with the existing OAuth 2.0 API. As it turns out the OAuth 2.0 plugin for PluggableAuth doesn't support any migration mechanism suitable for us. I looked into PluggableAuth but found its documentation, API design and integration into MediaWiki to be lacking, so I ended up writing my own MediaWiki extension: RedirectAuth.

You can try it out at https://demo-wiki.push-f.com/.

Note that unlike the previously suggested OIDC extension my extension does not perform mapping per email addresses but instead lets users link an existing wiki account with their OSM account no matter which email addresses they have configured.

Note that I have already asked the community for feedback for my extension on the talk@ mailing list and the community forum.

not-my-profile avatar Oct 14 '22 08:10 not-my-profile

@not-my-profile : can you maybe write a few words on how you have planned to support your extension going forward? How about translating messages to other languages?

As mentioned on the community site, I had a strong focus to use an officially supported wiki extension and avoid any custom code (see my comment from Aug 5). Due to the extreme shortage of knowledgable MediaWiki folks around here, this seemed like the least risky option, although user experience may not be as good as it could be.

mmd-osm avatar Oct 14 '22 09:10 mmd-osm

I would maintain it in my self-hosted repo and document it on the mediawiki.org wiki (I have not yet done so because I don't want to spend time writing documentation if I don't yet know if the extension will actually be used by the OSM wiki). I'd answer questions about the extension on its talk page at mediawiki.org as well as be available for contact via email for the operations working group. I think for translations I would sign up my extension at translatewiki.net (which is used by MediaWiki itself as well as hundreds of MediaWiki extensions).

not-my-profile avatar Oct 14 '22 09:10 not-my-profile