8 Replies Latest reply on May 28, 2009 4:56 AM by eroussel

    Security Question: OpenID integration

      Hello,


      I would need some advice on how to integrate OpenID authentication with Seam security. Here is what my end-result would look like in a nutshell:




      • user authentication is always performed using OpenID

      • OpenID identification is tied to a local user profile (or user account); a specific user profile may have many OpenID identifications associated to it

      • user profiles are stored in a relational database

      • roles and user-roles associations are stored in a relational database

      • authorization is handled through the Seam-provided security features




      More specifically I want to support the following three scenarios:



      1. an unknown OpenID authenticated user comes in; without the end-user intervention, a profile is created with some basic roles associated to it.

      2. a known OpenID authenticated user comes in; the correct profile is loaded and Identity is updated with the proper roles

      3. a logged-in user goes into his user profile maintenance page and requests that an additional OpenID identification be associated with his user profile.



      What is not clear to me is if when using OpenID authentication I can leverage the JpaIdentityStore or not to implement those scenarios.


      Thanks in advance for your help.

        • 1. Re: Security Question: OpenID integration
          norman

          All of those should be possible with the existing openid support.

          • 2. Re: Security Question: OpenID integration

            I think so too. But I don't exactly know where to start and this is why I need some advice.


            Once my user has been authenticated with OpenID, I want to do the following:



            • check if this OpenID identification is known;

            • if it is, then retrieve the associated account info from the DB and set the Identity component accordingly.

            • if it isn't, register this OpenID identification in the DB, then silently (not asking the user for any additional info) create a new account, and associate it with the registered OpenID identification.




            The documentation indicates that navigation rules are the way to handle further actions (sections 15.13.3 and 15.13.4). It seems to me that what this implies is that if you need to do processing to set up Identity before turning the user back to the application, you have to rely on the user doing something (such as providing a username or some other info).


            The only way around that would be to have a rule such as this one:



            <page view-id="/openid.xhtml">
                <navigation evaluate="#{myComponent.smartLogin()}">
                    <rule if-outcome="true">
                        <redirect view-id="/home.xhtml">
                            <message>OpenID login successful...</message>
                        </redirect>
                    </rule>
                    <rule if-outcome="false">
                        <redirect view-id="/home.xhtml">
                            <message>OpenID login rejected...</message>
                        </redirect>
                    </rule>
                </navigation>
            </page>
            



            where the method 'myComponent.smartLogin()' implements the process I described a few paragraphs above. Is that right or is there a smarter way to do this?


            Also, unless I'm wrong, the IdentityManager and IdentityStore facilities are not really useful here. In my scenario I need a 'useraccount' entity which could be the user-class, but there's no point having a property marked with @UserPassword when using OpenID for authentication (and it is a mandatory annotation).


            • 3. Re: Security Question: OpenID integration
              norman

              That's exactly the intention.  You can do all your silent processing in your action.


              Just because you are using OpenId does not preclude maintaining a local username and password.  I think most people using OpenId will do that, leaving OpenId as a login option and not a login requirement.  I'm actually not that familiar with the IdentityStore, so if there's something in there that you don't find isn't working well with OpenId, let me know and we can work with Shane to improve that.

              • 4. Re: Security Question: OpenID integration

                I have additional questions as I'm stuck with an issue.


                My Seam application uses OpenID for user authentication which works fine in 'development mode' (running on a dev machine and accessed via http://localhost:8080/<the app>/) but the app is unable to use OpenID authentication when deployed on a real server. Obviously differences in both setups explain the problem, but I have difficulties finding a solution to this.


                The server setup looks roughly like this:


                Server <-> firewall <-> Internet




                My initial problem was that when the openid4java Relaying Party validation code would be executed the application would throw an exception with an error message looking like this:



                ERROR [RealmVerifier] Discovery failed on realm: <...>




                More specifically, an authentication request was created (AuthRequest.createAuthRequest()), a call to validate it made (AuthRequest.validate()). However, while trying to validate the Relaying Party information a timeout occured within YadisResolver.headXrdsUrl().


                org.openid4java.discovery.yadis.YadisException: 1796: I/O transport error:
                        at org.openid4java.discovery.yadis.YadisResolver.headXrdsUrl(YadisResolver.java:579)
                        at org.openid4java.discovery.yadis.YadisResolver.discover(YadisResolver.java:249)
                        at org.openid4java.discovery.Discovery.rpDiscovery(Discovery.java:646)
                        at org.openid4java.server.RealmVerifier.validate(RealmVerifier.java:133)
                        at org.openid4java.server.RealmVerifier.validate(RealmVerifier.java:108)
                        at org.openid4java.message.AuthRequest.validate(AuthRequest.java:355)
                        at org.openid4java.message.AuthRequest.createAuthRequest(AuthRequest.java:101)



                I tracked the origin of the problem and found out that it was linked to the firewall. In my setup, the server has two network identities (hostnames):



                • the internal one mapping to an internal IP address (let's call it internal_hostname), and

                • an external one mapping to a public IP address (let's call it external_hostname.mydomain.com).



                This error message was triggered because the validation code would use external_hostname.mydomain.com to perform the validation which would trigger a deny firewall rule as the request would look like a Land Attack.


                A possible work-around for this is to adjust the server hosts file to make external_hostname.mydomain.com point to the internal IP address. In that case the Relaying Party information could be validated properly as the firewall wouldn't interfere.


                I actually implemented the change but ran into another problem.


                15:28:15,711 INFO  [ConsumerManager] Verifying authentication response...
                15:28:15,716 INFO  [ConsumerManager] Received positive auth response.
                15:28:15,717 ERROR [ConsumerManager] Return_To URL verification failed.



                This time I was able to authenticate with the OP but as I was returned to the site for post-authentication processing it appears that the Return_To URL verification failed


                I'm starting to think that as I'm trying to fix the initial problem I'm only moving the issue somewhere else, triggering another problem. So maybe I have the wrong approach.


                Any insight to help me make sense of this would be appreciated.

                • 5. Re: Security Question: OpenID integration
                  norman

                  If I am understanding you correctly, you just need to be able to customize the return to URL, correct?  That is definitely reasonable. 

                  • 6. Re: Security Question: OpenID integration

                    Actually, I'm not quite sure what's the way to solve this.


                    I increased the level of logging and this is what I found. When the method verifyReturnTo() (on ConsumerManager) is executed it fails with the following error message:


                    22:58:32,916 DEBUG [ConsumerManager] Return URL schema, authority or path verification failed.
                    22:58:32,916 ERROR [ConsumerManager] Return_To URL verification failed.



                    From the browser URL and the log file here is what I have:


                    1) Browser


                    URL looks like:


                    http://www.mydomain.com:80/myapp/openid.seam



                    2) Log


                    DEBUG [ConsumerManager] Verifying return URL; receiving: http://www.mydomain.com/myapp/openid.seam?<removed stuff>&openid.return_to=http%3A%2F%2Fwww.mydomain.com%3A80%2Fmyapp%2Fopenid.seam&<removed stuff>



                    I'm not an expert on implementing OpenID, but from that this is my conclusion so far:


                    a) The Return_To URL says that the port number must be present in the authority part of the URL.


                    b) In the browser URL field the port number is where it should be.


                    c) If the log tells the truth, somehow the port number  is stripped from the receiving URL somewhere in the verifyResponse() method in OpenId.java.


                    d) verifyReturnTo() fails.

                    • 7. Re: Security Question: OpenID integration
                      norman

                      Sorry.  Maybe I'm being even more dense than usual, but I have no idea what is going on.  Let's take a step back to the beginning before you made any changes. Here's how things should look:



                      You enter an openid and call login(). login() creates a URL for the external openid server and redirects the browser to the external server.  Inside of that redirect will be a return to URL that the openid provider should eventually redirect the user back to.  The user is redirected back to that return to URL and the authentication response is verified.


                      Which step did things go wrong in originally? 

                      • 8. Re: Security Question: OpenID integration

                        First problem


                        Before I made any change, what when wrong was that when login() is called at some point the method validateRpId() from class RealmVerifier fails (this method is called as part of the login() logic): the method tries to validate the RP Id (the server), which is a request to http://www.mydomain.com/myapp/openid.seam


                        Because there is a firewall between the Internet and the server, and because that type of request looks like a Land Attack as far as the firewall is concerned, the request was never allowed to go through (the firewall would block it). So validateRpId() was failing as well as login().


                        The workaround for this was to modify the /etc/hosts file on the server so that www.mydomain.com would point to the server internal IP address (as opposed to the external/Internet one). That way, when validateRpId() would execute it would resolve www.mydomain.com to the internal IP address (instead of the external one) and the firewall rule for Land Attacks would not get triggered. That would allow validateRpId() to do its job and return normally.


                        Just to make things crystal clear, prior to the /etc/hosts change, the request by validateRpId() would appear to come from the Internet, which is www.mydomain.com trying to fetch data from www.mydomain.com. With the /etc/hosts changes, the request stays within the internal network and never goes out to the Internet.


                        Second problem


                        Once this original problem was out of the way, another one appeared, which now looks to me as unrelated. The login() method works, the redirection to the OP works, returning from the OP also works. Where it doesn't work is at the authentication response verification.


                        From what I can see, authentication response verification is a two steps process:



                        1. check if the OP says the authentication was successful

                        2. check if the return to URL matches the actual browser URL.



                        Step 1 works. Step 2 fails. The details of the failure are in my last post, but in a nutshell, it looks like even though the return to URL in the browser's URL field looks like http://www.mydomain.com:80/myapp/openid.seam, somehow in the block of code that calls the verification method, the port number gets lost (:80 disappears).


                        Why that is exactly, I don't know.