{D}

^^%%DATA%%

h3=>{D}

^^%%DATA%%

r=>{S}→ btwoc=>{S}btwoc keyvalue=>{S}Key/Value pairs mode<= {F}
Description:%%des%%
HTTP method:%%method%%
Flow:%%flow%%

Mode '%%name%%': Request parameters

%%in%%
paramvaluedefaultnotes

Mode '%%name%%': Response parameters

Response format: %%format%%

%%out%%
paramvaluenotes

Mode '%%name%%': Extra notes

%%notes%% <=mode lit=>{D}"%%DATA%%" ip<= {F} %%name%% %%value%% %%default%% %%notes%% <=ip op<= {F} %%name%% %%value%% %%notes%% <=op obar=>{D}%%DATA%% ibar=>{D}%%DATA%% warn=>{D}WARNING: %%DATA%% <=localblocks _info?>Specs head<= <=head body<=

Table of Contents

Identity
An identity in OpenID is just a URL. The whole flow of the OpenID protocol is about proving that an End User is (owns) a URL. There is no profile exchange mechanism. For that, Consumers can read the End User's declared FOAF/XFN/vCARD/Atom/RSS/etc.
End User
The actual human user who wants to prove their identity to another site.
User-Agent
The End User's web browser. No special plug-ins or JavaScript required.
Consumer
The website that wants to consume the End User's identity. It's this site that's skeptical of the End User's declared identity and requires proof from the identity URL's declared identity server.
Server
Also called "identity server". This is the URL that the End User has declared is authorative over their identity. The End User puts this declaration in their identity URL's HTML <head>. The consumer discovers the identity server from the user's identity URL, then asks the identity server for cryptographic proof, which is returned as extra GET arguments back to the consumer as the User Agent is redirected around.

To use some URL as your identity with OpenID, that URL has to declare which OpenID server is authorative over it. The host serving a particular URL doesn't need to support OpenID --- the identity URL and OpenID server can be on two totally different services.

Let's imagine your identity URL (your personal website/blog/profile page) is, say, http://bob.com/. Bob.com's <head> should contain, say:

<link rel="openid.server" href="http://bob.com/openid-server.app">

Important notes:

It's possible to use somebody else's OpenID server without that server knowing about your website if you also have control of a URL which that other OpenID server will assert for you.

For example, you may want to use your website, http://bob.com/, as your identity, but you don't have the means (or desire) to run an OpenID server. However, you have a LiveJournal account (say, user "bob"), and you know that LiveJournal provides an OpenID server and that it'll assert that you control http://bob.livejournal.com/.

So, to use bob.com as your identity, but have consumers actually verify http://bob.livejournal.com/, you'd say (on bob.com):

<link rel="openid.server"   href="http://www.livejournal.com/openid/server.bml">
<link rel="openid.delegate" href="http://bob.livejournal.com/">

Now, when a consumer sees that, it'll talk to openid/server.bml and ask if you're bob.livejournal.com, never mentioning bob.com anywhere on the wire.

The main advantage of this is that you can keep an OpenID identity over many years, even as services come and go. You just keep changing who you use for your OpenID server, or maybe even run your own.

Important notes:

An end user visits a consumer site which says it supports OpenID. The consumer site says, "Hey, we support OpenID. You can leave an authenticated comment if you use OpenID." So the user then types in "bob.com":

Login with your blog URL:
For example: happygirl.bloghost.com

Note that the user can leave off "http://" and the trailing "/". A consumer must canonicalize the URL, following redirects and noting the final URL. The final, canonicalized URL is the user's identity URL.

It's also recommend that the form field be named openid_url so browsers auto-complete user's URLs between different sites, in the same way the ecommerce world tends to use conventions like "address1" and "address2".

Also note that this is called a "claimed" identity at this point, until it's been verified.

Now the consumer site must fetch the document (perhaps from cache) that the user entered. Make note that the user could be malicious and try to make you connect to your internal network, or tarpit you, etc. You'll probably want to use a paranoid HTTP library like LWPx::ParanoidAgent that protects you from attackers.

