Now that we have the token from RPX in RXPServlet, we need to load a page from RPX at the URL given in step 2 in the RPX Integration Guide. As of right now, that's
https://rpxnow.com/api/v2/auth_info
That page will send back the info we need about the user. Simple, right?
Well, it could be involved. We'd need to use a couple of Java classes to get the data from the URL, and then some other Java classes to unpack the data (it's formatted as XML, a common data interchange format). We'd need to figure out how to use these classes, and then how to format the HTML request to the auth_info page.
Luckily, RPX has provided a Java helper class that does this for us. Follow that link and copy the Java class into a new class in your project.server package. Add a "package project.server;" line at the top, and compile. It should pass with no problems, but will give some warnings. Ignore them.
Now, in your doPost method of your RPXResults servlet, we want to use that class to get the user's info from RPX. Create an instance like this:
Rpx rpx = new Rpx ("PutYourApiKeyHere", "https://rpxnow.com");
Don't forget to change PutYourApiKeyHere to the actual API key listed in your RPX Integration Guide. Then make a call to authInfo, passing in your token (that you should already have in the variable token, from the last post):
Element info = rpx.authInfo (token);
Note that when you try to find an import for Element, you'll get a big long list. The one you want is:
import org.w3c.dom.Element;
That's an XML Element as it's being used by the RPX helper class. Now you have the info from RPX!
Except that it's buried in the XML data structure represented by that Element instance. So we have to do a little bit extra to get it out of there.
First off, though, the data returned by RPX can include different fields depending on which third-party provider the user chose to use (Google, Facebook, Yahoo, etc). The RPX help has a list of possible fields.
You're guaranteed to get the identifier and providerName fields. The identifier is what we're supposed to use to uniquely identify this user in our database. So right now that's what we're interested in getting. Later we may use some of the other fields to prepopulate our user record.
Using the Element that was returned, we call getElementsByTagName and use the name of the profile field we want.
NodeList list = info.getElementsByTagName("identifier");
That returns a list of nodes, because you can use wild cards in the field name. We know we didn't, so there will be exactly one node in the list. We call getTextContent on that node to get the identifier.
writer.println (list.item(0).getTextContent ());
That prints the identifier out to the web page. Not what we'll want to do with it eventually, but at least we can verify that it's working. Run it now and sign in using whatever provider you want, and you should see your identifier come back.
It's a pretty ugly identifier, and too long to use as a primary key. So our next step is going to be to design a sane database structure to use for storing user records. While we're at it we'll structure it so a user can use more than one third-party provider for the same account on our site.
We'll start on that in the next post.
how would one integrate rpxnow.com with GWT?
ReplyDeleteRPX redirects the user's browser to a web page on your site that makes the connection to rpxnow.com to get the profile info. That page can't be a GWT page (because of the same origin policy), but it can be a straight servlet as I've described.
ReplyDeleteAt the end, the servlet can load a GWT page that starts up your application again.
I might also be possible to do all this in a popup, so that your GWT application never actually closes.
Sorry, that should have been, "It might also be possible to do all this in a popup"...I haven't personally done it, but if you do some searches on OpenID in a popup, the techniques are out there.
ReplyDeleteMy code needed a change from:
ReplyDeletewriter.println (list.item(0).getTextContents ());
to:
writer.println (list.item(0).getTextContent ());
Thanks, that must have been a transcription error. My code does indeed use getTextContent (). I've fixed the post.
ReplyDelete