Home

Advertisement

XRI & openID 2.0 questions

  • Feb. 17th, 2008 at 2:48 PM

I had a question this morning from the Drupal community about there openID 2.0 module.


Question:
-------------------------------------------------------
I am posting the question and my long-winded reply in the hope that they may be useful to others.

I'm a bit under water here. James Walker - primary author of the OpenID
module that is part of the recently released Drupal 6 core - posed the initial
question while in the process of updating the module to properly handle
Canonical Ids (see http://drupal.org/node/218097).

I gather from this discussion that most XRI OPs are not yet 2.0 compliant, and
this will break XRI resolution in Drupal 6 if it is updated to be 2.0
compliant. And this is necessary as part of managing Canonical IDs as account
identifiers when delegation occurs.

Can someone please try to clarify for me where we stand and what can be done
about it. In particular, is it possible to properly update the Drupal 6
OpenID module to handle XRI Canonical Ids and delegation?

I hope I'm making some sense.

Thanks!
=Fen
-------------------------------------------------------------
Reply:

Hi Fen,

Lets see if I can shed some light on this for you.


  1. OpenID 2.0 RPs must be backwards compatible with 1.0 OPs. In the XRI case they use the delegate in a openid 1.0 SEP, if they are doing delegation to a different identifier ie a URL or XRI.


  2. In OpenID 2.0 RPs use the LocalID element of the SEP for the delegated name. Only the element name has changed. It serves exactly the same purpose as delegate in 1.0.

    Support for the new LocalID element and the other XRI 2.0 changes are about to go into testing with the iBrokers.

    However until GRS updates there end to XRI 2.0 we cannot pass those elements to GRS, so the elements will only be supported in ooTao community registries.

    This dosn't prevent iBrokers from updating to openID 2.0 as that element is not used unless you are delegating to another identity.


  3. There is no canonical ID in openID 2.0, there is a claimed Identifier that in the URL case can have a fragment added to it by the OP to detect account recycling.

    Except in the case of directed identity the claimed identity returned must have the fragment stripped then match the claimed identity sent (The spec leaves some room for interpretation on this).
    The claimed identity including the fragment should be used as the primary key.


  4. The XRI resolution process in the RP resolves the canonical ID for an iname.
    This is sent to the OP as the claimed ID. The OP must return it unchanged. This is then used as the primary key. (non-directed identity case)


  5. There is nothing to stop a 2.0 RP from using the discovered CID from an XRI as it's primary key even if the SEP is openID 1.0.

    The ability to use the persistent identifier has always existed on the XRI side, but is new on the URL side, so it wasn't in the openID 1.0 spec.

    I believe it is perfectly acceptable to send the claimed identiy to a 1.0 RP though they may or may not return it.


As long as the RP maintains the value of the claimed identifier locally you are good to go for using it as the primary key.


So to the points in the Drupal thread.

Using the claimed identifier as the primary key is a problem with URL's if the OP is not using fragments to detect recycling.

You can have a may to one aliasing of URL's to claimed identifiers in openID 2.0 as well. This is done through http redirects.

Example:

    See openID 2.0 sec 7.2 for refrence.

    user-input: thread-safe.net

    RP preforms normalization step 1: http://thread-safe.net/

    RP preforms service discovery on http://thread-safe.net/ using accept header application/xrds+xml
    RP fallows redirects and retrieves XRDS from https://myopenid.com/thread-safe

    RP performs normalization step 2: https://myopenid.com/thread-safe

    If there is no LocalID in the SEP then according to sec 9.1 the claimed identifier must be used as the value of the openid.identity
    (This is the cause of much greif in the XRI case but I will get to that later)

    So in the request I have:
    openid.claimed_id=https://myopenid.com/thread-safe
    openid.identity=https://myopenid.com/thread-safe

    In the Replay I have:
    openid.claimed_id=https://myopenid.com/thread-safe#127863
    openid.identity=https://myopenid.com/thread-safe

    The claimed ID is used as the primary key.

You can see that my input value is never passed to the OP. If the RP wants to keep track of the product of that first normalization step they can for display purposes.
Unfortunately it looks like may RP never perform the second normalization so continue to use that as the display ID. This causes a rather large security hole in openID.

You can see that if the spec is followed you can have multiple URL inputs resolve to the same primary key.

We see two thing in this:

  • Retrieving the XRDS via https is optional and http is the default unless the OP is correctly configured to redirect to https: see sec 15.1.2 of the openID spec.
    (someone make ***** read it please all there ****** customers are at risk!!! name removed to protect guilty)


  • OP's assigning a fragment to allow RPs to detect account recycling is optional.


  • Now lets consider the XRI case:
    user-input: =jbradly

    RP preforms normalization: xri://=jbradley (the spec actually states =jbradley is preferred but a OP should accept ether)

    Service discovery is done over https: (This must be in the RP code, if they are using http: I consider that non compliant and dangerous) for the four service types in order. This is important because refs are only followed during service selection.
    This is broken in the janrain and other code that just retrieve the XRDS and pars it using the yadis code. libopkele preforms this correctly.

    We now have the Cannonical ID of the selected service in this case http://specs.openid.net/auth/2.0/signon

    This like in the URL case of fallowing http redirects, may fallow XRD and or SEP refs and redirects. Important to remember this is the CID of the service selected not the CID of the iName input.

    All things working correctly we now have:

    RP performs normalization step 2: xri://=!BF81.FD97.C81B.B4E5

    This is the primary key and will not change there are no further steps it is passed to the OP but must return unchanged.
    (the xri: is not recommended but the RP authors like it, and OP's should support ether.

    Note that if my SEP contains:
    https://linksafe.ezibroker.net/server/?qxri=

    And the RP is fully XRI 2.0 compliant my OP endpoint is now https://linksafe.ezibroker.net/server/?qxri==jbradley

    Again this is in libopkele. This allows a IDP to detect what the QXRI was for display purposes. (There are no IDP's that support this yet, but I thought it couldn't hurt)

    If there is no LocalID in the SEP then according to sec 9.1 the claimed identifier must be used as the value of the openid.identity

    The problem with the above is that we already have our persistent identifier in openid.claimed_id in the XRI case we are one step ahead of the URL case.

    So in the Spec for the request we have:

    openid.claimed_id=xri://=!BF81.FD97.C81B.B4E5
    openid.identity=xri://=!BF81.FD97.C81B.B4E5

    This causes a display problem at the OP unless they are recognizing the qxri= parameter.

    My recommendation and the way we did it in libopkele is to send the product of the first normalization or the localID in openid.identity.

    So libopkele sends:
    openid.claimed_id=xri://=!BF81.FD97.C81B.B4E5
    openid.identity=xri://==jbradley

    As the openid.identity is intended as the localID for the OP and as such is what is validated by the OP. The openid.claimed_id is not validated by the OP or used for any purpose I know of. It really is just for returning the URL fragment and or directed identity.


    How directed identity works for XRIs is something we need to dig into a bit. The openID spec 11.2

    If the Claimed Identifier was not previously discovered by the Relying Party (the "openid.identity" in the request was "http://specs.openid.net/auth/2.0/identifier_select" or a different Identifier, or if the OP is sending an unsolicited positive assertion), the Relying Party MUST perform discovery on the Claimed Identifier in the response to make sure that the OP is authorized to make assertions about the Claimed Identifier.

    this leaves room for interpretation.

    I read it as you do XRI resolution on the returned openid.claimed_id for http://specs.openid.net/auth/2.0/signon and compare the one of the URI elements must match the URI element of the OP.

    The service you are supposed to use is implied in the example but that might not be normative. I can see doing service discovery on "http://specs.openid.net/auth/2.0/server" as being equally if not more valid. The advantage being that you would have control over the identity being used for directed identity.

    So the bottom line for the Drupal question is:

    XRI uses secure resolution over https: the canonical ID is validated in the resolution process (in XRI 2.0 set CID validation = true, or re-resolve the CID in the RP to validate). The CID or (iNumber) is assigned by the parent of the XRDS not the user. In most cases this is GRS.

    There is no security problem with using the CID from the discovered XRDS as the primary key. This is true even if the OP is using openID 1.0.

    Hope this helps. I am happy to discuss these issues with the drupal folks.

    =jbradley

    Tags: