Showing posts with label rpxnow. Show all posts
Showing posts with label rpxnow. Show all posts

Friday, January 8, 2010

Unique Display Names

I'm going to focus on new techniques in the coming posts, because the semester is starting.

I've decided to make display names unique in the system. I'm not especially happy with this, since it leads to funky display names like Dave5386. But, if I'm going to provide some sort of forum interaction eventually, I also need a way to differentiate between people who might decide to use the same display name.

I could simply require and display some context to the display name, such as "Jay, Muskingum University". But, providing information online for kids that might be used to locate them is not a good idea.

So, I'll go with unique display names. This means I need to change what I already have a bit.

RPXResults.java is what first puts the user into the datastore. Right now it's getting the user name from the RPX records. That isn't guaranteed to be unique. I could auto-generate a unique name, but then they might not go back and edit it.

So instead, RPXResults.java will, when a user logs in for the first time, redirect to the page for editing their profile. They will be required to update their profile with a unique name before doing using the rest of the site as a logged in user.

While I'm at it, I also want to modify RPXResults.java so that instead of redirecting to /front when a user logs in and they do have their profile info set, it redirects to the page they were trying to access. So if they save a bookmark on My Contests, and try to go to that after the session has expired, it'll take them to the login page and then to My Contests after they log in. That's more user-friendly, and best to do now before I have too many of those sorts of pages to edit.

Redirect to Target Page

We'll do the redirect to the target page first. This requires me to pass into /rpxresults the target page. Here's a page at RPXNow about passing query parameters through the token URL.

Unfortunately, that didn't work for me. The extra query parameter simply never came through to RPXResults.java.

Luckily we do have another option. We can store the destination URL as a cookie on the local machine and fetch it from RPXResults.java.

We'll do FrontPage.java first as an example of how to modify the servlets. Here's the code to save the destination URL as a cookie:


Cookie cookie = new Cookie ("destinationURL", "/front");
resp.addCookie(cookie);


I added this to just before the template is displayed. Now we need to modify RPXResults.java to use the value of this cookie as the destination URL. Here's the code that would replace the resp.redirect ("/front") line:


String destination = null;
Cookie [] cookies = req.getCookies();

if (destination == null && cookies != null)
{
for(int i=0; i<cookies.length; i++)
{
Cookie cookie = cookies[i];

if ("destinationURL".equals(cookie.getName()))
destination = cookie.getValue();
}
}

if (destination == null)
destination = "/front";

resp.sendRedirect(destination);


Now that RPXResults.java is set up, I can go back and modify the ProfileServlet and AdminServlet classes to add the cookie in the same way that the FrontPage servlet does.

Now if I remember to do that for every servlet I add after this, the user will always get to the page they wanted to get to, even if they have to log in first.

That's probably long enough for one post, so I'll do unique user names in the next.

Wednesday, December 16, 2009

Making The User Datastore More Robust

On the front page, I want to expand the data model for the template. I want to be able to display a nice "Welcome back, Jay" sort of message, incorporating the member's name.

This means I have to get the user's name from the RPX results. While I'm at it, I want to make the User records more robust...for example, let's say that I have a hundred people sign up on the site, and later decide I want to start pulling their middle name from the RPX results.

The way the code is written now, I only pull from RPX results when the user first signs in to the site. I want to change that so that on future sign ins, if I discover that the datastore is missing user information I want, I fill that in from the RPX results.

The RPX results has a field called DisplayName that supposedly contains a version of the user's name suitable for display. I want to add that to my user record. I already have a field called Name in the User object, but so far I haven't set it. Here's how I'll set it from within RPXResults.java.

(By the way, I'm going to stop formatting the code for indentation manually in HTML. It's been too much of a pain, and shouldn't affect your ability to understand the code.)


String identifier = info.getElementsByTagName("identifier").item(0).getTextContent();
String name = info.getElementsByTagName("displayName").item(0).getTextContent();

PersistenceManager pm = PMF.get().getPersistenceManager();

Query query = pm.newQuery("select from project.server.User where identifier == identifierParam");
query.declareParameters("String identifierParam");

try
{
List results = (List) query.execute(identifier);
User user = null;

if (results.size() == 0)
{
user = new User ();
user.setIdentifier(identifier);
user.setName(name);
pm.makePersistent(user);
}
else
{
boolean changed = false;

user = results.get(0);

if (user.getName () == null)
{
user.setName (name);
changed = true;
}

if (changed)
pm.makePersistent(user);
}

HttpSession session = req.getSession();
session.setAttribute("userid", user.getId());
}
finally
{
query.closeAll();
pm.close();
}


Note how I set the name when the User already exists, but there's nothing in the name field. That allows me to add new RPX fields that I'm interested in later, and have existing Users pick up those fields.

Tuesday, June 9, 2009

Fetching User's Email From RPX

The next step in the login sequence is to fetch the user's information from RPX. This will include their email, which will be used as our unique identifier for a user.

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.

Starting RPXResults Servlet

To create the RPXResults servlet, navigate in Eclipse to your project.server package. Right click on that and choose New->Class. It should prepopulate the package name for you.

For the name of the class, use RPXResults (or whatever you used in your web.xml file in the previous steps). For the superclass, use javax.servlet.http.HttpServlet.

When the class comes up, right click on it and choose Source->Override/Implement Methods. Choose doGet and doPost and click OK.

The doGet method is what is called when the servlet is loaded via an HTTP GET method. That's what RPXResults will do. Just in case they change to POST later, we'll handle that, too. In your doGet method, replace what's there with a call to doPost, looking like this:

doPost (req, resp);
Now in the doPost, we want to just make sure that we're getting a result from RXP at first. We'll worry later about putting in the processing that's needed. Put something like this in doPost:

String token = req.getParameter("token");
PrintWriter writer = resp.getWriter ();

writer.print("RPX sent this: " + token);
The getParameter call on the request gets the token paramter passed by RPX to the servlet. We then use the PrintWriter to just output the token value as our HTML page. The user will see the token value come up after a successful login.

You can test this by running the web application and logging in via RPX. You should see the token value come up in the page after you log in.

Note that this servlet nicely highlights the way servlets work. A request for a web page starts the servlet running (via a call to doGet or doPost), and the servlet simply outputs the HTML that the user should see. In our case, we didn't output HTML, but we could have.

Next we'll put in the code to complete the login processing and set up a session.