Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How or when to follow redirected OpenIDs?

I'm currently implementing OpenID authentication for a website. During testing, I've noticed that Google accepts different versions of claimed Google Profile IDs, e.g.:

  • http://www.google.com/profiles/stefan.fussenegger
  • http://profiles.google.com/u/0/stefan.fussenegger/about
  • https://profiles.google.com/stefan.fussenegger
  • https://profiles.google.com/stefanfussenegger

Interestingly, the verified ID differs as well (for the samples above, same order):

  • http://www.google.com/profiles/stefan.fussenegger
  • https://profiles.google.com/stefanfussenegger
  • https://profiles.google.com/stefan.fussenegger
  • https://profiles.google.com/stefanfussenegger

Of course, this makes looking up the associated user account quite difficult, not to say impossible. Interestingly, all above IDs work for Stackoverflow. So I thought that there has to be some normalization step I'm missing in my implementation - or SO does some specialized voodoo to get things straight.

Looking at 7.2 Normatlization of the OpenID Authentication spec I found this:

URL Identifiers MUST then be further normalized by both following redirects when retrieving their content and finally applying the rules in Section 6 of [RFC3986] to the final destination URL. This final URL MUST be noted by the Relying Party as the Claimed Identifier and be used when requesting authentication.

Following redirects of claimed IDs doesn't help too much as I'm still left with two different IDs:

  • https://profiles.google.com/stefan.fussenegger
  • https://profiles.google.com/stefanfussenegger

Looking at redirects of verified IDs is much more helpful though as I always end up with this one:

  • https://profiles.google.com/stefan.fussenegger

Okay, looks like I should follow redirects of verified IDs, not claimed IDs.

The question now: Is it secure to follow redirects of claimed/verifed IDs, e.g. before search the DB like so:

do {
  user = lookup(verifiedId)
  if (user is null)
    response = fetchUrl(verifiedId)
    if (response.location is null) {
      break # no redirect, jump out of loop, unknown user
    } else {
      verifiedId = response.location # use redirect location
    }
} while (user is null)

return user;

If yes, I suspect that this should not only be done when looking up a user but when storing a new ID as well, right?

(If I should really follow redirect, I have another question about potential malicious redirects, but that will have to wait until I get an answer to this one. Might become obsolete anyway)

like image 442
sfussenegger Avatar asked Jun 10 '11 16:06

sfussenegger


1 Answers

Open ID 2.0 says that during discovery,

URL Identifiers MUST then be further normalized by both following redirects when retrieving their content and finally applying the rules in Section 6 of [RFC3986] to the final destination URL. This final URL MUST be noted by the Relying Party as the Claimed Identifier and be used when requesting authentication.

So, according to this, you should take the user-supplied identifier and normalize it by following redirects & following normal URL normalization procedures.

The result is considered the 'claimed identifier' (CI). Next, you'll do the association dance and determine if this claim is true.

Note - Some providers have a 'well-known' OpenId Provider (OP) URL, for example Google. If you notice the sign-in process for StackOverflow, you can simply click the Google button instead of filling out a form. In this variant, the 'well-known' OP URL is not the users CI. The user did not provide you with a CI. You'll need to wait until you complete the authentication dance and Google tells you who the user is.

It's at this point (after receiving a successful association call-back from the OpenId Provider) that you'll have an identifier for the user. Per section 9.1 you SHALL receive either both an openid.claimed_id and openid.identity, or neither field if you're doing something fancy with extensions (I'm not very familiar with this aspect of the spec).

Now you should store the openid.claimed_id on your end - this will be the identifier unique to this user. This could be different from what the user originally supplied you. It might also be different from where you ended up (after following redirects on the user-supplied identifier). The OpenID Provider has the final say.

In regards to the security of following redirects on the user-supplied identifier. There shouldn't be an issue here. Redirects allow a user to delegate authentication to a provider of their choosing. No matter where the redirects lead you, you will end up asking that OpenId Provider to establish an association with you. When you make this request you'll supply the (normalized) claimed identifier and the provider can decide if they wish to be responsible for the claimed identifier, and they will (somehow in their infinite wisdom) authorize that the user has ownership of this claimed identifier.

Going back to Google, the claimed identifier Google will end up supplying you will look nothing like your examples above. The example they use is openid.claimed_id=https://www.google.com/accounts/o8/id/id=AItOawl27F2M92ry4jTdjiVx06tuFNA (source).

Hope that helps...

like image 179
Tails Avatar answered Oct 21 '22 00:10

Tails