Configuring a YADIS URL using only Apache

This document describes how to support YADIS service discovery using only Apache configuration. This is desirable because it makes your YADIS identity URL faster and more reliable to use, and it doesn't require running any code on your server.

Quick start

If you are a quick study at configuring Apache, you can get your identity URL configured by:

  1. Create your YADIS services document. If you don't know how, look for documentation on openidenabled.com.
  2. Make sure mod_rewrite, mod_negotiation, and mod_headers are enabled in your server-level configuration, and that RewriteEngine On appears in your server-level (not just .htaccess) configuration.
  3. Download the .htaccess and type map files.
  4. Edit the .htaccess and type map files to match your server configuration. The type map file should be where you want your identity URL to be.

Apache configuration

This configuration is for Apache 2.0. It has not been tested, but this configuration should also work for Apache 1.3 and 2.2.

The configuration for the identity URL is achieved using .htaccess directives using the standard Apache modules mod_negotiation and mod_headers. These directives will also work with some small changes if they are put in a <Directory> section of the server-level configuration. Experiment and have fun.

Server-level configuration

There are minimal server-level requirements for the configuration described here. Most of the configuration is in a .htaccess file. For this configuration to work, you need configuration like the following:

# Change the paths to match your server configuration.
LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so
LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so

RewriteEngine On

mod_negotiation is compiled into most Apache configurations by default, but make sure that it's enabled.

Directory-level configuration

This directory contains the following files:

identity
The mod_negotiation type-map file that instructs the Web server when to return the HTML document and when to return the YADIS services document.
.htaccess
The Apache configuration directives to support YADIS efficiently.
home.html
This document. The content that will be returned for non-YADIS HTTP requests to this URL.
yadis.xml
The YADIS services document.

The following (relative) URLs will be served by Apache:

identity
The identity URL
yadis.xml
The YADIS services document

Adding the X-YADIS-Location header

Adding a header is trivially supported by the Header directive. The scope of the directive is set to only the URL that is to be used as the identity.

The directive is as follows:

<Files identity_url_content_name>
    # Add the X-YADIS-Location header to the response if the response
    # is a 200
    Header onsuccess set X-YADIS-Location http://my-web-site.example.com/yadis.xml
</Files>

At this point, the URL can be used as an identity URL by any compliant relying party, with no further configuration. The content negotiation step is an optimization that will make it faster to use your identity URL, but can be omitted.

If you are using only this configuration, then identity_url_content_name is the file that you want to be served for your identity URL. For other configurations, read on.

Setting the content type for the YADIS services document

The YADIS services document is of type application/xrds+xml. In order for content negotiation to work, the content type must be set correctly in the HTTP response headers. It is also a good idea to set the content type even if you are not using content negotiation so that the client knows how to treat the file in general.

We force apache to use the correct content type with this configuration:

<Files yadis.xml>
    # Make sure that Apache serves the YADIS services document as the
    # correct content-type.
    ForceType application/xrds+xml
</Files>

Using content negotiation

If you are using content negotiation, you will need to create an Apache type-map file indicating the locations of the documents to use when different content-types are requested. There are two requirements for using this system with YADIS. First, any content that is returned that is not a YADIS services document must have the X-YADIS-Location header set. Second, the YADIS services document must be returned with the proper content-type.

Those requirements are taken care of by the preceeding two configuration snippets, so all that is left is to set up the type map.

The simplest usable type map is a file with the following contents:

Content-Type: application/xrds+xml; qs=0.8
URI: yadis.xml

