OpenID

Group Membership Protocol

It has been suggested that Group Membership be merged into this page. (Discuss)

The following describes a simple protocol for enumerating the members of a group and for determining whether a given identifier is a member of a group. For the purpose of this protocol, a "group" is defined as a set of URIs. These URIs might be OpenID identifier URLs, mailto: URIs representing email accounts, xmpp: URIs representing Jabber IDs or even urn:isbn: URIs representing books.

Potential applications of this include:

  • Public whitelists/blacklists for trusted OpenID identifiers
  • Determining whether two identifiers are "synonymous" by mutual membership of each other's synonym group
  • Machine-readable "friends" lists
  • Other random/innovative applications such as a group of books I've read, identified by urn:isbn: URIs.

This protocol is nowhere near formally defined, and this "specification" is far from exhaustive. However, it is hoped that until such time as a formal specification can be written this simpler description will suffice.

Contents

Design Goals

The design goals of this protocol are:

  • Be as simple as possible. No-one likes a spec that takes hours to grok and implement.
  • Allow others to build upon this simple starting point with higher-level information. For example, a trust service could implement blacklists using this protocol but include additional information about why a particular identifier has been blacklisted as an extension.
  • Make it possible to expose groups that are not enumerable, either because they are private in nature or because they are too large.

Fundamentals

For the purpose of this protocol, a "group" is defined as a set of URIs. The group itself is also identified by a URI. This implies that groups can be members of other groups, though this does not necessarily mean that all members of the nested group are also members of the nestee.

The protocol does not attempt to describe the meaning or purpose of a particular group, it only provides a means to enumerate the members and a means to determine whether or not a particular URI is a member.

Some group endpoints may recursively call other group endpoints when computing their response. A simple TTL and caller mechanism are provided to avoid loops in this case. Endpoints which do not make recursive calls can disregard these values.

The process of determining that a particular URI represents a group uses Yadis discovery, which implies that the URI must be retrievable as per the Yadis spec. In other words, at this time groups must be represented by HTTP or HTTPS URIs. If a future, compatible version of Yadis allows other URI schemes then this protocol will inherit support for them too.

Protocol Steps

Discovery

Given a URI, the first step is to perform Yadis service discovery to firstly determine whether it is a group (i.e. whether it supports the group membership service) and secondly to retrieve the URL of the service endpoint that will actually answer the query.

The Yadis service identifier for the group membership service is http://openid.net/xmlns/groupmembership. The service element contains only URI elements as defined by the XRD format. For example:

<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)"><XRD>
    <Service xmlns:gm="http://openid.net/xmlns/groupmembership/xrds">
        <Type>http://openid.net/xmlns/groupmembership</Type>
        <URI>https://yoursite.example.com/groupmembership</URI>
        <LocalID>http://example.com/users/joe/friends</LocalID>
        <gm:CanEnumerate /><gm:CanQuery />
    </Service>
</XRD></xrds:XRDS>

The URI element gives the base URL of the service endpoint. The LocalID specifies the endpoint's "local name" for this group; if it is not specified, the URL where discovery was performed is the group's local name. The gm:CanEnumerate and gm:CanQuery elements, when present, specify that the endpoint supports enumeration and membership query requests respectively.

All group membership endpoints SHOULD support membership query requests. Endpoints MAY also support enumeration requests; for small, public groups this is recommended so that callers may cache the membership list to reduce hits to the endpoint for future queries.

The endpoint URI obtained during discovery will be used for all of the remaining protocol steps. A client implementation MAY cache the discovered endpoint URI for use over multiple requests for performance reasons. If a request to a cached endpoint returns any HTTP error code, the client MUST invalidate the cache, re-attempt discovery and retry the request at the new endpoint if any. If the endpoint URI has been knowingly changed by the operators of the server, issuing a permanent redirect at the old URI is STRONGLY RECOMMENDED.

Other Discovery Mechanisms

Another specification MAY re-use the group membership protocol described below while providing its own mechanism to discover the endpoint. For example, a more specific application of this protocol might define its own Yadis service URI and directly refer to the group membership endpoint, without the indirection of giving the group a URI of its own. In this case, the discovery mechanism as described in the new specification replaces the above when this protocol is used in conjunction with that new specification.

This suggests that any library implementing this protocol should provide separate API access to both the discovery and the group membership steps in order to facilitate re-use of the latter when using alternative discovery mechanisms. However, this is not a normative requirement.

Group Queries

