Forum OpenACS Development: RFC: External Authentication

Posted by Lars Pind on
Please review the specification for external authentication here:

I'm excited to hear your feedback.


Posted by Lars Pind on
Still really excited, if perhaps a bit bored :)


Posted by Malte Sussdorff on
Hi Lars, you asked for it.

Your proposal sounds good. Two notes:
Jabber already has the who's online function and if I'm not mistaken it is already uploaded to OpenACS.
How about adding the possibility for password changing drivers (using a secure connection, the OpenACS system has the capabilities through it's interface to change the password at the remote domain).

Curious as I am, how much of this is already covered by funding, what parts will be missing and when are you aiming to deliver.

Posted by Bernt Pettersen on
First: It looks really nice, Lars.

A few comments:

The terminology "domain" is ok. (User is related to a certain group/domain - i.e. "student", "staff", "local")

I guess the domain contains a "full name" in addition to pretty-name. This could be used to generate "full-username"
from "username" + "domain full-name" - like the example in "Login Without Explicit Domain"
This would also conform with i.e. IMP webmail, where the
"domains" are the different imap-servers.

I'll continue read the spec and perhaps give some more comments...


Posted by Oscar Bonilla on
In the spec:

Each domain driver will have a set of configuration options dependent on the driver, such as host, port, etc. We will need to  find a mechanism for the driver to tell us which configuration  options are available, a way to set these, and a way for the driver  to access these settings.

How about each driver creates a table with the columns it needs and we just let the admin fill the information on a row of the table? The UI would be easy to generate automatically if we know the table name.

Optional: Password policy

How would we handle password policies of the external authentication service? What if the external service returns "password has expired, must change"? We should log in the user but ask him to change password.

$$$$ Also need a way for the driver to say which configuration options it has, and a user interface for setting them. Sounds very much like current package parameters.

Would it be possible to have multiple configurations for the same driver? What if I have two LDAP servers and want to authenticate with both?
This is why I think a table for each driver with a column for each parameter and a row for each instance of the driver would work best.

This also leads to the "stackable" feature. Would it be possible to stack the drivers so that it's required for the user to authenticate with all of them? or maybe any of them? We could do this using PAM but PAM doesn't support all the features the ext-auth module will support.

Lars asked me if I had a *real* need for this. The answer is no. I just tought it would be something we should consider from the begining.

Operating system (Linux/Solaris) PAM: Delegate to the operating system, which can then talk to RADIUS, LDAP, whatever. This is convenient because there'll be plenty of drivers for the OS PAM level, so we don't have to write them all ourselves. The downside is that we can't do things like account creation, password management, real-time account synchronization, etc., not supported by PAM (I'm not entirely sure what is and is not supported).

This would also mean writing an ns_pam module to talk to the PAM libraries. I don't think PAM has account creation, but it definitely has password management.

I'll send more toughts later...



Posted by Lars Pind on
Thanks for the feedback.

As for who's online, how is this implemented using Jabber, and does it require Jabber to work? What I had in mind was something simple which just keeps track of what users have requested pages in the past x seconds (typically around 5 minutes), and so must be assumed to be online. Then that could integrate with a real-time messaging service, so that if you click that person's name, a chat session will start.

Changing passwords: This is part of the 'standard' authentication driver. Did you miss it, or are you suggesting that we split it up from the authentication driver and make it it a separate service contract?

As for funding, we're looking into how much we can get done for the money that are currently on the table.

Oscar's comments have already been incorporated into the spec.


Posted by Malte Sussdorff on
There should be a standalone "who is online" patch somewhere burried in the bug-tracker. Ask Björn Kiesbye about it. It does exactly what you envisioned.

Most likely I missed it, but I did not see how you could change the password on an LDAP server using the OpenACS webfrontend. So no suggestion for splitting it up.

My reason for asking concerning time and what is funded: We need to get LDAP support ready til June. So if you external authentification is ready and supports LDAP as an external source, great. If LDAP as an external source is missing, we can build it. If all is missing, we are applying the same patch we did for ACS 3.5.