Content-Type: */*; qs=0.9
URI: identity_url_content_name

To use this type-map, you need to put the file in your directory and tell apache that it is a type-map:

<Files type_map_name>
    # Tell apache that this file is a type-map that should be handled
    # by mod_negotiation
    SetHandler type-map
</Files>

In this configuration, identity_url_content_name should be the file that contains the content that you want displayed for regular requests for your identity URL and type_map_name should be the name that you want to use for your identity URL.

There is one more wrinkle. Because the content must be available to Apache and the type-map also points to the same URL internally, there are now two URLs that are usable as identity URLs. This is OK, but to reduce confusion, there is one more configuration trick in the file. This trick makes direct requests for the content URL to redirect to the identity URL:

# Don't allow direct access to the .html file, so that there's no
# confusion about which one is the idenity URL. This redirect means
# that no matter which of the direct content URL or the desired
# identity URL is entered, only the desired identity URL will be used
# by YADIS consumers.
RewriteEngine On

# In order for this test to succeed, RewriteEngine must be turned On
# in the site-wide apache configuration. Unless it is, the SCRIPT_URL
# variable will not be set.
RewriteCond "%{ENV:SCRIPT_URL}" "^server_relative_content_url$"

# Redirect requests for the non-YADIS content to the identity URL.
RewriteRule "^server_relative_content_url$" "server_relative_identity_url" [R,L]

Testing your configuration

You can test your configuration with any tool that will retrieve a URL and show you redirects and headers. We used wget.

Check the content type of your YADIS services document

Retrieve the YADIS services document and make sure that the response has the correct Content-Type header.

$ wget -S http://openidenabled.com/yadis-test/apache/yadis.xml
--14:12:50--  http://openidenabled.com/yadis-test/apache/yadis.xml
           => `yadis.xml'
Resolving openidenabled.com... 67.137.230.70
Connecting to openidenabled.com|67.137.230.70|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Wed, 07 Dec 2005 22:12:50 GMT
  Server: Apache/2.0.54 (Debian GNU/Linux)
  Last-Modified: Wed, 07 Dec 2005 19:46:48 GMT
  ETag: "f0074-130-69a95a00"
  Accept-Ranges: bytes
  Content-Length: 304
  Keep-Alive: timeout=15, max=100
  Connection: Keep-Alive
  Content-Type: application/xrds+xml
Length: 304 [application/xrds+xml]

100%[====================================>] 304           --.--K/s

14:12:50 (10.74 MB/s) - `yadis.xml' saved [304/304]

Make sure that the Content-Type header is set to application/xrds+xml.

Check the presence of the YADIS location header

Retrieve the identity URL and make sure that the header pointing to the YADIS services document is correct.

$ wget -S http://openidenabled.com/yadis-test/apache/identity
--14:16:41--  http://openidenabled.com/yadis-test/apache/identity
           => `identity'
Resolving openidenabled.com... 67.137.230.70
Connecting to openidenabled.com|67.137.230.70|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Wed, 07 Dec 2005 22:16:41 GMT
  Server: Apache/2.0.54 (Debian GNU/Linux)
  Content-Location: home.html
  Vary: negotiate,accept
  TCN: choice
  Last-Modified: Wed, 07 Dec 2005 21:56:26 GMT
  ETag: "f0079-1eeb-39443680;f0077-64-3a93e800"
  Accept-Ranges: bytes
  Content-Length: 7915
  X-YADIS-Location: http://openidenabled.com/yadis-test/apache/yadis.xml
  Keep-Alive: timeout=15, max=100
  Connection: Keep-Alive
  Content-Type: text/html
  Expires: Wed, 07 Dec 2005 22:16:41 GMT
Length: 7,915 (7.7K) [text/html]

100%[====================================>] 7,915         --.--K/s

14:16:41 (85.98 KB/s) - `identity' saved [7915/7915]

Check to see that the X-YADIS-Location header is present and contains the URL to your YADIS services document.

Check the content URL redirect

Retrieve the content URL and look for a redirect to the identity URL.

$ wget -S http://openidenabled.com/yadis-test/apache/home.html
--14:20:35--  http://openidenabled.com/yadis-test/apache/home.html
           => `home.html'
Resolving openidenabled.com... 67.137.230.70
Connecting to openidenabled.com|67.137.230.70|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 302 Found
  Date: Wed, 07 Dec 2005 22:20:35 GMT
  Server: Apache/2.0.54 (Debian GNU/Linux)
  Location: http://openidenabled.com/yadis-test/apache/identity
  Content-Length: 235
  Keep-Alive: timeout=15, max=100
  Connection: Keep-Alive
  Content-Type: text/html; charset=iso-8859-1
Location: http://openidenabled.com/yadis-test/apache/identity [following]
--14:20:35--  http://openidenabled.com/yadis-test/apache/identity
           => `identity'
Reusing existing connection to openidenabled.com:80.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Wed, 07 Dec 2005 22:20:35 GMT
  Server: Apache/2.0.54 (Debian GNU/Linux)
  Content-Location: home.html
  Vary: negotiate,accept
  TCN: choice
  Last-Modified: Wed, 07 Dec 2005 21:56:26 GMT
  ETag: "f0079-1eeb-39443680;f0077-64-3a93e800"
  Accept-Ranges: bytes
  Content-Length: 7915
  X-YADIS-Location: http://openidenabled.com/yadis-test/apache/yadis.xml
  Keep-Alive: timeout=15, max=99
  Connection: Keep-Alive
  Content-Type: text/html
  Expires: Wed, 07 Dec 2005 22:20:35 GMT
Length: 7,915 (7.7K) [text/html]

100%[====================================>] 7,915         40.82K/s

14:20:35 (40.72 KB/s) - `identity' saved [7915/7915]

Check that there is a redirect to your identity URL and that the redirected URL has the YADIS header present.

Check to be sure content negotiation is working

Retrieve the identity URL with an Accept header that indicates a preference for application/xrds+xml.

$ wget --header="Accept: application/xrds+xml; q=1, text/html; q=0.9" \
-S http://openidenabled.com/yadis-test/apache/identity
--14:25:45--  http://openidenabled.com/yadis-test/apache/identity
           => `identity'
Resolving openidenabled.com... 67.137.230.70
Connecting to openidenabled.com|67.137.230.70|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Wed, 07 Dec 2005 22:25:45 GMT
  Server: Apache/2.0.54 (Debian GNU/Linux)
  Content-Location: yadis.xml
  Vary: negotiate,accept
  TCN: choice
  Last-Modified: Wed, 07 Dec 2005 19:46:48 GMT
  ETag: "f0074-130-69a95a00;f0077-64-3a93e800"
  Accept-Ranges: bytes
  Content-Length: 304
  Keep-Alive: timeout=15, max=100
  Connection: Keep-Alive
  Content-Type: application/xrds+xml
  Expires: Wed, 07 Dec 2005 22:25:45 GMT
Length: 304 [application/xrds+xml]

100%[====================================>] 304           --.--K/s

14:25:45 (10.74 MB/s) - `identity' saved [304/304]

Check that the Content-Type is application/xrds+xml. Also look at the contents of the file to make sure that it contains the XRDS file.


Copyright (c) 2005 JanRain, Inc.