The consumer then parses the head section and finds the "openid.server" and the optional "openid.delegate" declarations. (Note: consumers MUST support openid.delegate)

The protocol supports both a "smart mode" and "dumb mode" to accomodate consumers of differing capabilities. A smart consumer does a little more work at the beginning to save itself work later, but requires local caching of state information. A dumb consumer is completely stateless, but requires extra HTTP requests.

It's recommended that a consumer, if possible, first submit an openid.mode=associate POST request to the identity server and get a shared secret. This shared secret will be used as the HMAC-SHA1 key in future identity check requests. If a consumer already has a shared secret with that server that hasn't expired, it should keep on using that.

The shared secret can be exchanged either in plain-text or encrypted with a Diffie-Hellman-negotiated secret. Not that if Diffie-Hellman is used, it's only used in the associate mode. The identity check modes assume you already have some shared secret, regardless of how you got it.

Now the consumer constructs a URL to the identity server's openid.mode=checkid_immediate (or checkid_setup) URLs and sends the User-Agent there. By sending the User-Agent there, the user's cookies and whatever other login credentials are sent back to their trusted identity server. The server does its work, appends its response onto your supplied return_to URL, and sends the user-agent back at you.

Details of each OpenID request type.

associate method=>POST flow=>consumer server consumer des=>Establish a shared secret between consumer and server. format=> in<= openid.mode value=> default=> notes=> ip?> openid.assoc_type value=>Preferred association type. default=> notes=>Optional. Currently only one value. Defaults to HMAC-SHA1. ip?> openid.session_type value=>Preferred way to encrypt the shared secret. Either blank or default=>None. (cleartext) notes=>Without specifying DH-SHA1, secrets are sent in the clear. As this is usually between consumer and server, which are usually on good net connections (not, say, WiFi), it's not as risky as you might think to send it in the clear. However, it's still recommended that you use the DH-SHA1 mode to encrypt the shared secret. ip?> openid.dh_modulus value=>base64((p)) default<=
base64((p)), where p = 1551728981814736974712322577637155\ 3991572480196691540447970779531405\ 7629378541917580651227423698188993\ 7278161526466314385615958256881888\ 8995127215884267541995034125870655\ 6549803580104870537681476726513255\ 7470407658574792912915723345106432\ 4509471500722962109419434978392598\ 4760375594985848253359305585439638443 <=default notes=>The "p" value of Diffie-Hellman, if using DH-SHA1 session_type. If unspecified, default value is used. ip?> openid.dh_gen value=>base64((g)) default=>base64((g)), where g = 2 notes=>The "g" value of Diffie-Hellman, if using DH-SHA1 session_type. If unspecified, default value is used. ip?> openid.dh_consumer_public value=>base64((g ^ x mod p)) default=> notes=>The consumer's public key. Required if using DH-SHA1 session_type. ip?> <=in out<= assoc_type value=>The association type for the returned handle. notes=>The only current mode is , and all consumers must support it, even if the consumer asked for a different type. Note that in your consumer's cache, mapping assoc_handles to secrets, you must also map the assoc_handle to its assoc_type. op?> assoc_handle value=>The association handle to be provided in future transaciton. notes=>An opaque handle on this association (assoc_type, and shared secret) to be sent to the server later. See limits below on limits of assoc_handle's format. op?> issued value=>UTC date on server notes=>The server's date at time of issuing this association handle. See misc section below for format of date. op?> replace_after value=>UTC recommended replacement date notes=>Optional. The date after which the consumer is advised to fetch a new association. op?> expiry value=>UTC expiration date notes=>The date at which the assoc_handle will no longer work. op?> session_type value=>The encryption mode that the server chose. May be blank/absent, or . notes=>Yes, you can ask for DH-SHA1 encryption and get back a plaintext secret. If this troubles you, don't use the handle and instead use dumb mode with that server. (and if somebody sniffed the plaintext secret, it won't matter, since you'll never accept queries using that assoc_handle). If the server can't do DH, it's probably limited in some way, but using dumb mode is still safe, if not a little slower. op?> dh_server_public value=>base64((g ^ y mod p)) notes=>The server's Diffie-Hellman public key, if using DH-SHA1 mode. op?> enc_mac_key value=>base64(SHA1((g^ (xy) mod p)) XOR secret(assoc_handle)) notes=>The encrypted shared secret, if using DH-SHA1 mode. op?> mac_key value=>base64(secret(assoc_handle)) notes=>The plaintext shared secret, when not using DH-SHA1 mode. op?> <=out notes<= <=notes mode?> checkid_immediate method=>GET format=>query string arguments flow=>consumer UA server UA consumer des=>Ask a server if a user is who they say they are, getting back immediate a "yes" (with signature) or "can't say" answer. notes=>