Have you thought about linking to other sites with an automatic setting of the authentification cookies of the remote site? Though it does require some development on both systems, it is useful especially if you think about Single Sign On. If interested I can ask Denis to post a working solution (theoretically, as ACS 3.5 code probably doesn't do you good).

Posted by Andrew Grumet on
Hi Lars,

Looking excellent. Thoughts and suggestions below. I apologize in advance for the length 😊

Terminology: I think Authority is much more descriptive than Domain. When a person logs in, they are saying, "I am Telly Monster". OpenACS asks them to prove it. When the driver forwards the username and password, they are forwarding it to a system that is authoritative about who is Telly Monster and who isn't.

Login screen: I see the benefits of the way you did it. Namely, everything is explicit and there's no need for guesswork. The user logs in with their username and, in theory, need not have an email address (some customers seem to care about this). Here's an alternative:

  What is your e-mail address? (text box)
  Have you logged in to Sesame Street Intranet before?
    No, I am a new user.
    Yes, my password is: (text box)

For new users, we link them to a form where they can specify their authority (using friendly prompts such as "I have an MIT login" instead of "") and input their username. Once they've authenticated, we can try to pull their profile information, ask them to verify it, and create the new local account. From then on, the user logs in with their email address, which gets translated to a username on the backend before calling the authenticator.

This should solve the problem that you mentioned about existing accounts with the same email address. The only real down side here is that the "username != email" adherents will be unhappy because we require an email address.

Account creation and password management: I wouldn't go too nutty with these in the early stages. It may actually be desirable, as a matter of policy, to forward the user on to a central system, just to be sure that they understand that their changes will propagate beyond the OpenACS instance they happen to be working in.

Login modes: the problem with a function call like this:

auth::authenticate -username -domain -password -secure:boolean
is that it assumes that you will always use a username and password. But we also know that other methods exist, such as the redirection-based systems (CAS and Passport) and client certificates. Hence the ad-hoc "login using CAS" button on your form. Still, this function is useful for password-based login and so should sit on any login page presented by the system.

To handle the more general case, in an ideal world we would replace the calls like this

set user_id [ad_conn user_id]
and this
with a single, service-contract-backed call that looks like this
set user_id [auth::get_user_id -require_valid_user:boolean]
Based on a configurable policy, this function could choose between using an OpenACS cookie, a client certificate, redirecting to a local sign-in page over HTTP or HTTPS, or redirecting to a central (e.g. CAS) sign-in page.

The down side, of course, is that making a change like this would require touching virtually every page in OpenACS. Ouch, probably not a weekend project. Still, this is my current thinking on the issue and I thought it was at least worth mentioning.

Security: There's something broken about having a middleman (e.g. OpenACS) between the user and the authority. Consider the Kerberos example, where we send our username and password over (hopefully) HTTPS and let OpenACS handle the actual Kerberos authentication. From a security pov, what we've done is to assert that a) HTTPS is an acceptable stand-in for Kerberos, and b) we trust the operators of the OpenACS instance to require HTTPS and implement it correctly.

I find this problematic but don't yet have the solution sketched out. I've written some code for another system that checks for HTTPS and tries to discourage the user from entering remotely-checked passwords if HTTPS is not present. What we probably need, long term, is a vocabulary for talking about trust levels, and a way for the various drivers to advertise their requirements (i.e. "I only accept passwords transmitted over 128-bit HTTPS or better").

Posted by Lars Pind on

Thanks a bunch for your feedback.

Re authority. Okay. I actually favor authority as well. Should we have a vote? :)

Login screen: That's Amazon's login page, which I like a lot. We could make it an option. I've heard the requirement for email-less logins frequently enough that I think it would be good to honor that. Also, in a heterogenous environment where all the other systems ask for username, that'll be easier for people to understand. But we could surely make it an option.

Regarding the "auth::authenticate -username -domain -password -secure:boolean" vs. "set user_id [auth::get_user_id -require_valid_user:boolean]": My scheme does the same thing, but without touching the every page in the system :)

Your function:

