A major problem for a digital citizen
like myself is identity
management. I have 48 accounts listed in my password management file (a flat
file encrypted with PGP, not automated), plus 13 obsolete ones which may or may
not still exist, plus quite a number of non-recorded accounts using a generic
low-security password. Managing these accounts is a hassle. The vendors or
servers with generic accounts are not trusted to do a good job keeping out
thieves, and sooner or later the generic password is going to be revealed.
OpenID is an alternative way to manage your identity. Here is some terminology:
The RP does not have to trust the OP. The EU is the one who trusts the
OP to honestly and securely identify him and report his identity to the RP.
In other words, the EU considers the OP to have adequate security to protect
his identity when working with the particular RP.
The RP is not going to get any assurance from the OP of the EU's real
identity, for example whether his tax identification number is correct.
Thus OpenID is appropriate where the RP needs to be sure that the same EU
appears time after time. For example, the EU stores files on the RP's machine,
and then the EU and nobody else may retrieve them. Or the EU deposits money
with the RP (e.g. a game site) and then incurs charges against this credit
balance.
A bank or an online brokerage needs a real-world identity for the
customer, and will not get it from OpenID. This RP needs to use other
means to assure itself who the customer is, such as a scanned image of a
manual signature under penalty of perjury, which in most jurisdictions is
considered to be proof
of identity. Some nations
provide their citizens with X.509 certificates of identity, which can be
used for this purpose. However, the brokerage RP can be assured via OpenID
that an EU authenticated previously in the real world is still the same
person in future transactions.
The way of using OpenID intended by the developers goes like this:
The EU posts (on a website he has exclusive control of) what is called a delegation document. This is either a generic HTML document containing LINK elements giving the OpenID URL and the URL of the OP, or else a XML document of mime-type application/xrds+xml (historically referred to as a YADIS document) giving the same information (plus other stuff). The XRDS document could specify more than one OP. The EU can edit this document at any time to change to a different OP.
The EU presents the URL of this document to the RP in its login box. He can also put in the OpenID URL directly, from which the server's URL can be inferred. The given URL (converted to canonical form, and with certain exceptions) is the Claimed Identifier by which the EU wants the RP to recognize him.
The RP retrieves the OpenID delegation document and extracts the OpenID and OP URLs.
The RP redirects the EU's browser to the OP server URL with appropriate query parameters including a URL in its own webspace for the final step.
The OP may or may not show a login form to the EU. Authentication technology is entirely up to the OP; e.g. it can demand a certificate, or a Kerberos ticket, or a low-tech password. Normally the EU can choose to cancel the login, and depending on the capabilities of the OP, the EU may be able to authorize returning pre-filled personal data such as a full name, e-mail address, language or country of residence.
On successful authentication the OP redirects the EU's browser back to the URL requested by the RP, appending the Claimed Identifier finally decided on, and pre-filled personal data if authorized. On failure an error code is returned.
All of this should be happening over SSL/TLS, and the interactions are signed with a Diffie-Hellman key, and there is a nonce value, so a replayed or otherwise fraudulent authentication response will be rejected.
The RP receives the results from the OP and knows authoritatively which EU it is dealing with, from the Claimed Identifier.
The Claimed Identifier is the primary key by which the RP should recognize the user. However, it's a fact of life that some RPs require an e-mail address and use this as the primary key, and others are known to cue on the Local_ID, which is definitely not best practice.
I have signed up with StartSSL.com for
a free X.509 identity certificate, which includes OpenID service. Their OP
login page demands this certificate, and authentication is possible only if
the EU's (my) browser can wield the private key whose corresponding public
key was signed to make the certificate. Needless to say, the private key is
encrypted; the key and the password never leave my machine. My OpenID URL
is https://luser.startssl.com/ and the hostname
resolves to the
OP's address, so it can be used also as the server URL.
For the examples in this document I have turned on security by obscurity,
referring to my server as www.example.net
and to my own loginID as
luser
.
There are quite a number of other OpenID OPs including Google, Yahoo!,
AOL, and Wordpress. Of these, Google is the
most professional
. See Google's documentation:
Federated Login for Google Account Users.
You specify
https://www.google.com/accounts/o8/id as the OpenID URL. Download the
content at that URL, which is a YADIS document, and see the URI element
for the server's URL.
Google recommends that the RP's login form should have an icon to fill in
Google's URL and submit the form.
If you have authenticated to your Google account and have a recent cookie,
Google will believe in it; without the cookie Google will ask for your
Google ID and password. Then Google will ask you to confirm logging in,
and will send back your OpenID as they know it, which contains a randomly
generated ID string (always the same for any one EU). If Google sends back
personal data, I haven't figured out how to turn this on.
( Andrew Arnott says: (2010-04-05)
The RP needs to use the AX extension, not SREG, and must require
the
e-mail address.)
Google would prefer that you authenticate to Google first, not giving your Google ID and password when logging in to the RP's site, to avoid phishing: a malicious RP redirects you to their malicious OP which puts up a page simulating your OP (Google), and if you aren't on your toes you will give your credential to the identity thief. Other non-password methods, like the X.509 certificate or Kerberos, similarly resist this phishing attack.
Here's another vulnerability (OpenID-1.1 only): when authentication is finished, the OP redirects the user's browser to the RP, with appended authentication info. If someone snatches this redirection off the wire, he can re-play it later and be admitted. Moral: always use HTTPS. OpenID-2.0 includes a nonce value so each confirmation URL can be used only once, but HTTPS is still recommended.
The Claimed Identifier is the identifier which you, the EU, want the RP to believe is your identity. This identifier may be an XRI (not discussed further here) or a URL (most common). There are three variants for the referent of this URL:
You may specify the URL of the OP's server or a XRDS document
identifying the real server. The OP figures out by other means what the
Claimed Identifier is going to be. Google does it this way, ideally
using the pre-authenticated cookie for your Google ID to know who you are.
Tell the RP https://www.google.com/accounts/o8/id
, same for all
users. The Claimed Identifier will be the server URL with an invariant
code appended.
You may specify the Local_ID assigned to you by the OP, for example
https://luser.startssl.com/
. This URL is required to resolve
to the OP's server or a XRDS document identifying the server.
The problem with both these methods is that your Claimed Identifier is locked to a particular OP, and if you had to change to a different OP, your accounts with all the RPs would become inaccessible. The recommended way to use OpenID is to create a Delegation Document which is either a XRDS or a HTML document that specifies at least the OP server's URL, and should have the Local_ID if the OP requires it. The URL of the delegation document (not the Local_ID) is your Claimed Identifier.
Of course your Claimed Identifier is now locked to the domain hosting your delegation document. It is recommended that this domain name be under your administrative control, i.e. actually owned by you.
The RP passes the Claimed Identifier to the OP, but be aware that the OP can send back a different Claimed Identifier that it is certifying. Google always does this.
I am going to use the third method, posting a delegation document on my own server and using its wild-side URL as my Claimed Identifier (OpenID). Its URL on my system: https://www.example.net:1443/~ooba/openid.rds
For the delegation document, the most flexible is a XRDS (YADIS) document. See this Wikipedia article about YADIS for the format of the YADIS document and the available parameters. It needs to have a mime-type of application/xrds+xml. To make Apache report this mime-type you can append to /etc/mime.types a line
application/xrds+xml rds
where I have arbitrarily picked rds
as the extension for this
type of file. Alternatively you can add to your Apache configuration file
this line in the global scope or the VirtualHost that is going to be serving
the document (assuming mod_mime is loaded):
AddType application/xrds+xml rds
You can also do double indirection, but I'm not sure what the advantage of this is. Create a HTML document at your Claimed Identifier URL and insert (in the head) a line like this giving the URL of the real YADIS document:
<meta http-equiv="X-XRDS-Location" content="http://example.com/yadis.rds" />
You can bypass YADIS entirely by inserting these lines in the head of the HTML document at your Claimed Identifier URL (this is for OpenID-2.0):
<link rel="openid2.provider" href="https://openid.example.com/" />
<link rel="openid2.local_id" href="https://myname.example.com/" />
The first one is the URL of the OP's server, and is required. The second, which some OPs don't require, is the Local_ID which the OP is willing to certify. Some OP servers can infer the Claimed Identifier by other means, e.g. a cookie or a X.509 certificate.
Special cases for the Claimed Identifier:
Since it is a URL, any special characters must be URL-encoded with %hex. Entities &, <, > and " are allowed, though.
A Local_ID must resolve to either the OpenID server or a web site from which a YADIS document can be retrieved that gives the real server.
In theory, if the local_id is specified as http://specs.openid.net/auth/2.0/identifier_select then the OP should let the EU specify the Claimed Identifier, assuming that it is willing to certify more than one identifier for the particular EU. Google always overrides the EU's Claimed Identifier by injecting this value, but does not actually give the user a choice.
For OpenID-1.1 the relations were called openid.server and openid.delegate.
The OpenID that the user provides to the RP is converted to a canonical form, and that becomes the Claimed Identifier (possibly with a fragment appended).
It is recommended that https be used with OpenID, and if the user gives a http scheme, the server should redirect the query to an otherwise equivalent https URL, which becomes the Claimed Identifier.
The Claimed Identifier certified by the OP may have a unique fragment appended (e.g. #qwerty) if the undecorated Claimed Identifier appears (to the OP) to have been used in the past by a different EU.
Before I put my various OpenIDs into production I want to test them, so I can be sure that any failures are not due to defects in the OpenID. What OpenID test sites are available, beyond something I put up myself?
Morten's test page . Outcomes:
In all cases it says Authentication error: not a valid OpenID
,
when given URLs as listed above. Could it be that it's unwilling to
attempt HTTPS?
OSIS I5 OpenID Interop Testing Site. This site is intended for automated testing of OpenID components, focusing on compliance with specific spec items and resisting specific errors. It's going to take a fair investment to learn to use it, and so far I haven't gotten much useful information out of it.
A search on Google reveals a number of other test sites from when OpenID was new (e.g. 2007), but these have been taken down (2012).
It looks like I'm going to have to put together my own RP software to use as a test site.
I need to test my OpenID before putting it into production, and this has turned out to be surprisingly difficult. It looks like I am going to have to set up my own RP for testing. (Note: I have only one server to test this on, and it contains production data, so it is not available to the general public. Sorry.) What software is available?
I'm basing my test program on Net::OpenID::Consumer. This package is notorious for the variety of its dependencies, direct and indirect. Here is a list of what I had to obtain (all from the SuSE Build Service). Don't be surprised if there are other dependencies not on the list: I've done some other web projects and have already installed web-related packages. These are all for OpenSuSE 11.4, either noarch or i586. For i586 substitute x86_64 if your webserver is a 64bit machine.
Links for testing:
URL of my tester: https://www.example.net:1443/~ooba/openid-rp.cgi Since this is a production server, authentication is required before you can even reach this port to do the test. Sorry.) The program asks for all known Simple Registration Extension (SREG) fields, such as the full name and e-mail address.
HTML delegation document: https://www.example.net:1443/~ooba/openid.html
XRDS delegtion document: https://www.example.net:1443/~ooba/openid.rds
Outcomes:
Success, and returns the
full name, nickname, e-mail, country and postcode (zip code, in USA).
Startssl's X.509 identity certificate is required for admission, and
on repeated logins you don't see the StartSSL page as long as the TLS
context is maintained; however, on the initial connection you get their
login page and you need to click on Authenticate
(vs. New
User
) and authorize your browser to send the certificate. There is
no page identifying the RP and asking whether you want to send SREG
fields; it just sends them.
A delegated OpenID, i.e. the URL of the delegation document, is honored with the same outcome as above. In my case the HTML delegation document contains (in its head) these links:
<link rel="openid2.provider" href="https://www.startssl.com/id.ssl">
<link rel="openid2.local_id" href="https://luser.startssl.com/">
A XRDS delegation document is also honored. Here is mine. Notice the
Type element: this specifies version 2.0; the corresponding Type for
version 1.x (deprecated) would be http://openid.net/server/1.0
<?xml version="1.0" encoding="UTF-8"?> <xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0"> <XRD> <Service priority="10"> <Type>http://specs.openid.net/auth/2.0/signon</Type> <URI>https://www.startssl.com/id.ssl</URI> <LocalID>https://luser.startssl.com/</LocalID> </Service> </XRD> </xrds:XRDS>
This OpenID, entered directly in Setup mode, was certified by the OP and the RP (tester) finished the interaction successfully. All the SREG fields configured on the OP were received and readable on the RP. The MyOpenID URL was not tested as part of the delegation documents but it's expected that it would work equally well.
In immediate mode,
it returns The openid.mode argument is not correct
(code bad_mode).
It fails to cause the RP to retry in setup mode, which is what it should
have done if unwilling to use immediate mode.
When started in setup mode, Google displays a confirmation page, then
certifies an identity. It does not offer to send any SREG fields, and
in fact sends none, not even the Gmail address. The claimed identity,
invariant per EU, is
generated arbitrarily by Google from an alphabet apparently of
[-_0-9A-Za-z] (word
characters plus hyphen, 64 possibilities),
for example
https://www.google.com/accounts/o8/id?id=qwERtyiop... (39 random bytes)
Given a delegation document specifying the above URL as the server, the tester redirects to it, but the result is a XRDS document specifying the real server, and the EU's browser downloads and saves it, which is kind of useless.
If the delegation document specifies the server that's in the XRDS document, the tester (in setup mode) will behave just as it would if the XRDS URL had been entered as the OpenID. In particular, the verified OpenID URL will be the one returned by Google with the 39 random bytes, not the URL of the delegation document.
Thus, Google's OpenID is functional on the test site, taking into account Google's idiosyncratic way of using OpenID, which locks the EU into Google as an OP.
To put OpenID into production on my site, I need to actually restrict some content (it's my webmail server, initially), and to do that I need to install and configure an Apache webserver module. For a programmatic web app (versus static pages) an alternative is to hack it to do the OpenID interaction itself, but this is a lot of work, and a pre-made module at the HTTP level is much more attractive. Further, it is common for various mechanisms to be used to authenticate to the same service, e.g. OpenID, X.509 certificates, Kerberos, Shibboleth (SAML), LDAP, etc. etc. To make a laundry list of mechanisms happen requires support in the webserver.
The Apache webserver has available two modules for OpenID authentication. One is called mod_auth_openid. Project website for mod_auth_openid. Project inception is before 2008-05-29 (the date of their oldest release remaining on the download site). I have had a lot of trouble setting it up; it rejects all the OpenIDs I've used to test it, both on my own server and on their test site. Hence I'm trying a different module.
Mod_auth_oid also handles OpenID-2.0 authentication for Apache.
Mod_auth_oid also depends on mod_parp (for parameter parsing).
One of the advantages of mod_auth_oid is that the administrator can configure a map from an arbitrary OpenID (including the mangled kind certified by Google) to a local loginID, whereas with mod_auth_openid the web app needs to handle this.
Here are my experiences installing mod_auth_oid. Unfortunately nobody has made it available on the SuSE Build Service (and I am motivated enough to try it out but not motivated enough to take responsibility for it on the Build Service).
Download page on Sourceforge. (87Kb download.)
The documentation lists the very simple procedure to compile it. The apache2 directory it refers to is under the toplevel source directory. The apxs compilation wrapper is provided by the apache2-devel package (on a SuSE system) and it's called apxs2.
Compile the modules mod_auth_oid, mod_auth_oid_file, mod_parp (one at a time). It installs them in the normal place for modules; in my case it's /usr/lib/apache2. You can also compile mod_auth_oid_ldap if it's relevant for you.
The three modules just compiled must be added to the Apache configuration. On a SuSE system it's /etc/sysconfig/apache2 APACHE_MODULES.
The module verifies the OP's SSL host certificate, and so the CA certificate which is responsible for that cert must be available. Normally it will be included in the installed root certificate collection.
The documented configuration was moderately easy to adapt for my context. See below for details.
Mod_auth_oid appeared to interact successfully with the OP. Upon success it should have redirected the client's browser to the originally requested page, but it actually redirected to either the toplevel index of the virtual host or to the OpenID login page (which was just used to log in), depending on which OpenID was used. I put a lot of effort into debugging this problem, but was never able to get it to work.
I'm providing HTTP authentication to my webmail server, SquirrelMail. Using the login_auth plugin, it honors the REMOTE_USER variable if present, and solicits a loginID and password otherwise. To keep things simple I'm showing only the OpenID and Kerberos mechanisms, though I implemented all the ones listed previously. The Kerberos, LDAP and X.509 mechanisms all work in this framework. Shibboleth would work if I had permission from the IdP.
Here is the directory structure, shown as Locations, i.e. the corresponding directories are directly under /home/httpd/htdocs:
<a href="kerberos/"> Log In With Kerberos </a>
<a href="openid/"> Log In With OpenID </a>
The virtual host includes this configuration (showing mod_auth_oid material only, not Kerberos etc.):
# OpenID authentication for the SquirrelMail directory <Location /squirrelmux/openid> AuthType OpenID Order allow,deny Allow from all Require valid-user </Location> # SSL infrastructure for OpenID SSLProxyEngine on SSLProxyCACertificatePath /etc/ssl/certs # AOID_LogLevel debug [didn't give any useful messages] # OpenID login form # Location (URL) of login handler (required) AOID_LoginPath /oid-forms/login AOID_LoginSuffix .shtml <Location /oid-forms> Options +Includes AddType text/html .shtml AddOutputFilter INCLUDES .shtml </Location> # Static mapping of the admin user (DEBUG) AOID_User https://www.example.net:1443/~ooba/openid.html luser AOID_User http://luser.myopenid.com/ luser # This file maps OpenID to loginID. It is shared among all OpenID # apps. It should be writeable by the Apache user (wwwrun) to enable # administration. '=' separates the OpenID from the loginID. Sample: # http://luser.myopenid.com/=luser <IfModule mod_auth_oid_file.c> AOID_File_DB /home/httpd/data/openid2user.txt AOID_DB_Delimiter = AOID_File_AdminPage /home/httpd/htdocs/oid-forms/file_mapper.html <Location /oid-admin> SetHandler auth-oid-file AuthType OpenID # Lacking AOID_File_UserOnly, this user can edit any map row. Require user luser </Location> <Location /oid-map> SetHandler auth-oid-file AuthType OpenID Require valid-user # User can edit only OIDs mapping to itself AOID_File_UserOnly on # Requires form parameter parsing, mod_parp: SetEnvIf Request_Method GET parp SetEnvIf Request_Method POST parp </Location> </IfModule>
I have now confirmed that my StartSSL.com OpenID actually works, which was the goal of this project.
However, my motivation to work on OpenID was to set up an account on a popular social network site, but to avoid the ridiculous proliferation of account passwords. The site appeared to use OpenID, but when I dug deeper I discovered that this was an illusion: it neither accepts nor provides OpenID service. I was very disappointed, but I finished the OpenID project anyway to learn about the protocol.
I would very much like OpenID to become widely accepted by RPs, but I see several black clouds on its horizon:
The absence of functioning test sites really inhibits EUs from doing OpenID right, because OpenID is not exactly simple, and getting the delegation document right can be a challenge.
The lack of functioning Apache modules for the RP puts a big barrier in the way of applying OpenID authentication to web apps hosted on Apache. I can't believe that the modules never work, but they certainly don't work for me.
Technorati often will own a domain name and a webserver to go with it, in which an OpenID delegation document can be hosted, but think of setting your mother-in-law up with OpenID: the best a typical EU can do is to obtain an OpenID locked to a commercial provider such as AOL. This is really not the way we want to use OpenID. One cure, easy in a fantasy world, is for the EU's nation to define and support a domain name for each citizen, with a small web hosting allowance sufficient for a delegation document.
Along similar lines, important RPs such as banks, brokerages and government agencies need assurance of real-world identity. This level of authentication would happen when the EU registers on the site (opens an account), and after that, OpenID would be sufficient to give the RP confidence that the EU on a subsequent visit is the same one that registered and whose real-world identity is known.
In OpenID the EU picks the OP and makes a judgment that the OP has sufficient security and honesty to certify his identity (and not certify it for hackers). This judgment is of course wishful thinking; the OP is not going to allow the EU to audit its (typically free) service and the EU is unwilling to invest the effort to perform the audit. We need some kind of proxy endorsement of OPs. The model of X.509 root certificate authorities should be noted, good and bad parts both.
Market penetration of OpenID is not sufficient for the EU to justify putting a lot of effort into getting it working. No site whatsoever that I have an account at accepts OpenID, and only one acts as a OP (using their X.509 identity certificate for authentication).
A major barrier to market penetration is that the RP will be liable if an identity thief authenticates as the EU, even if the fault lies with the EU's custody of his credential or with poor security at an unwisely chosen OP. It would help market penetration a lot if RPs could be considered to have done their part for security if they insist that the OP meet a competence criterion such as being certified by some government bureaucracy. A competent OP should use an authentication mechanism that not only resists threats in transit across the net, but makes it hard to successfully hack either endpoint, and which encourages safe custody of the credential by the EU.
The biggest player in OpenID is Google, but they don't accept outside providers or delegation documents, and they totally trample on interoperability: as an OP they won't certify a delegation document, only their own Local_ID, and they ignore SREG, requiring the AX extension for personal information. For shame!
Wikipedia article about XRDS. It has an example of a very complete XRDS document.
Wikipedia article about YADIS. It also has an XRDS example.
Kim Cameron,
The Laws Of Identity
(May 12, 2005, PDF).
A useful discussion of properties that an identity system should have.
It looks like OpenID was inspired by the discussions that went into this
document.