Bug 24539 - Build generic authentication module interface
Summary: Build generic authentication module interface
Status: RESOLVED WISHLIST
Alias: None
Product: Koha
Classification: Unclassified
Component: Authentication (show other bugs)
Version: master
Hardware: All All
: P5 - low enhancement (vote)
Assignee: David Cook
QA Contact: Testopia
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-01-30 07:34 UTC by David Cook
Modified: 2022-12-06 06:40 UTC (History)
4 users (show)

See Also:
Change sponsored?: ---
Patch complexity: ---
Documentation contact:
Documentation submission:
Text to go in the release notes:
Version(s) released in:


Attachments
Bug 24539: Add hooks to allow pluggable authentication modules (5.69 KB, patch)
2021-05-26 08:23 UTC, David Cook
Details | Diff | Splinter Review
Bug 24539: Add hooks to allow pluggable authentication modules (5.79 KB, patch)
2021-06-02 08:37 UTC, David Cook
Details | Diff | Splinter Review
[DO NOT PUSH] koha-plugin-test-auth Koha Plugin for testing (3.81 KB, application/zip)
2021-06-02 08:53 UTC, David Cook
Details

Note You need to log in before you can comment on or make changes to this bug.
Description David Cook 2020-01-30 07:34:25 UTC
Locally, I run a number of custom authentication integrations for Koha, and I've been thinking it would be great if there were a generic authentication module interface.

So I've refactored my local OpenID Connect implementation, so that the changes to Auth.pm and opac-auth.tt are very generic, and other authentication modules could easily leverage them without touching Auth.pm or opac-auth.tt. (The latter remains translateable as generic messages are in opac-auth.tt and they are bound to keys passed from the authentication module.)

Right now, you invoke a custom endpoint (I have ideas about making those more generic and extensible too) and it passes an object to C4::Auth::checkauth(). The module for that object and some other data are persisted in the logged in user's session.

To log off, you just hit the log off button, Koha gets relevant info from the user's session, and runs the authentication module's logout hook. 