set user_id [auth::get_user_id -require_valid_user:boolean]
corresponds precisely to
set user_id [ad_conn user_id]
In my section on single-sign-on, I actually suggested the auth::require_login could redirect to a CAS server instead of to the local /register pages. Doesn't that solve that problem?

What I don't understand about your auth::get_user_id function is that it looks like it will redirect the user's browser to a login page, and conduct that whole page flow, all while the code calling this function is on hold, waiting for its return value, which it then gets when the user is done with the login process. Now, we all know that isn't possible. In reality, the function would send a redirect to the browser, then throw an error, which will cause the thread to stop executing. Then after the user continued the entire login sequence, our page would get requested by the browser again, and the auth::get_user_id would get called from the top again. At least the good old "ad_maybe_redirect_for_registration" makes it obviously clear what is really going on.

Yes, we can definitely make the Authentication API itself a service contract, for which you can supply different implementations, and I think it's a good idea to do so. But I've decided on an approach with more framework to make it easier to use without hand-coding being necessary. And that may push that layer of indirection off to a later date. Unless, of course, someone requires this feature today, in which case I'll be happy to hear from them :)

Security: That's the way it is. If you want something more secure, there's nothing stopping you from using CAS, no?


Posted by Andrew Grumet on
Correspondence of functions: yes, I agree that your functions are equivalent.  And yes, the function I proposed would work roughly like ad_maybe_redirect_for_registration.  There would be subtle differences if we authenticated the person by their client certificate, and the process of redirecting would be programmable so that we could send them off to CAS or a subsite-specific URL.  And yes, you guessed the program flow, sorry if I glossed over that (side note: *everything* in ASP.NET works this way, so it has a way of creeping into one's thinking).

Security: no arguments; I worry about giving people so much rope to hang themselves with, but better to build an imperfect system than no system at all.

Posted by Jon Griffin on
On the email-less login, I have done this and it wasn't hard but it wasn't trivial either.

Is your plan to not require email to use OACS? That is the only way to go forward as many individuals share email addresses and many don't like to use them. It will require data model changes (parties requires email). The good news is PG 7.3 allows drop constraints and columns etc. IIRC.

Posted by Benjamin Bytheway on
I am also very interested in email-less login, or at least a site that is much more anonymous than the currently OACS provides out of the box.  I would love to see a parameter in the the kernel (or maybe acs-subsite) that allowed you to toggle between real names and screen names. This would impact a lot of different packages, so it wouldn't be a trivial change.  I've also thought of putting together a package that would allow users to send notes, kind of like emails, back and forth to each other through the context of the site itself.  That way, the site could protect the privacy of the users by not giving out emails & real names at all, but still allowing communication.

I'm actually in the beginning stages of developing a site that requires this sort of approach, so I'm willing to throw my hat in to help with the grunt work.

Posted by Jon Griffin on
Here are some notes from another site I did. It was ACS classic 4.
Posted by Lars Pind on

Just to make sure I'm not missing something:

In my proposed scheme, the only interaction normal pages should have with the Authentication API would be "auth::require_login", "ad_conn user_id", and perhaps "ad_conn untrusted_user_id".

You say subtle differences ... I just want to make sure that we're on the same page here: If we implemented "auth::require_login" with a service contract, and thus let you plug in a different implementation, our two designs would be completely equivalent in what they could accomplish, no?

If not, I'm missing something. Please help me :)


Posted by Andrew Grumet on
Lars, you are correct.  When I said subtle differences, I meant subtle differences between the way ad_maybe_redirect_for_registration works now (check cookie and redirect) and the way a more general version of it might work (e.g. check cookie; check for client cert; redirect).
Posted by Michael Hebgen on
Hi Lars,

here are some comments on the External Auth Specs.

a) Design Goals

  The design schould be on the one hand open to add other
  authentification mechanisms when needed and on the
  other hand very modular to enable a start with minimal
  requirements (implementations) as soon as possible.

