Possible to check for IdP authentication status and retrieve user attributes?

Created on 16 April 2025, 8 days ago

Problem/Motivation

My organization is in the process of transitioning from simpleSAMLphp Authentication to SAML Authentication and I'm not finding a straightforward way to (1) determine whether a user is authenticated with the IdP, and (2) retrieve user attributes from an active SAML session. Our present use case occurs within a custom middleware but I've also tested from within a custom route controller. And, to be clear, the SAML Authentication module is otherwise functioning as expected, eg, users are successfully authenticated with the IdP and Drupal.

With the available services in the simpleSAMLphp Authentication module, I'm able to use the methods isAuthenticated() and getAttributes(), respectively, to achieve the above. In the absence of something similar to isAuthenticated(), I was hoping I might be able to make a similar determination based on whether the array returned by Drupal\samlauth\SamlService->getAttributes() is populated (and, if so, assumption would be that there's an authenticated session). However, even after a user has successfully authenticated with the IdP and is logged into Drupal, the getAttributes() method always returns an empty array when called from our custom middleware or route controller (both of which are bringing in the SamlService using dependency injection).

Is it possible that the array is empty due to the IdP configuration? Or, am I perhaps misunderstanding at which point the array is actually populated given the current state of the SAML authentication flow? The comments on the service method state, "This method will return valid data after a response is processed," so perhaps calling the method outside of an authentication flow (on, say, a route's page load) correctly returns an empty array?

Many thanks in advance!

πŸ’¬ Support request
Status

Active

Version

3.11

Component

Code

Created by

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @c. s. comfort
  • πŸ‡³πŸ‡±Netherlands roderik Amsterdam,NL / Budapest,HU

    isAuthenticated() does not exist, because it addresses an issue that is specific to SimpleSAMLphp / does not exist with SamlAuth.

    See the two images at https://www.drupal.org/docs/contributed-modules/saml-authentication/usin... β†’ if necessary:

    • with SimpleSAMLphp, SAML auth happens outside of Drupal. Then the user is redirected to Drupal, with a cookie set -- and Drupal still needs to figure out whether the at-that-moment-anonymous user is allowed to be logged in. (using isAuthenticated(), which -from my memory- checks the cookie.)
    • with SamlAuth, SAML auth happens inside Drupal, during the POST request to /saml/acs. And the SamlAuth module immediately logs the user in, during the same POST request. Either the user is logged in, or the user is presented with a login error. There is no other 'vague in-between state' like with SimpleSAMLphp auth.

    Is it possible that the array is empty due to the IdP configuration? Or, am I perhaps misunderstanding at which point the array is actually populated given the current state of the SAML authentication flow?

    getAttributes() only returns data while processing the /saml/acs POST request, not on any other page load -- and only after SAML authentication has already happened and was successful.

    Without knowing the specifics of what you are solving, I am guessing:

    • you're trying to do something during some event very early in the page load -- which is not a concept that works anymore.
      • you should be doing the same thing immediately after successful SAML authentication. There's an event for that: SamlAuthEvents::USER_SYNC. See the comments there. See UserSyncEventSubscriber for an example implementation.

      Note on this event, SAML authentication has already happened and your user ($event->getAccount()) is going to be logged in (except if you or some other event subscriber throws an exception or something horrible happens while saving the user account). So you don't need to do an isAuthenticated() check.

      Or, alternatively, if you want to do something after authentication and Drupal user login has happened, and you cannot cancel it in any way anymore: check the ExternalAuthEvents::LOGIN event. SamlService->getAttributes() should work there always, if you need the attribute values (though the service isn't injected so it's slightly ugly).

      This means you need to do some rewrite work, for this different concept. But the result will probably be (conceptually) simpler, I'm guessing.

  • πŸ‡³πŸ‡±Netherlands roderik Amsterdam,NL / Budapest,HU
  • Hi @roderik, thanks for the thorough replyβ€”and for speaking to some of the inherent differences in the modules' approaches. Our use case is one where we're limiting access to specific paths based on a user's affiliation, retrieved from their attributes provided by the IdP (eg, a "staff" affiliation). We don't, however, map these users to individual Drupal accounts as there's no need (or desire) for the users to be registered locally: if they have the requisite affiliation, the page is returned; and, if not, they're redirected.

    I'll return my attention to the events to see what I can shake out. Thanks again!

  • πŸ‡³πŸ‡±Netherlands roderik Amsterdam,NL / Budapest,HU

    We don't, however, map these users to individual Drupal accounts as there's no need (or desire) for the users to be registered locally

    Oof! That makes things harder. I haven't heard yet, of a use case like this.

    To summarize my previous response: samlauth intrinsically links "Idp authentication" to "Drupal user login". That's easier for the common use case.

    But in your case,

    • you need to peel those things apart again
    • you likely need to store some (extra) status/data in a 'user session' that is separate from a logged-in Drupal user (which simpleSAMLphp did, and which samlauth doesn't do), in order to do this access check on those paths.

    I think it's possible, though, to use just the "SAML authentication" part of this module and to remove the "Drupal user login" part, without making things too complicated. (I'm going to assume, for simplicity, that you never want to have a Drupal user login after someone returns from the IdP.)

    • You'll need to overwrite the samlauth.saml service with a custom service that extends SamlService
    • Your extending class can 'empty out' doLogin() (or change acs(), which is the only caller of doLogin(). But I assume leaving acs() is easier).
    • This is assuming that you don't want anything registered in the authmap table. (Also, I am not going into the horribble piece of spaghetti that is the doLogin() execution path... because you won't need it / will remove it.)
    • Then I advise experimenting with saveSamlSession():
      • There is a concept of a "Saml session", separate from the Drupal login. But ss you can see from the horribly large comments, it's an afterthought / code that has just stuck around since v1.0 and not (to my knowledge) been used well.
      • Decide what data you need, in an access check that you do on the other page loads.
      • Then store that data somewhere you can get to it. (getAttributes() is available here if you need it)
      • As you can see, currently the 'session data' is stored in PrivateTempStore (and you can just add your data next to it). I'm personally not convinced that this is better than just storing this data in $_SESSION -- because as soon as you store something in PrivateTempStore, you automatically/necessarily get a session anyway. I guess it depends on the nature of your data.

    I.... think that works without further complications?

    If you discover things that need to be patched along the way, I'm open to it. Although, until other people come along who want the same thing, I can imagine this is not going to turn into a configurable option to "skip user creation/login", but instead just a note in the README that this can be achieved with the above.

  • πŸ‡³πŸ‡±Netherlands roderik Amsterdam,NL / Budapest,HU

    I'm going to turn this into a reminder for me to, long-term, add something to the README and/or add a config option to skip user login (that might not be exposed in the UI).

    It would be good to know if the current saveSamlSession() code is adequate for implementing such a use case.

  • Again, thanks for taking the time to think this through and share some possible solutions for consideration. The wrinkle is that we do have some users that authenticate with the IdP for Drupal user logins (eg, our admins, content editors), so we can't ditch that portion of the module as you outlined. I'm starting to foresee a path down which we may refactor our approach to handling the attribute-restricted paths in a way that relies on normal Drupal user sessions/entities.

Production build 0.71.5 2024