My goal is to make it so that people can rapidly develop authentication modules, and make it easy to swap in and out different modules. (For instance, my local OpenID Connect code works and works reasonable well, but it's not perfect. However, I shouldn't need to touch Auth.pm and opac-auth.tt to improve that code. The improvements should be isolated within the specific authentication module itself.)
Comment 1 David Cook 2020-01-30 07:41:23 UTC
I'm not using the existing Koha plugin system, as I'm still not 100% sure I want to implement these as plugins that end-users can upload... but still thinking.
Comment 2 David Cook 2020-07-08 12:42:48 UTC
(In reply to David Cook from comment #1)
> I'm not using the existing Koha plugin system, as I'm still not 100% sure I
> want to implement these as plugins that end-users can upload... but still
> thinking.

If https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=25672 is pushed, then I think it would be reasonable to do auth plugins as Koha plugins.
Comment 3 mark.jaroski 2021-05-24 12:09:19 UTC
I haven't looked at the code yet, but does this implementation include just-in-time provisioning to allow first-contact federated users to arrive sight-unseen?

How do you normally assign a card number to such users?
Comment 4 mark.jaroski 2021-05-24 12:50:55 UTC
Also, do you have a patch for this?
Comment 5 David Cook 2021-05-24 23:07:32 UTC
(In reply to mark.jaroski from comment #3)
> I haven't looked at the code yet, but does this implementation include
> just-in-time provisioning to allow first-contact federated users to arrive
> sight-unseen?
> 
> How do you normally assign a card number to such users?

(In reply to mark.jaroski from comment #4)
> Also, do you have a patch for this?

The idea here is just to add hooks into key parts of login and logout to run the modular authentication code. 

The actual handling would be done by the specified authentication module. 

I do have code for this although it's just local at the moment. I could look at writing a patch and posting it tomorrow night. I have some plans for changes to authentication and authorization for Bug 28325, so it's all on my mind at the moment.
Comment 6 David Cook 2021-05-24 23:08:35 UTC
Unfortunately, the C4::Auth module scares a lot of people, and no one wants to risk breaking the existing authentication and authorization, so it can be a daunting task to make changes there.

But I'm glad to have you on board.
Comment 7 David Cook 2021-05-25 00:38:09 UTC
(In reply to mark.jaroski from comment #3)
> How do you normally assign a card number to such users?

It depends a bit on what you want. You could map an attribute from the IdP to be the card number, or you could auto-generate a card number. I have some SAML-based systems that have staff ID numbers that end up as card numbers.
Comment 8 David Cook 2021-05-26 08:23:34 UTC
Created attachment 121412 [details] [review]
Bug 24539: Add hooks to allow pluggable authentication modules

This patch adds the ability to specify an authentication module
to C4::Auth::checkauth(). That module then needs to implement at
the very least a do_login() method and optionally do_logout()
and get_data() methods if they're needed for that authentication
style.
Comment 9 David Cook 2021-05-26 08:25:08 UTC
Note that this is a variation of what I'm using locally, and thus it hasn't been fully tested yet. 

The authentication module I use is also provided locally in my Koha. I'm looking at creating a "Koha Plugin" test module for this, but I've run out of time tonight.
Comment 10 mark.jaroski 2021-05-26 09:14:11 UTC
Here's an odd idea from something I've noticed in the code. It looks like there's Shibboleth support in there.

That gives Koha admins the option of configuring authentication at the webserver level with mod_auth_mellon or mod_auth_oidc either of which can be configured to pass Shibboleth-style headers to an application.

I've configured other applications that way, and it works well. Of course, you have to guarantee that requests come to the application from your webserver and nowhere else, but in a single box architecture that's no problem at all.
Comment 11 David Cook 2021-05-27 01:53:16 UTC
(In reply to mark.jaroski from comment #10)
> Here's an odd idea from something I've noticed in the code. It looks like
> there's Shibboleth support in there.
> 
> That gives Koha admins the option of configuring authentication at the
> webserver level with mod_auth_mellon or mod_auth_oidc either of which can be
> configured to pass Shibboleth-style headers to an application.
 
I think some vendors used to use mod_auth_mellon but it fell out of favour for some reason. Now mod_shib is used for the Shibboleth auth.

I haven't looked at mod_auth_oidc, but it looks like it's available in Debian, so that could always be interesting: https://packages.debian.org/buster/libapache2-mod-auth-openidc

> I've configured other applications that way, and it works well. Of course,
> you have to guarantee that requests come to the application from your
> webserver and nowhere else, but in a single box architecture that's no
> problem at all.

Using this patch here, you'd probably be able to use those quite easily.
Comment 12 David Cook 2021-05-27 01:53:51 UTC
I'm not sure when I'll get back to this. My schedule is pretty busy so probably next week.
Comment 13 mark.jaroski 2021-05-27 17:00:41 UTC
My comment here is pretty tangential to the auth framework idea and is more about authentication methods in general.

-------------------------------------------------------------------------------


For what it's worth I think that people weren't happy with mod_auth_mellon mainly because the documentation was limited, and for a long time liblasso didn't support SHA-256 signatures.

Both of those issues are fixed now: liblasso has improved a lot and RedHat provided excellent documentation. So I don't think SAML2 is quite dead yet.

That said most people seem to prefer OAuth2 flows.

The problem there is that OAuth2 was meant for Authorization (AuthZ) as opposed to Authentication (AuthN), so using most of the flows for AuthN is pseudo-authentication, and is technically incorrect, since the IDP *doesn't* certify that the user in the flow is any particular user just that they have the right to grant access to a particular resource.

In the openid scope, and especially in the pure OpenID-Connect flow the IDP *does* certify that that the user is who they say they are.
Comment 14 David Cook 2021-06-02 03:27:02 UTC
I might have another poke at this tonight.
Comment 15 David Cook 2021-06-02 08:37:54 UTC
Created attachment 121535 [details] [review]
Bug 24539: Add hooks to allow pluggable authentication modules

This patch adds the ability to specify an authentication module
to C4::Auth::checkauth(). That module then needs to implement at
the very least a do_login() method and optionally do_logout()
and get_data() methods if they're needed for that authentication
style.
Comment 16 David Cook 2021-06-02 08:46:41 UTC
To test (using a test auth plugin):
1. Download and install Koha Plugin koha-plugin-test-auth
2. Upload plugin via http://localhost:8081/cgi-bin/koha/plugins/plugins-home.pl
3. Enable the plugin
4. Go to http://localhost:8081/cgi-bin/koha/admin/patron-attr-types.pl
5. Create a new patron attribute with the code "TestAuthT" (you should make the description "TestAuthMagicLogin" but it doesn't matter, and you should make it a Unique identifier, so that a token can only belong to one patron)
6. Go to http://localhost:8081/cgi-bin/koha/members/memberentry.pl?op=modify&borrowernumber=51&step=4
7. Add "1234" to TestAuthMagicLogin attribute
8. Log out of Koha
9. koha-plack --restart kohadev 
#This loads the plugin route
10. Go to http://localhost:8081/api/v1/contrib/testauth/authn?token=1234
11. Note that you've been logged in as the "koha" user
12. Check /var/log/koha/kohadev/plack-api-error.log, and note warnings like the following:

[2021/06/02 08:43:47] [WARN] Koha::Plugin::Dcook::TestAuth::AuthN::Plugin do_login at /var/lib/koha/kohadev/plugins/Koha/Plugin/Dcook/TestAuth/AuthN/Plugin.pm line 17.
[2021/06/02 08:43:47] [WARN] Koha::Plugin::Dcook::TestAuth::AuthN::Plugin get_data at /var/lib/koha/kohadev/plugins/Koha/Plugin/Dcook/TestAuth/AuthN/Plugin.pm line 60.
[2021/06/02 08:43:47] [WARN] $VAR1 = {
          'TestAuthKey' => 'TestAuthValue'
        };

13. Log out of the staff interface
14. Note that you've been logged out
15. Check /var/log/koha/kohadev/plack-intranet-error.log, and note warnings like the following:

[2021/06/02 08:44:44] [WARN] Koha::Plugin::Dcook::TestAuth::AuthN::Plugin do_logout at /var/lib/koha/kohadev/plugins/Koha/Plugin/Dcook/TestAuth/AuthN/Plugin.pm line 52.
[2021/06/02 08:44:44] [WARN] $VAR1 = {
          'TestAuthKey' => 'TestAuthValue'
        };

16. Go to http://localhost:8080/api/v1/contrib/testauth/authn?token=1234
17. Note that you've been logged into the OPAC
18. Log out of the OPAC
19. Note the warnings in /var/log/koha/kohadev/plack-api-error.log and /var/log/koha/kohadev/plack-opac-error.log
Comment 17 David Cook 2021-06-02 08:53:53 UTC
Created attachment 121536 [details]
[DO NOT PUSH] koha-plugin-test-auth Koha Plugin for testing

This is a Koha Plugin that I slapped together to prove that the generic authentication module interface works. 

Perhaps next week I can put together an OpenID Connect module. It might be tricky to do as a Koha Plugin, since checkauth() is based on CGI.pm and that doesn't play nicely with Mojolicious... although maybe $c will be in scope. 

That's for another night though...
Comment 18 David Cook 2021-06-02 09:00:40 UTC
(In reply to David Cook from comment #17)
> Perhaps next week I can put together an OpenID Connect module. It might be
> tricky to do as a Koha Plugin, since checkauth() is based on CGI.pm and that
> doesn't play nicely with Mojolicious... although maybe $c will be in scope. 

Darn. I just did a little test, and $c is not in scope for do_login when called within checkauth() from a Mojolicious controller. 

I do have another idea which is so hacky. In the koha-plugin-test-auth, I redirect STDOUT and STDERR to /dev/null, but I could actually redirect STDOUT to a scalar variable, and then I could parse the output and translate it to Mojolicious... 

Rather than hacking apart Koha's existing functions, it might be a better idea to refactor checkauth() so that it's comprised of separate login and logout functions, which can be called in a more environmentally aware way. 

Anyway, definitely calling it a night...
Comment 19 David Cook 2021-06-02 09:05:11 UTC
(In reply to David Cook from comment #18)
> (In reply to David Cook from comment #17)
> > Perhaps next week I can put together an OpenID Connect module. It might be
> > tricky to do as a Koha Plugin, since checkauth() is based on CGI.pm and that
> > doesn't play nicely with Mojolicious... although maybe $c will be in scope. 
> 
> Darn. I just did a little test, and $c is not in scope for do_login when
> called within checkauth() from a Mojolicious controller. 
> 
> I do have another idea which is so hacky. In the koha-plugin-test-auth, I
> redirect STDOUT and STDERR to /dev/null, but I could actually redirect
> STDOUT to a scalar variable, and then I could parse the output and translate
> it to Mojolicious... 
> 
> Rather than hacking apart Koha's existing functions, it might be a better
> idea to refactor checkauth() so that it's comprised of separate login and
> logout functions, which can be called in a more environmentally aware way. 
> 
> Anyway, definitely calling it a night...

Actually, the better way to do it would be to focus on the concept of authenticated sessions. You could have a Koha Plugin perform a login however it wants, and it just ends up with a session. It then redirects or whatever and sets the session cookie. Then it's all good.
Comment 20 David Cook 2021-06-02 09:09:46 UTC
Note that this generic authentication module interface doesn't have to be done using Koha Plugins. 

We can provide these authentication modules in the Koha core codebase. Then we'd just need to write the controller code (in CGI style for the time being) that leverages the particular authentication module.
Comment 21 David Cook 2021-06-03 00:57:51 UTC
The more I think about this... the more I still like it. 

A Koha Plugin could create a session independently without checkauth(), and then could still leverage the logout hook.

I'm still thinking a minimum session created via a Koha::Session object is probably a good idea though...
Comment 22 mark.jaroski 2021-06-03 07:39:02 UTC
I don't know enough about Koha to comment on internal workings, but wouldn't you always want a session in most authentication situations?

I guess in the Shibboleth scenario you don't need a session because every request comes with the server variable or header, but for things like OpenID-Connect you don't want to do the handshake for every page load.

Again, maybe I don't understand how Koha handles sessions.
Comment 23 David Cook 2021-06-03 23:16:56 UTC
(In reply to mark.jaroski from comment #22)
> I don't know enough about Koha to comment on internal workings, but wouldn't
> you always want a session in most authentication situations?

Yep.

> I guess in the Shibboleth scenario you don't need a session because every
> request comes with the server variable or header, but for things like
> OpenID-Connect you don't want to do the handshake for every page load.

I haven't used the built-in Koha Shibboleth integration. I've implemented my own custom Koha Shibboleth integration, so I can't comment on how Koha does it by default. But I just do a once off call to the SAML IdP, create a Koha session, and then use that from there on out.
Comment 24 David Cook 2022-12-06 06:40:58 UTC
I'm unlikely to continue working on this.

We've got the generic OIDC/OAuth client now. If I do bump into a scenario where I need to handle a different AuthN situation, then I'll revisit. 

Although really all a person needs to do is create a new Koha session, send back the CGISESSID cookie, and you're golden from there...