b) Diagram

  The Authentication Driver API can

  1) either connect to PAM (portable authentication module)
      of the underlying supported Unix-Systems (Linux,
      Solaris) and then PAM can be configured to connect to
      a RADIUS server or via LDAP to a Kerberos Server, an
      Active Directory e.g. - an example of this is the
      OpenSource SSH-Daemon.

  2) or connect to "it´s own" RADIUS or LDAP driver for
      authentication checking. The creation of these drivers
      is subject to a service contract implementation.

  For the 1st implementation I suggest to use the PAM
  services of the Unix-System, thus using the existing
  PAM-RADIUS or PAM-LDAP modules. We would like to start
  with PAM-RADIUS and to migrate to PAM-LDAP against an
  Active Directory later.

c) Configuration

  The domain driver for the "local" domain (the existing
  OpenACS registration using email address and password)
  MUST be integral part of the implementation and NOT
  subject to another service contract.

d) Synchronizing and Linking Users

  An Email address belongs either to a remote domain or
  to the "local" domain. Any conflict with an existing
  Email address - and we can foresee some - should be
  handled with an error message and administrative
  intervention reuqired and NOT with program code.

e) Account creation

  Out of our needs a batch job to run each night for
  creation and/or update of the OpenACS/dotLRN accounts
  would be sufficient to start with.

  Dynamic creation needs much more thoughts and other
  authentication services - a RADIUS or an Active Directory
  Server does not handle Email addresses and can therefore
  not return this information.

f) Login levels

  I do not see or understand the needs for an "untrusted
  login", can you please explain it´s purpose - what do we
  need it for?

  Secure login: Good idea, but what do you mean with secure
  connection? The usage of VPN or other secure connections
  is out of OpenACS and therefore we can not check it. Or do
  you mean the whole OpenACS access is handled using https?

g) Options

  Some options like

  - the fallback-option (Single-Sign-On)
  - email any password change (Email account owner on
    password change)
  - login without explicit domain
  - who´s online

  are fine with me and should be implemented in the 1st
  service contract implementation

h) Password hash

  For the "local" domain it is accepted to encrypt the
  password and to keep the encrypted password. Once a
  "local" user logs in, hes/her cleartext password is
  again encrypted and the result will be compared with
  the stored one.

  For the remote domains this does not make any sense. The
  different authentication services are using different
  encryption methods (and we do not want to implement all
  these methods) and the drivers (PAM-RADIUS or PAM-(S)LDAP)
  are transmitting the cleartext password for validation
  checking - over secured connections to the auth servers.

i) Implementation list

  Suggestion: Can we create and add an implementation list
  like a menu so the service contractors can select the
  items they need and want to get implemented?

  The list itself may look like:

  1) Authentication driver

      a) via Unix (Linux, Solaris) PAM drivers for RADIUS
        and LDAP access
      b) via new OpenACS RADIUS and LDAP drivers

  2) Account creation and update

      a) through a batch job at regular intervals
      b) dynamic at login request


  n) Options

      a) fallback to OpenACS accounts
      b) Email notification on password changes
      c) login without explicit domain
      d) who´s online

Regards, Michael

Posted by Oscar Bonilla on
Regarding PAM...

Let's not forget that PAM deals with authentication and *only* authentication. There's no way to find out the user's names, or any information whatsoever about the user via PAM. That's what the name service switch (/etc/nsswitch.conf) is for.

In the OS world, you can configure different sources for authentication (PAM) and account information (NSS). I think we should also separate authentication from information retrieval for account creation. What if I want my users to authenticate using LDAP but want to get their information from another source (like another database)?

Just a thought...



Posted by Lars Pind on

Thanks for your feedback. It's been incorporated into the doc.

A few comments:

c) Configuration

The term "service contract" is a technical OpenACS term, which corresponds to Java's "interface". It's a way of specifying an API between two software components, which lets you supply different implementations of the same API, the same interface.

As opposed to a legal "service contract" or "support contract" in which a technology company promises to uphold certain levels of service to the client.

The ext-auth doc is talking about the former type of service contract, the technical one, only.

I think a service contract (also called 'acs-service-contract') is the right approach to implementing the local account as well.

h) Password hash

This is how things work currently. I haven't mentioned this, since we're not planning on changing it.

