Forums de discussion

Authenticator interface problem

aclement aclement, modifié il y a 16 années.

Authenticator interface problem

Junior Member Publications: 34 Date d'inscription: 18/04/05 Publications récentes
The Authenticator interface is a great way to plugin our own authentications mechanism in a very clean way.
There is one big problem : we do not have access to the request (hence neither the session).
Other people have had the same issue as this thread shows :
http://www.liferay.com/web/guest/community/forums/message_boards/message/117471

But no answer has been given until now.

While I understand why the methods in Authenticator should only return FAILURE or SUCCESS, when you are trying to authenticate on another database (or whatever referential is used) you sometimes receive information that doesn't fit into liferay's user.
You could create another object to store the received info in the db but this isn't always what is needed. In my case, I just need to put the received info in session so my portlets can then access it...

Of course I have already checked the code to see what is done. The LoginAction class calls a service method on UserService that has the following signature :

public int authenticateByEmailAddress(long companyId,
		java.lang.String emailAddress, java.lang.String password,
		java.util.Map headerMap, java.util.Map parameterMap)


I understand why someone would be reluctant to pass the request to a service method. Maybe we could pass another custom map that loginAction would then set in the session...

It's just an idea. But the need for this is real.

Arthur
thumbnail
Ray Augé, modifié il y a 16 années.

RE: Authenticator interface problem

Liferay Legend Publications: 1197 Date d'inscription: 08/02/05 Publications récentes
You can achieve this pretty easily by editting the jsp of the Login/Sign In portlet.

Here is a small patch you could use:

Index: html/portlet/login/view.jsp
===================================================================
--- html/portlet/login/view.jsp (revision 13749)
+++ html/portlet/login/view.jsp (working copy)
@@ -49,11 +49,13 @@
                String login = LoginAction.getLogin(request, "login", company);
                String password = StringPool.BLANK;
                boolean rememberMe = ParamUtil.getBoolean(request, "rememberMe");
+               long groupId = BeanParamUtil.getLong(layout, request, "groupId");
                %>
 
                
"&gt;<portlet:param name="struts_action" value="/login/view" /><portlet:param name="<%= Constants.CMD %>" value="<%= Constants.UPDATE %>" />" method="post" name="<portlet:namespace />fm"&gt; <input name="save_last_path" type="hidden" value="0"> <input name="<portlet:namespace />rememberMe" type="hidden" value="<%= rememberMe %>"> + <input name="<portlet:namespace />groupId" type="hidden" value="<%= groupId %>"> <liferay-ui:error exception="<%= AuthException.class %>" message="authentication-failed" /> <liferay-ui:error exception="<%= CookieNotSupportedException.class %>" message="authentication-failed-please-enable-browser-cookies" />


Notice all I did was add a reference to the current groupId obtained from the layout bean. If the bean doesn't exist (which is impossible with a portlet) you'll get 0, otherwise you'll have the "groupId" accessible from

java.util.Map parameterMap


Cheers!

PS: Add as many params as you might need in the authenticator. Also, note that this is just a change to the Sign In Portlet.. and not the global sign-in form (/c/portal/login), though that one could definitely use the same trick to inject the "groupId" into the parameterMap.