checkid_immediate mode is commonly used for "AJAX"-style setups, doing fancy JavaScript stuff. The more classic mode to check an identity is checkid_setup.

in<= openid.mode value=> default=> notes=> ip?> openid.identity value=>identity URL being checked default=> notes=>The identity URL the end user is asking the ID server to verify. The exact question is: "Does the user logged in to your site own this URL, and do they allow the trust_root (and therefore the return_to URL) to know that?" (Note that the question isn't "Who's logged in?") Note: An identity server should only assert to URLs that it manages/produces directly. If a user wants to assert other URLs outside of that server realm, they should use openid.delegate. ip?> openid.assoc_handle value=>The assoc_handle from the associate mode. default=> notes=>Optional. Consumer must use dumb mode (check_authentication mode) if an assoc_handle isn't provided. Also, if you use an assoc_handle the server doesn't know about, it'll pick its own and you'll have to use dumb mode as well. ip?> openid.return_to value=>URL to return back to default=> notes=>This is where the server is being asked to return the User-Agent back to, with the response in the GET paramaters. Note that the URL you provide may contain an existing query string, and the server must preserve it when appending the response parameters. ip?> openid.trust_root value=>URL for user to trust default=> notes=>(Optional, but recommended) -- The URL which the user will actually see to approve. The return_to URL must descend from the trust_root, or the identity server will return an error, not a redirect. Namely, the URL scheme and port must match. The path, if present, but be equal or below the trust_root, and the domains on both must match, or, the trust_root contain a wildcard like http://*.livejournal.com (but the wildcard may only be at the beginning) You can try to pass things like http://*.com/ or http://*.co.uk/, but any respectable identity server will protect their users from that. Defaults to return_to URL if absent. ip?> <=in out<= openid.mode value=> notes=> op?> openid.user_setup_url value=>URL to send user to complete process notes<= If the identity assertion fails, the server provides this URL for where the user can do whatever's necessary to fulfill the assertion, be it login, setup permissions, etc. The server should return a URL which doesn't imply anything about what's needed, so the consumer is left in the dark about why the assertion failed.

The identity server handling this user should eventually return them to the return_to URL, acting like a checkid_setup response, with either a or mode. <=notes op?> openid.identity value=>Identity URL notes=>The identity URL being asserted. op?> openid.assoc_handle value=>Opaque handle notes=>The assoc_handle being used to find the HMAC key for the signature op?> openid.issued value=>UTC time of server notes=> op?> openid.valid_to value=>UTC time until this assertion is no longer valid notes=> op?> openid.return_to value=>A verbatim copy of the return_to URL parameter you sent in the request, before the server modified it. notes=> op?> openid.signed value=>List of signed fields notes=>Comma-seperated list of parameters that the signature covers, without the "openid." prefix. For example: identity,return_to lit?> op?> openid.sig value=>base64(HMAC(secret(assoc_handle), token_contents) notes=>Where "token_contents" is a key-value format string of all the signed keys and values in this response (in the same order as listed in the "signed" field). Consumer will have to recreate the token_contents string prior to checking the signature. op?> openid.invalidate_handle value=>an association handle notes=>Optional. If the server didn't accept/recognize your provided assoc_handle for whatever reason, it'll choose its own to use, and copy the one you provided back into invalidate_handle, to tell you to stop using it. You should then send it along in your check_authentication request to verify it actually should be dropped. op?> <=out mode?> checkid_setup method=>GET flow=>consumer UA [server UA ]+ consumer format=>query string arguments des=>Ask a server if a user is who they say they are, but be willing to wait for the reply (you're passing off control of the User-Agent to the identity server for a period of time). Returns either a "yes" (with signature) answer, or a "cancel" signal. in<= openid.mode value=> ip?> openid.identity value=>identity URL being checked default=> notes=>same as checkid_immediate ip?> openid.assoc_handle value=>The assoc_handle from the associate mode. default=> notes=>same as checkid_immediate ip?> openid.return_to value=>URL to return back to default=> notes=>same as checkid_immediate ip?> openid.trust_root value=>URL for user to trust default=> notes=>same as checkid_immediate ip?> <=in out<= openid.mode value=> or notes=>Either for a positive assertion, or to signal that the user has decided to stop the process and return back where they were. In a lot of cases, you won't get a cancel mode: the user will just quit or press back. But if you do, your app should return to what it was doing. In the case of a cancel mode, the rest of the response parameters will be absent. op?> openid.identity value=> notes=>same as checkid_immediate op?> openid.assoc_handle value=> notes=>same as checkid_immediate op?> openid.issued value=> notes=>same as checkid_immediate op?> openid.valid_to value=> notes=>same as checkid_immediate op?> openid.return_to value=> notes=>same as checkid_immediate op?> openid.signed value=> notes=>same as checkid_immediate op?> openid.sig value=> notes=>same as checkid_immediate op?> openid.invalidate_handle value=> notes=>same as checkid_immediate op?> <=out notes=>(None) mode?> check_authentication method=>POST flow=>consumer server consumer format=> des=>Ask server if a message is valid. For dumb (stateless) consumers. notes=> in<= openid.mode value=> default=> notes=> ip?> openid.assoc_handle value=>The assoc_handle from the checkid_* response. default=> notes=>If you knew the secret associated with this assoc_handle, this whole HTTP request would be unnecessary. That's what distinguishes regular mode from dumb mode: knowledge of an assoc_handle. ip?> openid.sig value=>The signature you want to check. default=> notes=>The openid.sig you got back from the checkid_* request. ip?> openid.signed value=>The list of signed items you want to check. default=> notes=>The openid.signed you got back from the checkid_* request. ip?> openid.* value=> default=> notes=>Send all the openid.* response parameters from the openid.signed list which you'd previously gotten back from a "checkid_*" request, with their values being exactly what you got back. ip?> openid.invalidate_handle value=> default=> notes=>(Optional) If you got an "invalidate_handle" response during a checkid_* request, that means the server didn't recognize your assoc_handle (maybe it lost it) and it had to pick its own. Which means you have to fallback to dumb mode, since you don't have the shared secret which the server is using. So while you're doing this check_authentication request, also send along the "invalidate_handle" response from the server, and it'll be checked to see if it actually is missing/bogus (and an attacker isn't just trying to wipe a consumer's cache). ip?> <=in out<= lifetime value=>expiry time in ASCII decimal seconds notes=>0 if invalid signature or signature has expired. Non-zero for the number of seconds the assertion is still valid. op?> invalidate_handle value=>opaque assoc_handle notes=>If present, the consumer should delete from its cache the assoc_handle with the provided invalidate_handle value. op?> <=out notes=>

Servers are required to implement this mode for error recovery and dumb consumers (which can't keep state locally), but it's recommended that you use it as little as possible, as it shouldn't be necessary most the time. It's good for debugging, though, as you develop your consumer library.

mode?> <=body page?>