zopyx
(Andreas Jung)
July 10, 2023, 6:16pm
1
Question regarding PAS: my GSOC student works on WebAuthn for PAS. We have a custom login page that sends the attached credentials through a POST request to the Plone/login
endpoint. The PAS plugin is configured as authenticator plugin. However, we don't see the data from the POST request neither in self.request
of your PAS plugin nor in the parameters of the authenticateCredentials
call. If we authenticate a user through username/password (standard Plone login process), we see the related form data from Plone login form. Is there anything in PAS filtering or preventing a POST to /login
to be passed down to the chain of configured authenticator plugins?
davisagli
(David Glick)
July 11, 2023, 1:04am
2
Not sure, but you could step through the logic that uses the plugins in https://github.com/zopefoundation/Products.PluggableAuthService/blob/master/src/Products/PluggableAuthService/PluggableAuthService.py#L532 to find out.
You probably need to also register an extraction plugin.
djay
(Dylan Jay)
July 11, 2023, 1:58am
3
I think restapi breaks PAS in many ways. for example you can no longer pass back redirects or messages to display the user.
This seems to be a similar problem to what you are having.
opened 11:32AM - 31 Jul 18 UTC
Possibly related: issues #178 and #522, or other [issues involving PAS](https://… github.com/plone/plone.restapi/issues?q=is%3Aissue+is%3Aopen+PAS).
For a client I have a PAS plugin that authenticates to a service outside of Plone. It's not LDAP, but LDAP would be a good approximation. The plugin works fine in standard Plone (5.1). But it fails with the `@login` endpoint: this endpoint takes the login name and looks for a user with this name. This currently only works when the user is found by a user enumerator (`@login` calls `_verifyUser`, which calls `enumerateUsers`). My plugin does not do user enumeration, because it only does extraction, authentication and properties for the current user. This may be true for an LDAP plugin as well, depending on how you have set it up.
My solution was:
- Copy the code that gets the login and password (`json_body(request)`) from `@login` to my extraction plugin.
- Override the `@login` endpoint to basically only call `plone.api.user.get_current()` and create a token for that user (if the user is not anonymous). Note that the whole PAS machinery has been called before entering the `@login` code.
This works for my use case.
Would this be a valid approach to use in `plone.restapi` itself? So register the JWT plugin as an extraction plugin as well, and check for an authenticated user in the `@login` endpoint.
Good:
- It works seamlessly with any other PAS plugin, regardless of whether it has support for user enumeration or not, for example a plugin reading SAML headers from the request.
- Issue #522 would be solved automatically, I expect.
- You are really logged into Plone, not just according to `plone.restapi` but also according to `acl_users`. (But maybe this is already the case.)
Bad:
- `json_body(request)` will get called on *all* requests, instead of only when calling `@login`, so might give a small performance drop. But this is true for any extraction plugin. We could add some checks to return quickly, but I guess the `json_body` call is quite fast.
- Might open a can of worms, where people are used to the way `plone.restapi` and `@login` are currently functioning.
zopyx
(Andreas Jung)
July 11, 2023, 5:09am
4
Yes, this was a useful hint.
Taking the form data/JSON data from request.body
from within the extraction plugin will work.
The data returned from extractCredentials()
will show up as credentials
parameter in our authenticateCredentials()
method.