Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nearly all OpenId relying parties reject assertions from my dotNetOpenAuth backed provider

Constructing an OpenId provider, I've run into the curious problem that only Stack Exchange sites will accept it.

Discovery works fine, and watching log traffic I'm sending (what to me looks like) a valid response back.

Wonderfully, there are no compliance tests* to tell me what's wrong, nor do most sites which offer logins via OpenId give you any useful error messages. Stack Overflow gives some, but it seems to be the only relying party that just accepts my assertions, so... yeah.

Anyway, if I try to login to (for example) Typepad I ultimately redirect back to a url like

https://www.typepad.com/secure/services/signin/openid?openid-check=1&archetype.quickreg=1&tos_locale=en_US&portal=typepad&oic.time=1303249620-9db5665031c9c6b36031&openid.claimed_id=https://example/user/8c481fb7-1b5c-4e50-86b5-xxxxxxxxx&openid.identity=https://example/user/8c481fb7-1b5c-4e50-86b5-xxxxxxxxx&openid.sig=hoaxQrsN4BBg6H8kp50NoQwpHmcO96BBe+jB3oOP2UA=&openid.signed=claimed_id,identity,assoc_handle,op_endpoint,return_to,response_nonce,ns.alias3,alias3.mode&openid.assoc_handle={634388464235195799}{oqMrOA==}{32}&openid.op_endpoint=https://example/openid/provider&openid.return_to=https://www.typepad.com/secure/services/signin/openid?openid-check=1&archetype.quickreg=1&tos_locale=en_US&portal=typepad&oic.time=1303249620-9db5665031c9c6b36031&openid.response_nonce=2011-04-19T21:47:03Z1aa4NZ48&openid.mode=id_res&openid.ns=http://specs.openid.net/auth/2.0&openid.ns.alias3=http://openid.net/srv/ax/1.0&openid.alias3.mode=fetch_response

Broken out for (slightly) easier reading:

openid-check=1
archetype.quickreg=1
tos_locale=en_US
portal=typepad
oic.time=1303249620-9db5665031c9c6b36031
openid.claimed_id=https://example/user/8c481fb7-1b5c-4e50-86b5-xxxxxxxxx
openid.identity=https://example/user/8c481fb7-1b5c-4e50-86b5-xxxxxxxxx
openid.sig=hoaxQrsN4BBg6H8kp50NoQwpHmcO96BBe+jB3oOP2UA=
openid.signed=claimed_id,identity,assoc_handle,op_endpoint,return_to,response_nonce,ns.alias3,alias3.mode
openid.assoc_handle={634388464235195799}{oqMrOA==}{32}
openid.op_endpoint=https://example/openid/provider
openid.return_to=https://www.typepad.com/secure/services/signin/openid?openid-check=1
archetype.quickreg=1
tos_locale=en_US
portal=typepad
oic.time=1303249620-9db5665031c9c6b36031
openid.response_nonce=2011-04-19T21:47:03Z1aa4NZ48
openid.mode=id_res
openid.ns=http://specs.openid.net/auth/2.0
openid.ns.alias3=http://openid.net/srv/ax/1.0
openid.alias3.mode=fetch_response

Here's XRDS for the user (since discovery seems ok).

<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS
xmlns:xrds="xri://$xrds"
xmlns:openid="http://openid.net/xmlns/1.0"
xmlns="xri://$xrd*($v*2.0)">
    <XRD>
      <Service priority="10">
        <Type>http://specs.openid.net/auth/2.0/signon</Type>
        <LocalID>https://example/user/8c481fb7-1b5c-4e50-86b5-xxxxxxxxx</LocalID>
        <Type>http://openid.net/extensions/sreg/1.1</Type>
        <Type>http://axschema.org/contact/email</Type>
        <URI>https://example/openid/provider</URI>
      </Service>

      <Service priority="20">
        <Type>http://openid.net/signon/1.0</Type>
        <Type>http://openid.net/extensions/sreg/1.1</Type>
        <Type>http://axschema.org/contact/email</Type>
        <URI>https://example/openid/provider</URI>
     </Service>
   </XRD>
</xrds:XRDS>

If you dig into TypePad's html, you find the following error message

<!-- Error Code: unexpected_url_redirect -->

... which is why I use them as an example. Kind of useless, but its something.

This code is very heavily based on the example MVC project that ships with dotNetOpenAuth, the SendAssertion implementation is where I suspect things are getting ill.

protected ActionResult SendAssertion(IAuthenticationRequest authReq)
{
    // Not shown: redirect to a prompt if needed

    if (authReq.IsDirectedIdentity)
    {
        authReq.LocalIdentifier = Current.LoggedInUser.GetClaimedIdentifier();
    }
    if (!authReq.IsDelegatedIdentifier)
    {
        authReq.ClaimedIdentifier = authReq.LocalIdentifier;
    }

    authReq.IsAuthenticated = this.UserControlsIdentifier(authReq);

    if (authReq.IsAuthenticated.Value)
    {
        // User can setup an alias, but we don't actually want relying parties to store that since it can change over time
        authReq.ClaimedIdentifier = Current.LoggedInUser.GetClaimedIdentifier();
        authReq.LocalIdentifier = Current.LoggedInUser.GetClaimedIdentifier();

        // Not shown: responding to AX and SREG requests
    }

    var req = OpenIdProvider.PrepareResponse(authReq);
    var ret = req.AsActionResult();

    return ret;
}

Sorry this is such a huge info dump, but I haven't got a lot to go on and accordingly can't really narrow it down to something simple.

So I guess the ultimate question is... any ideas what I'm doing wrong here?

*OK, there are sort of tests. But nothing says "ah-ha, this is broken."

like image 669
Kevin Montrose Avatar asked Nov 04 '22 23:11

Kevin Montrose


1 Answers

There turned out to be a whole bunch of other gotchas here, none of them really pertaining to dotNetOpenAuth.

  1. This whole thing is forcing SSL, and redirecting all HTTP traffic that comes in.
    • ... except that the redirect for all HTTP traffic was to /, which broke discovery
  2. HTML discovery was broken.
    • while, in theory, relying parties can do all discovery using headers in practice nearly everybody starts parsing HTML at some point.
  3. Some relying parties munge up the OpenId 1.1 and 2.0 specs
    • this overlaps a bit with #2, but some relying parties are perfectly happy to do discovery with headers (OpenId 2.0) but then go hunting for openid.server in the returned HTML.

dotNetOpenAuth as one of the (apparently few) OpenId libraries out there that can do the whole OpenId dance over headers worked just fine, by coincidence.

like image 193
Kevin Montrose Avatar answered Nov 09 '22 13:11

Kevin Montrose