(I'll backref the other thread to this one.)

Hope that helps!
aclement aclement, modifié il y a 16 années.

RE: Authenticator interface problem

Junior Member Publications: 34 Date d'inscription: 18/04/05 Publications récentes
Ok, your solution is pretty good when you want to use a parameter that is already present in liferay. Thanks for this.

But this doesn't solve my problem. Maybe my description wasn't clear so I'll try again :
To be a bit more precise, I'll use a concrete example.

I hooked in my own Authenticator interface implementation that calls a webservice to authenticate the user.
In case of success, several params are sent back by the webservice that I would like to put in session. One of these param is the 'institution code' of the user.
What I was trying to do is pass this institution code from my custom auth class to a hooked in loginPostAction where I could put it in session. I tried to do this with a threadLocal, but I have faced undetermined behavior and I think this is due to a res.sendRedirect at the end of the login, hence the thread in loginPostAction might not always be the same than the one of the login.

Now I only see 2 ways of getting my institution in my loginPostAction : either store it in db during authentication phase, or call the WS again in LoginPostAction.
Neither of the solutions look neat to me, but I guess I'll go with the first one because calling twice a webservice would cost too much in terms of performance.

Arthur CLEMENT
Erik R Hermansen, modifié il y a 15 années.

RE: Authenticator interface problem

Junior Member Publications: 60 Date d'inscription: 15/05/08 Publications récentes
I think it is probably better for me to resurrect this old thread than ask in a fresh post. Arthur's question and the distinction made here is the same as mine. My situation is, well... same thing he said:
aclement aclement:

I hooked in my own Authenticator interface implementation that calls a webservice to authenticate the user.
In case of success, several params are sent back by the webservice that I would like to put in session. One of these param is the 'institution code' of the user.


Like Arthur, I need to store a piece of information returned from a webservice during authentication, and ideally, store that piece information with Liferay's session. Is there a neat way to do this?

-Erik
Erik R Hermansen, modifié il y a 15 années.

RE: Authenticator interface problem

Junior Member Publications: 60 Date d'inscription: 15/05/08 Publications récentes
Would you believe I'm still working on this problem a half-year later? Well, I did do one or two other things in that time. emoticon

So I implemented Ray's solution. I learned that I can create new variables and pass them to the custom Authenticator-derived class. For some reason, I thought I'd be confined to using preexisting variables defined someplace. The trouble I have is getting a value back from the custom Authenticator. I was naive enough to try this:


public class MyCustomAuthenticator implements Authenticator {
	
	public int authenticateByEmailAddress(
			long companyId, String emailAddress, String password,
			Map<string, string[]> headerMap, Map<string, string[]> parameterMap)
			throws AuthException {
		parameterMap.put( "MySpecialVariable", new String[]{"12345"} );
		return SUCCESS;
	}

        /* ...The other two authenticateBy*() methods here... */
}
</string,></string,>


...but this doesn't get written back to the request or anywhere. From walking through code in LoginAction.login() and callees, I think headerMap and parameterMap are just intended to be read-only params to the authenticate methods. So I can pass anything I want to authenticateBy*() methods. I just don't know how to return anything back.

Well, I do have two junky solutions:
* Use expando APIs to store the value in my custom authenticator. But the persistence layer really shouldn't be involved for this.
* Add code to LoginAction.java to store modified parameterMap values in the portlet session if their prefixes match the known session shared variables prefixes in portal-ext.properties. But I like to avoid changing the Liferay wars to avoid upgrading problems.

There is hopefully some simpler way to accomplish this.

-Erik
Erik R Hermansen, modifié il y a 15 années.

RE: Authenticator interface problem

Junior Member Publications: 60 Date d'inscription: 15/05/08 Publications récentes
One more junky solution, the one I ended up using for now...

* Pick a member of the User object that isn't being used by my website and use it to pass back information. In my case, candidates were the open ID and welcome message fields, since my site uses neither. So I kludged this into my authenticator class:


User user = UserLocalServiceUtil.getUserById(userId);
user.setOpenId(valueIWantToPassBackToJsp);


I am hoping that my primitive solutions will be so embarrassingly shoddy, that someone will feel compelled to scream the Right Way to do it into my ear. Other than that, I am documenting my painful thought process in case my boss wants to know how I ended up slapping this junk code together.

-Erik
thumbnail
Andew Jardine, modifié il y a 10 années.

RE: Authenticator interface problem

Liferay Legend Publications: 2416 Date d'inscription: 22/12/10 Publications récentes
Worth noting -- if you want to use the expando attribute for this (during a custom authenticator) then you need to make sure that the UPDATE permission on the field is checked for a guest. Technically, at this stage the user has not actually logged in yet and is still considered a guest. Perhaps not the most elegant option if you are working with something secret but there aren't many other choices.

Another option I have used is to create a service builder project and then set a deployment context dependency for it. You can then write a record while in the authenticator, but again, not necessarily the best option if you are trying to avoid persistence layer access. In my case I also used it to record invalid logins for reporting so it served a dual purpose.