Queries are made to the endpoint URL after transforming it with the query string argument specified below. The new arguments are appended to the URL using the same rules described in the OpenID authentication specification. In particular, the client must take care to merge the new arguments with any arguments that may already be present in the URI by using an ampersand rather than a question mark to delimit the new arguments.

There are two request modes: enumerate members and query membership. These are distinguished only by the lack of a grpmember.identifier argument in the former.

grpmember.group
The URI of the group to check for membership, as used for Yadis discovery in the previous step
grpmember.identifier
The URI which will potentially be a member of the group, when doing a group membership request
grpmember.ttl
The number of levels of indirection to potentially follow when answering this request
grpmember.caller
The URI of the group making a recursive call. This is optional; when not present, it is assumed to be zero.

This will result in a URL like:

https://example.com/grpendpoint?grpmember.group=http://group.example.com/&grpmember.identifier=urn:isbn:1857988132&grpmember.ttl=5

The client will then perform an HTTP GET request on this URL. A successful query is answered with a 200 OK response in XML format, with a content-type of either application/xml or text/xml. The contents of the response depend on the request mode.

Enumeration Query

For an enumeration query, a successful response is an XML document whose root element has a local name of "Members" and a namespace URI of http://openid.net/xmlns/groupmembership.

The direct children of this element are "Member" elements in the same namespace, each with a "uri" element in the default namespace giving the URI of the member. Child elements other than "Member" must be ignored when processing the list. The member elements may optionally contain other elements in externally-defined XML namespaces as an extension mechanism.

HTTP/1.0 200 OK
Content-type: application/xml

<Members xmlns="http://openid.net/xmlns/groupmembership">
<Member uri="urn:isbn:1857988132" />
<Member uri="urn:isbn:0596000278" />
</Members>

Membership Query

If the server is able to give an answer about the named group identifier, it will return a 200 OK response with content-type application/xml and with an entity body containing a well-formed XML document with a root element in the namespace http://openid.net/xmlns/groupmembership. This element will have a local name of either "Member" or "NonMember", depending on whether or not the URI is a member of the group. The endpoint may also return an element "Undetermined" in the same namespace, to indicate that it was unable to determine membership at this time. For example:

HTTP/1.0 200 OK
Content-type: application/xml

<Member xmlns="http://openid.net/xmlns/groupmembership" />

The use of a compliant XML parser to parse this document is STRONGLY RECOMMENDED.

The XML document MAY contain additional elements in other namespaces within the root element. The meaning of these elements is to be defined by third-parties and extension specifications. However, the expected use is to describe why the URI is or is not a member of the set and perhaps to provide additional data such as a relevance score.

Unsuccessful Responses

If the server is not able to given an answer for the named group or the named identifier, perhaps because it has no knowledge of the given group, it MUST return a 404 Not Found response. Clients MUST follow the behavior described in the previous section when a 404 Not Found response is recieved in response to a query to a cached endpoint URL.

The server MAY return a redirect response. The client MUST then re-try the request at the new URI indicated by the Location header, appending the query arguments as in the original request. In the case of a permanent redirect as defined by the HTTP/1.1 specification, the client MUST update or invalidate the cached endpoint URL that led to the redirect, if any. A temporary redirect is assumed to be transient and so the client MAY retain its existing cached endpoint URI in this case. The client MAY refuse to follow an unreasonable number of redirects for a single request.

The server MAY return a standard 401 Unauthorized response if it is unwilling to service requests without authentication. However, clients MAY support only "anonymous" requests or only particular authentication mechanisms. If the client does provide authentication but the supplied credentials do not provide access to the requested data, the server MAY return a 401 Unauthorized response to indicate that other credentials are able to access the resource, or a 403 Forbidden response to indicate that no credentials will allow access to this data.

The server MAY make requests to one or more other groups to determine its response. In this case, the server MUST decrease the TTL by 1 when making the subsequent request, and MUST NOT make a recursive call when it recieves a TTL of 0. A server also SHOULD NOT make a recursive call to a particular group if it can determine that the optional grpmember.caller value is that group, to avoid "flapping" back and forward between two mutually-recursive endpoints until the TTL expires. Both sending and checking the grpmember.caller value are STRONGLY RECOMMENDED.

Response Caching

TODO: write about caching, referring to the standard HTTP caching rules and relevant headers.

Important Note

This is only a draft and has yet to be tested in any significant implementation. Experimental implementations are encouraged, but use in production applications is not yet advised.