Martin Atkins: Yahoo!'s OP and now it seems Microsoft’s OP both ignore the value of openid.identity provided to them, and just return an assertion for whatever user’s logged in.
I may ultimately need to black-list such ids. If everybody uses the same URI, I can’t tell them apart.
Martin Atkins: Yahoo!'s OP and now it seems Microsoft’s OP both ignore the value of openid.identity provided to them, and just return an assertion for whatever user’s logged in.
I may ultimately need to black-list such ids.
Looking at live.com instructions:
At any Web site that supports OpenID 2.0, type
openid.live-INT.com
in the OpenID login box to sign in to that site by means of your Windows Live ID OpenID alias.
If everybody uses the same URI, I can’t tell them apart. That doesn’t concern me much, but do find it a bit distressing that that’s the recommended usage.
What concerns me is that people may use such a URI for delegation. If Jorgen, for example, were to add such a generic URI as his openid.delegate
link, then anybody who has a windows live id could authenticate using his blog URI.
What concerns me more is if somebody follows these instructions for delegation. Then anybody with a Windows Live id could authenticate using his blog.
I note that Jorgen left a comment on Martin’s blog using http://openid.live-int.com/jt. As long as that URI is uniquely his, and can’t be used by anybody else, that’s fine.
just that the Provider will make the user choose their OpenID versus using what they typed in on the RP.
Either I’m profoundly misunderstanding something, or that is a big problem.
If your site delegated to Windows Live and you had a Windows Live id, you could still post here as you. But if I had also Windows Live id, and the provider will let me chose my OpenId vs what was typed in on the RP, couldn’t I also post here as you?
John (if that’s who you really are): just to be clear... you started this process leaving a comment with the URI of johnpanzer.com; you are asserting that I should ignore that?
This, frankly, makes no sense to me.
Somebody posted a comment here with the URI of johnpanzer.com. jonhpanzer.com asserts that the owner of that web site also owns some screenname at AOL. If the OP for AOL ignores my request to validate that the person behind the keyboard owns that screenname, but tells me instead that that person owns some other unrelated property, what can I assume about the original assertion that the person leaving the comment is the owner of johnpanzer.com?
Gosh!
I should probably clarify a few things from that post. I wrote it assuming that my audience was familiar with the ins and outs of OpenID Auth 2.0, but obviously there are some finer points that are not obvious.
First though, let me note that I wrongly accused Microsoft’s implementation of this behavior when in fact their implementation operates as expected. Yahoo!'s implementation is ignoring openid.identity, though.
Let’s assume for the moment that I’m logged in to Yahoo as the user “example”, and I’ve obligingly configured my OpenID identifier to be “http://me.yahoo.com/a/example
” . Let’s assume also that Sam has delegation set up on his site pointing at his Yahoo! identifier which I will assume to be http://me.yahoo.com/a/samruby
. Also, some other guy has set up http://example.com/
to delegate to the identifier http://yahoo.com/
with Yahoo!'s provider.
Now consider that I’m at an RP site and I’m about to log in. There are various things I can type into the OpenID login box...
1. If I type “http://me.yahoo.com/a/example
”, I get sent over to Yahoo! and they send back in response positive assertion for http://me.yahoo.com/a/example
, which becomes my identifier.
2. If I type “yahoo.com”, the RP determines during discovery that this is an “OP identifier” rather than a normal OpenID identifier, so I get sent over to Yahoo! to verify the magic, hard-coded “identifier_select” identifier. Yahoo! sends back a positive assertion for http://me.yahoo.com/a/example
, which becomes my identifier.
3. If I type in “http://me.yahoo.com/a/samruby
”, the RP sends me over to Yahoo! with openid.identity set to http://me.yahoo.com/a/samruby
. Yahoo! ignores this value and sends back what is effectively an unsolicited positive assertion for http://me.yahoo.com/a/example
which, if the RP supports unsolicited positive assertions (it’s only a SHOULD), becomes my identifier. If it doesn’t, signin fails.
4. If I type in “http://intertwingly.net/
”, the RP sends me over to Yahoo! with openid.identity set to http://me.yahoo.com/a/samruby
due to the delegation. Yahoo! ignores this value and sends back an unsolicited positive assertion for http://me.yahoo.com/a/example
. Discovery on “http://intertwingly.net/
” tells the RP that the delegate is samruby, not example. Signin fails.
5. If I type in “http://example.com/
”, the RP sends me over to Yahoo! with openid.identity set to http://yahoo.com/
due to the delegation. Yahoo! ignores this value and sends back an unsolicited positive assertion for http://me.yahoo.com/a/example
. http://example.com/
tells the RP that the delegate is yahoo.com. Signin fails.
So as you can see, the two outcomes here are either you get signed in as an identifier you rightfully own or signin fails. There is no case where two distinct users get the same identifier, nor any case where one user can sign in as another. If anything different happens to what I’ve described above, the RP is broken.
Yahoo!'s ignoring of openid.identity is a user experience issue, not a security issue. If delegation is not in use, the user ends up logged in as a different identifier to what they entered, as in case 3 above. If delegation is in use, everything proceeds nicely until the assertion goes back to the RP and the RP fails with some cryptic error message about incorrect delegation. The only security-related problem here is that a user who is logged in as an account other than the one he thinks he’s logged into at the RP may end up disclosing information to an unintended party.
With other providers that do respect openid.identity, the expected failure conditions happen much earlier allowing them to be responded to in a more sensible way, and users will not unexpectedly get logged into accounts other than the one they specified in the RP login box.
(Your [link] code only seems to transform the first naked URI, by the way. In the preview, at least.)
If anything different happens to what I’ve described above, the RP is broken.
I’m using JanRain’s open id for python, currently 2.0.1, if would like, you can see the code. The relevant portion of the logic is as follows:
server = consumer.Consumer(session, store) response = server.complete(args) if response.status == consumer.SUCCESS:
Is my RP broken? Do I need to do more to protect against unwanted unsolicited positive assertions?
(Your [link] code only seems to transform the first naked URI, by the way. In the preview, at least.)
I took the liberty of un-[link]-ing your URIs. Better results can be obtained by enclosing the URIs in triple curly braces.
Documentation: consumer, complete, success, failure.
Not a word about unsolicited responses. I can’t tell from the documentation whether such responses are to be considered a SUCCESS or a FAILURE. If it is a SUCCESS, I see no suggestion that I need to make further checks in order not to be susceptible to spoofing.
Unsolicited Positive Assertions aren’t really anything to worry about. All it means is that you’ve got an answer to a question you didn’t ask. The consumer library will still check to make sure that the assertion is valid, so spoofing is not an issue. In this situation you can either ignore it, or make the best of it.
In the sign-in use case, RPs generally “make the best of it” by signing the user in as whatever identifer was in the positive assertion, regardless of what the user originally entered. This is probably what your blog should be doing. Generally this outcome requires no special action on your part since the Consumer library will hand you the URL that was in the assertion, not the URL that was originally requested.
If what you’re doing is trying to is verify that a user owns a particular URL, then an unsolicited positive assertion isn’t useful to you, so you can error in this case. In order to do this, you need to “remember” somehow what URL you originally asked for, which requires state on your end. You can then compare the original URL with the URL in the assertion and fail if they are not the same. As long as you don’t use the original URL for anything, though, you don’t need to do this.
You can try this for yourself to see if your implementation is vunlerable. Find a Yahoo! identifier for some other user and enter it into your URI box. Authenticate using your Yahoo ID. Yahoo will send back your identifier. What I assume will happen is that the comment will get posted using your identifier rather than the identifier you originally entered. The other possibility is that you’ll fail out with some error. If you manage to successfully post with the wrong identifier, then you’ve got a problem and I’d be happy to help you figure out what it is!
Unsolicited Positive Assertions aren’t really anything to worry about
OK, so I don’t have to do anything.
You can then compare the original URL with the URL in the assertion and fail if they are not the same.
So, I do need to do something.
Find a Yahoo! identifier for some other user and enter it into your URI box
To do that, I need to do is to find somebody who has delegated their id to Yahoo! Any ideas?
But really, what I want is the answer to my previous question, which I will restate this way: using JanRain OpenId for Python, version 2.x, what does SUCCESS mean? Does it mean that it has verified the ID you asked for? Or does it mean that it has answered some question which may, or may not, have any relation to the one you asked?
You don’t need to do anything unless you want to bitch when you get back a different URL than you asked for. There isn’t really any harm in unsolicited positive assertions for your use-case.
Delegation isn’t really important here. If JanRain’s libraries are correct (which they might not be, but let’s assume they are for the moment) the verification of the assertion will fail when you put delegation in the mix. The case where delegation is not in use is the case where you might get tripped up, since you must make sure to use the URL in the positive assertion, not the URL the user originally entered. If you do that, then it won’t be possible for anyone to spoof anyone else.
If you do want to test to see if JanRain’s libraries are doing delegate verification correctly, you can publish a page that delegates to someone else’s Yahoo! OpenID identifier and try to log in with it. If everything goes to plan, the library will determine that the assertion is invalid and fail.
I did try to post this with my URL set to “http://yahoo.com/”, and it failed with the following error despite the fact that I authenticated with Yahoo successfully:
[link] is an OpenID enabled website. In order to use this URI, you need to be able to authenticate with the OpenID Provider for that website.
So it looks like you’re currently reacting to unsolicited positive assertion cases by failing. That’s fine. The way you’ve got your UI set up, people are unlikely to put in OP identifiers anyway, since you don’t make a big deal out of the fact that your URI box supports OpenID.
So it looks like you’re currently reacting to unsolicited positive assertion cases by failing
Actually, it looks like I can’t submit a comment using my yahoo id, so I don’t think that we know enough about whether I react property to unsolicited positive assertions or not. Either I’m doing something wrong, or Yahoo’s implementation isn’t compatible with JanRain’s Python library version 2.0.1.
I’m not eager to either upgrade or fix it until I get an answer to my question as to what SUCCESS means.
you don’t make a big deal out of the fact that your URI box supports OpenID.
The OpenId icon is on the right instead of the left, and given my seasonal Halloween colors, it is a bit washed out. I do get a fair percentage of openid validated comments, which I indicate using the openid logo next to their name.
For better or worse, OpenID doesn’t make any distinction between different kinds of redirect, so JanRain’s library is behaving as per spec.
One issue you might be encountering is that Yahoo! adds a fragment part to the URLs they assert, so the URL you get back won’t be exactly equal to what you entered. An early 2.0-enabled version of Net::OpenID::Consumer Perl implementation ran into this problem too. The issue I think is with this bit of your code:
if response.status == consumer.SUCCESS and \
session['original_id'] == response.identity_url:
In order to support Yahoo!'s behavior (which, in this case, is unfortunately allowed by the spec as a means for “identifier recycling") you’ll need to either remove that equality check and change all of your later code to use response.identity_url
instead of session['original_id']
, or adapt that check so that it trims the fragment part off response.identity_url
before doing the comparison. JanRain’s consumer library should already be doing this "check for equality without the fragment” thing, so you shouldn’t need to duplicate it in your code. If JanRain’s implementation works like the Perl implementation then anything returned in response.identity_url
will have been verified as a correct assertion. Hopefully your response on their mailing list will confirm this to be the case.
(This discussion is quite useful from a “finding things that need to be documented better” perspective. Once we’re done here, we could probably write up a good wiki page or something about this stuff. It’s clear that there’s a bunch of pitfalls for the unwary.)
For better or worse, OpenID doesn’t make any distinction between different kinds of redirect, so JanRain’s library is behaving as per spec.
Depends on what spec you are talking about. I don’t know if you know much about my background, but I tend to have a real problem with people who view HTTP as merely an inconvenience that they can tunnel over, and ignore any problems that they create by doing so.
Are the words “resides temporarily under a different URI” so difficult to understand?
then anything returned in response.identity_url will have been verified as a correct assertion
I like that! I’ve made the change, lets see how that works out.
For what it’s worth, I agree that OpenID could have done better with the redirect thing — and I said as much in response to you in that mailing list thread you linked to. There was a concern that implementors would get this wrong more than they get it right because most people conflate the different redirect responses and don’t understand the differences between them. Also, many existing HTTP client implementations and web app frameworks bake this “all redirects are created equal” assumption into their APIs, making life difficult if you actually do what to respect what the HTTP spec says.
Unfortunately it’s “higher layer protocol’s progative” to ignore, mash together or otherwise misinterpret stuff provided by lower layers. This is nowhere near the first example of this sort of behavior, and there are unfortunately several other examples in OpenID as it exists today. I agree that it’s not a good state of affairs, but I also think OpenID’s pretty set in stone at this point and making yet another incompatible change to the core protocol when we’re only just now starting to get near-universal support for Authentication 2.0 would be counter-productive.
There are no “unsolicited positive assertions” here since the assertion comes in response to an authentication request made by the blog software (or the OpenID library used by the blog).
The only place where unsolicited positive assertions are currently used is in the update protocol for the attribute exchange extension, which allows the OpenID provider to push updates to user attributes to the relying party without going through the user’s browser. Here, a positive assertion message is sent directly server to server without any matching request.
In the directed identity case (which you seem to be talking about), the process goes something like this:
1. user enters in an identity URL
2. blog performs discovery on the URL and determines that it is a server URL rather than an identity URL
3. blog initiates an authentication request with the server found through discovery. The request uses a special value for openid.identity and openid.claimed_id.
4. [user authenticates with their identity provider]
5. blog receives positive assertion response containing the real values for openid.identity and openid.claimed_id.
6. blog performs discovery on the new openid.claimed_id value to verify that the server is able to make assertions about this identity.
The client library should hide most of this from you, and using claimed ID from the response in place of what the user entered is enough to support directed identity (in fact, not doing so will likely lead to problems).
Now if you have a server that responds with a different identity to the one included in the request and it wasn’t a directed identity request, then the client library should treat the response as invalid (I believe the python-openid library does this). So it should be safe to trust the identity returned by the library if it says the status is SUCCESS.
James,
Your last paragraph — responding with a different identifier to the one in the request — is what we’re talking about, and what Yahoo!'s OP is doing. I asserted that this is technically valid per spec if you assume that the request wasn’t answered and by coincidence Yahoo! sent an unsolicited positive assertion moments later.
Given the language about unsolicited positive assertions in the spec — namely that RPs SHOULD accept them — RPs SHOULD accept these random “answer to a question I didn’t ask” responses from Yahoo! And, in fact, many of them do just by virtue of the fact that they don’t retain enough state to know what question they asked in the first place. For many RPs, the only state they maintain is the per-OP association information. An OpenID positive assertion response contains all of the information you need to verify it without any additional state as long as you don’t want to explicitly disallow unsolicited positive assertions.
And just to restate my conclusion from earlier: as long as you only use the URL in the assertion and not the URL the user originally entered you’re safe from spoofing. If you care about the OP switching identifiers on you then you need to do a bit of extra work, but this isn’t necessary for the “find a URL to identify this user by” use-case.
With that said, I wouldn’t disagree that it’d be good to have some more detail in the spec about this so that you don’t need to resort to fitting together obscure interactions from different parts of the spec in order to decide whether this behavior is valid.