i) Implementation list

Will do.

Thanks again.


Posted by Joel Aufrecht on
Some feedback from Collaboraid design reviews, which I'm detailing here because a few may be open community issues. If items don't make sense, check the spec at the top of the forum, and bear with us as we keep improving our documentation process.
  • We are going to assume that auth::authenticate, which is the new function responsible for authenticating any login (local or remote), does all of the error handling for unreliable drivers. That is, even if the remote authentication server croaks, auth:authenticate will not fail. This means that we should try to incorporate a timeout mechanism into auth::authenticate, even if the drivers also have timeouts. (affects Peter, EXT-AUTH-01)
  • Auth::authenticate must always return an auth_message if auth_status is not ok. (affects Peter, EXT-AUTH-01)
  • auth::register needs to detect if it was called by a new user or by an admin (presumably, if the connection has a user id). If called by a registering new user, it should set a cookie for the new user_id. If called by an admin, it should not. (affects Peter, EXT-AUTH-01)
  • acs-subsite.EmailForgottenPasswordP should be checked in concert with the appropriate authentication driver. Only if both are true should a link be offered to email a forgotten password (affects Lars, EXT-AUTH-05)
Posted by Lars Pind on

Can you (or others) tell us more about the details of loading user information from a remote system in batch?

We're looking at the IMS Enterprise v 1.1 specification:

You can see our current detailed design here:

Is this what's needed?

How are you doing this at Galileo?

One thing the IMS spec does not talk about it how to get the XML document in the first place. SMB, FTP, HTTP, SOAP ...

Any input on how we should do this?

Does anybody know how Blackboard or any of the other giants do this?

We could really use some feedback from the "real world" on this.


Posted by Oscar Bonilla on
<blockquote> Can you (or others) tell us more about the details of loading user
information from a remote system in batch?
How are you doing this at Galileo?

Right now we just configured our AOLServer to connect to two databases. The GES database and the University's database - we're lucky that both run Oracle. We have a scheduled tcl procedure that digs in both databases, figures out what changed and updates the GES database accordingly.

We've also built a lot of administrative pages that let you figure out if a particular user is out of sync. Check what happened during the last sync, etc. There's one particularly useful page that lets you merge two users. The cool thing about this page is that, unlike the original merge page which would break whenever a module added a FK to the users table, it looks at the metadata that Oracle stores and figures out from which tables it has to move/delete info in order to merge the users. So even if other programmers add tables and modules that use the users table the merge page still works. I can give you the source code if you want it... altough it only works in Oracle... I don't know enough PostgreSQL to know if it's even possible there...

<blockquote> Is this what's needed? (* Referring to IMS *)

I don't know! I'll read the standard and come back with more comments.



22: Status Report (response to 1)
Posted by Joel Aufrecht on
Mat Kovachs has delivered alpha code for ns_pam. There is an open question of whether or not he should also build ns_extauth, which would have the same functions but use an external helper program. This is necessary if we want to use ns_pam functions that require root, such as local passwords in /etc/shadow. It may not be necessary for LDAP and RADIUS, in which case it is outside the scope of the funded project but still useful and perhaps worth funding - or finding a volunteer.

We have code-reviewed and tested the new login/register and password recovery. It is checked in and running in HEAD and on our test servers. We are working on the last parts, batch sync and authority management

The major progress last week was to get the test servers up and running. The test servers drop databases, check out the latest code, and rebuild every night. This is very important for testing, and especially for moving forward towards 5.0.

We are expecting to finish phase I fairly close to the original budget for the whole menu, so there is fairly high risk that we will exhaust the budget before completing Phase II. Phase II currently includes LDAP, https, on-demand sync, untrusted login, cookie handling rewrite, and remote account creation, all Priority A, and password policy, priority B.

Posted by Todd Gillespie on
Is there a new URL for this spec?  The above no longer works.
Posted by Carl Robert Blesius on
URL is correct. Seems to be a server/network issue.

FYI: Most of the specification has been implemented and is in the OpenACS CVS if you want to poke around (there should be some documentation in there too).