J. Manger
Telstra
T. Lodderstedt
Deutsche Telekom AG
A. Gleditsch
Telenor
February 14, 2017

OpenID Connect Account Porting
draft-account-porting-04

Abstract

This specification defines mechanisms to support a user porting from one OpenID Connect Provider to another, such that relying parties can automatically recognize and verify the change.

Note

Discussions on this draft occur in the OpenID Foundation MODRNA working group.


Table of Contents

1. Introduction

OpenID Connect is a federated identity protocol that allows a user to leverage his or her authentication to an OpenID Connect Provider (OP) to login to Relying Parties (RPs). An RP can (and often will) accept logins via many OPs. The primary identifier for a user at an RP is the combination of a Subject Id ("sub" value) and OP identifier ("iss" value). The "sub" value is only defined to be unambiguous within the context of an "iss" value [OpenID.Core] §5.7. "Claim Stability and Uniqueness". This design deliberately isolates each OP's user population so one OP cannot adversely affect the security of another OP. One consequence, however, is that a user porting from one OP to another appears to an RP as two unrelated users.

This specification extends OpenID Connect to enable an RP to recognize when a user logging in via a New OP is the same user who has previously logged in via an Old OP. That is, it enables porting between OPs to be handled automatically by RPs. The central idea is that the user logs into both Old and New OPs in a single session to establish the link between the user's accounts at each. Subsequently, RPs can interact with both OPs to confirm that link, without needing the user to repeat the dual logins.

OpenID Connect account porting is motivated by (but not limited to) the GSMA Mobile Connect scheme, in which mobile network operators collectively offer federated identity services [MobileConnect]. Most jurisdictions allow users to port their mobile phone number between network operators, and a reasonable expectation of those users will be that their Mobile Connect account (provided by those same operators) can also be ported at the same time. There is a significant difference between phone number and account porting, however. Other parties are generally unaware when number porting occurs; they simply keep using the same phone number to communicate with the user. In contrast, RPs are aware when an OpenID Connect account is ported as they interact with a New OP.

Section 1.2 gives an overview of the message flows during porting. Section 2 defines metadata that indicates an OP supports porting, which can be obtained via [OpenID.Discovery]. Section 3 specifies an API offered by an Old OP that a New OP uses to obtain porting-related details about a user. This API is protected by an OAuth 2.0 exchange to ensure the user consents to releasing the details to the New OP. Section 5 specifies how a New OP can identify the Old OP in an id_token presented to an RP. Section 6 describes how an RP can confirm the porting information with the Old OP. Appendix A provides a example of a complete porting flow.

1.1. Requirements Notation, Conventions, and Terminology

The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].

Throughout this document, values are quoted to indicate that they are to be taken literally in protocol messages.

Terms such as "OpenID Provider" (OP), "Relying Party" (RP), "Subject Id" (sub), and "Sector Id" are defined in [OpenID.Core]. This document uses the terms "Old OP" and "New OP" to indicate OpenID Providers that support users porting out and porting in respectively. An OP can support both roles (and hopefully most will).

1.2. Porting flow

Porting involves a user, their Old OP and New OP, plus any number of RPs. A sample porting flow is described below.

The first stage for a specific porting event is for the New OP to get confirmation from the Old OP that the user wants to port. The New OP does so by calling the Old OP's Porting data API on the user's behalf, which first requires orchestrating an approval interaction between the user and the Old OP using OAuth 2.0 [RFC6749].

  1. The user browsers to the New OP, logs in, and indicates that they want to port from a particular Old OP.
  2. New OP initiates an OAuth 2.0 flow by redirecting the user's browser to the Old OP's authorization endpoint.
    1. The user authenticates to the Old OP, and confirms they want to port to the New OP.
    2. Old OP redirects the user's browser back to the New OP.
    3. New OP completes the OAuth 2.0 flow by getting an access token from the Old OP's token endpoint.
  3. New OP calls the Old OP's Porting data API with the access token; receiving a "port_token".

The second stage of the porting process occurs multiple times: once for each RP. It occurs the first time the user visits a given RP after porting. That is, the first time the user logs in to a given RP via the New OP. A normal OpenID Connect flow occurs, but the New OP includes extra details in the resulting "id_token" to indicate that the user previously used a particular Old OP.

When an RP does not recognize the New OP's Subject Id for the user, the RP can see whether it knows this user under an id from a previous OP. The RP does not blindly trust the New OP to identify the account that was ported. Instead, the RP gets confirmation from the Old OP that the user really did port to the New OP. It is only in that confirmation that the Old OP identifies the ported account, enabling the RP to link the user's ids from both OPs.

  1. The user browsers to the RP, and choses to login via the New OP.
  2. RP initiates an OpenID Connect login by redirecting the user's browser to the New OP's authorization endpoint.
    1. The user authenticates to the New OP, and consents to being identified to the RP.
    2. New OP redirects the user's browser back to the RP.
    3. RP completes the OpenID Connect flow by getting an id_token from the New OP's token endpoint.
  3. New OP included an "aka" member in the id_token listing the user's Old OP, along with an encrypted version of the user's "port_token" obtained in the first stage. Note: the "port_token" is encrypted for the Old OP.
  4. RP prepares to make API calls to the Old OP, by calling the Old OP's token endpoint (with an OAuth 2.0 client credential grant) to get an access token.
  5. RP calls the Old OP's Porting check API with the access token, passing along the encrypted "port_token"; receiving the Old OP's Subject Id for the user in response.
  6. RP automatically accepts the two Subject Ids from Old and New OPs as identifiers for the same account at the RP.

A user could have ported to the Old OP from an even Older OP. That can be indicated by the Old OP including an "aka" member in it's Porting check API response (step 5 above). An RP can automatically process that port in the same way that an "aka" value from the New OP is handled.

The porting flow maintains the privacy benefit offered by the use of pairwise Subject Ids, but still works if an OP uses public Subject Ids.

It is possible for an OP to implement the porting flow without maintaining details of the RPs each user has visited.

2. Discovery

OpenID Discovery offers details about the features supported by an OP [OpenID.Discovery]. The following members MUST be included in the metadata for an Old OP that supports users porting out:

"port_data_endpoint"
Base URL for the Porting data API described in Section 3 that a New OP can use to obtain details about an account that is being ported. The URL MUST use the "https" scheme.
"port_check_endpoint"
Base URL for the Porting check API described in Section 6 that an RP can use to confirm that an account has been ported to a specific New OP. The URL MUST use the "https" scheme.
"port_enc_algs_supported"
Array of string algorithm names [IANA.JOSE] identifying content encryption algorithms supported by the OP that can be used by a New OP to encrypt a "port_token" as described in Section 4.

Below is a sample metadata request and response.

GET /.well-known/openid-configuration HTTP/1.1
Host: oldop.example.net
HTTP/1.1 200 OK
Content-Type: application/json

{
                   "issuer": "https://oldop.example.net/",
   "authorization_endpoint": "https://oldop.example.net/connect/authorize",
           "token_endpoint": "https://oldop.example.net/connect/token",
       "port_data_endpoint": "https://oldop.example.net/connect/port_data",
      "port_check_endpoint": "https://oldop.example.net/connect/port_check",
  "port_enc_algs_supported": [ "A256GCM", "A256CBC-HS512" ],
  …
}

3. Porting data API

The Porting data API (offered by an Old OP and consumed by a New OP) provides a token to represent a porting event. The New OP passes the token (after encrypting it [Section 4]) to each RP when the user subsequently logs into that RP [Section 5]. The RP, in turn, passes the token to the Porting check API [Section 6] to confirm the port with the Old OP.

Access to porting data requires the consent of the user to whom it relates. The Old OP MUST support the OAuth 2.0 authorization code flow for accessing the API [RFC6749]. The Old OP MUST support Bearer access tokens [RFC6750]. The New OP MUST include the scope "port_data" when requesting access.

The API provides a single read-only resource per user. It can be read with a "GET" request. The path "/me" appended to the base URL (from the "port_data_endpoint" member in the Old OP discovery metadata) indicates the resource for the user associated with the access token used to make the request.

The response is a JSON object with a "port_token" member that has a string value. The Old OP needs to be able to identify the user and New OP involved in the porting from the port token. The Old OP MUST choose port token values to have sufficient entropy that they cannot be guessed by other parties, including by New OPs and RPs that have seen other port tokens. All "port_token" values from the Old OP for a given New OP MUST be the same length so the length itself (which is typically preserved by encryption) does not allow correlation across RPs. Consequently, a port token cannot simply be the concatenation of ids for the user and New OP. It could be a combination of the user and New OP ids signed by the Old OP. It could also be a random number used as a reference to porting details recorded by the Old OP.

Any unrecognized members in the response MUST be ignored (such as "extra_stuff" in the example below).

Below is a sample porting data request and response:

GET /connect/port_data/me HTTP/1.1
Host: oldop.example.net
Authorization: Bearer E3yyDiR5_i8CFCVDo3h8T5qgKpAdu8XkGZBv81vn428
HTTP/1.1 200 OK
Content-Type: application/json

{
  "port_token": "3O9YHawMDXLpKb-FVjQ1_qSS9R9wbwb0TWbUxLvqAAI",
  "extra_stuff": 34
}

Only OPs explicitly trusted by the Old OP SHOULD be allowed to use the API (eg to use the "port_data" scope) as it is security and privacy sensitive. In some situations an Old OP will know via other processes when and to whom a user is porting (eg when it is combined with porting a phone number). In such circumstances, the Old OP SHOULD reject requests about a user not known to be in the process of porting, and SHOULD reject requests from any OP other than the known porting destination.

4. Encrypting "port_token"

A "port_token" value received by a New OP is encrypted to create an "enc_port_token" value, which is passed to an RP, then back to the Old OP.

When pairwise Subject Ids are used for enhanced privacy (ie a user is assigned a separate "sub" value for each collection of related RPs identified by a "sector_id"), any porting information revealed to RPs also needs to be at least pairwise so as not to undermine that privacy. This is achieved by using encrypted versions of the "port_token". Encryption is performed by the New OP, and decryption by the Old OP.

A separate encryption MUST be used whenever a different "sub" value is involved. The encryptions MUST involve nonces so each ciphertext is different. The nonces themselves MUST NOT allow extra correlations, which effectively means multiple encryptions of a given "port_token" for multiple RPs MUST NOT be pre-calculated during the porting process if the nonces include a counter or time-based component. Instead, it is safe to perform the encryption for an RP when the user subsequently logs in to that RP.

It is RECOMMENDED that a fresh "enc_port_token" value be calculated each time one is sent so they can have short lifetimes, hence avoiding requirements for revocation.

Encryption offers less privacy benefit when the New OP uses public Subject Ids. However, encryption is still used in such cases to avoid introducing additional options to the porting mechanism.

A "port_token" value is encrypted to create an "enc_port_token" value. The encryption follows the JSON Web Encryption [RFC7516] process, creating a JWE compact serialization. Direct encryption with a shared symmetric key is used ("alg":"dir"), with a key derived from the "client_secret" value that the New OP uses to authenticate to the Old OP. Deriving the key follows section 10.2 "Encryption" of [OpenID.Core]: using the left-most bits of the SHA2 hash of "client_secret". The key id ("kid") is set to the "client_id" value. The content encryption algorithm is chosen from the "port_enc_algs_supported" list published in the Old OP's discovery metadata. The plaintext is the "port_token" value.

The JWE header MUST include a type parameter ("typ") with the value "openid-connect-porting" to explicitly indicate the semantics of the message. The JWE header MUST include a "sector_id" parameter, holding the host component (ie a fully-qualified domain name) of the RP's "sector_identifier_uri" or "redirect_uri" [OpenID.Core] § 8.1. "Pairwise Identifier Algorithm".

Below is a sample JWE, starting with the New OP details:

    client_id: s6BhdRkqt3
client_secret: vNQ4Pe8F9p67dgXMc4c_NxSABb9y15vOkxRmyxCey7U

JWE protected header:

{
        "typ": "openid-connect-porting",
        "alg": "dir",
        "enc": "A256GCM",
        "kid": "s6BhdRkqt3",
  "sector_id": "rp.example.org"
}

Content encryption key (CEK), calculated as SHA-256(client_secret); and random 96-bit nonce (iv); shown in hex and base64url respectively:

          cek: b7b80342942f94b4d873fd4a0e0470fa2b94afe02f94162c34cefdcd1d4b3f58
           iv: 48V1_ALb6US04U3b

The payload is the "port_token" value from the previous section. The additional authenticated data (AAD) is the base64url-encoding of the JWE protected header.

    plaintext: 3O9YHawMDXLpKb-FVjQ1_qSS9R9wbwb0TWbUxLvqAAI
          aad: eyJ0eXAiOiJvcGVuaWQtY29ubmVjdC1wb3J0aW5nIiwiYWxn
               IjoiZGlyIiwiZW5jIjoiQTI1NkdDTSIsImtpZCI6InM2Qmhk
               UmtxdDMiLCJzZWN0b3JfaWQiOiJycC5leGFtcGxlLm9yZyJ9

Applying AES-GCM encryption gives ciphertext and an authentication tag:

    ciphertext: …
           tag: …

[[TO BE COMPLETED: calculate real ciphertext/tag values]]

Putting it all together (note the 2nd field of the JWE is empty as there is no encrypted key when using direct encryption):

"enc_port_token": "eyJ0eX…9yZyJ9..48V1_ALb6US04U3b.ciphertext.tag"

5. Indicating a previous identity: "aka" member

This document defines the "aka" (also known as) member that indicates that the given user has (or had) another identifier from an Old OP. An "aka" member can be included in an id_token (issued as part of an OpenID Connect login), or in a Porting check API response [Section 6].

An "aka" value is a JSON object with "enc_port_token" and "iss" members, both with string values. "iss" identifies the Old OP. "enc_port_token" can be used at the Old OP's Porting check API [Section 6] to confirm the port and obtain the Old OP's "sub" value for the user.

An "enc_port_token" value is opaque to an RP who receives it: they simply copy it from the "aka" member to an API parameter. The OP sending an "enc_port_token" MUST construct its value by encrypting a port token as per Section 4.

On receiving an id_token, a RP uses "iss" & "sub" to lookup the user's account in the RP's systems. If no such account is found, but the id_token contains an "aka" member, then the RP MUST process the "aka" value to determine if the RP knows the user via federation from an Old OP. Processing involves: calling the Porting check API for the Old OP with the given "enc_port_token"; looking for an existing account at the RP that matches the Old OP's "sub" value; and linking that account to the New OP's "sub" value.

Below is a sample payload of an id_token.

{
  "iss": "https://newop.example.net/",
  "sub": "JRrr09BzhZ6BJ9t0yD8DzyZjg7ziB3a40jeoUvZkUgw",
  "aud": "s6BhdRkqt3"
  "iat": 1471238000
  "exp": 1471238300,
  "at_hash": "mrxuWF8TvnILKqFpZgEYCQ",
  "aka": {
    "iss": "https://oldop.example.net/",
    "enc_port_token": "eyJ0eX…3JnIn0..48V1_ALb6US04U3b.ciphertext.tag"
  }
}

6. Porting check API

The Porting check API is offered by an Old OP to RPs so they can confirm that a user did port to a specific New OP. The API converts an "enc_port_token" to a Subject Id ("sub" value), which the RP needs to identify the ported user's existing account at the RP.

Access to the Porting check API requires authentication of the RP. This enables the Old OP to match the caller of the API to uses of the OpenID Connect authentication process so it can apply any RP-specific policy. The Old OP and RP MUST support the OAuth 2.0 client credentials flow [RFC6749] and Bearer access tokens [RFC6750]. The RP MUST include the scope "port_check" when requesting access.

An RP makes a "GET" request to the API endpoint (from the "port_check_endpoint" member in the Old OP discovery metadata), adding query parameters identifying the New OP ("iss") and a porting event ("enc_port_token"). A successful response (eg with a status code of 200) conveys a JSON object that can have "sub", "aka", and "remove" members. A successful response means the user associated with "enc_port_token" has ported from the Old OP to the given New OP. Otherwise an unsuccessful HTTP response status code MUST be returned (eg 400 Bad request).

The Old OP makes the following checks:

A successful response is a JSON object that can include the following members (any unrecognized members MUST be ignored):

"sub"
Old OP's Subject Id for the ported user for the RP, as a string. This member MAY be absent if the Old OP knows the user never logged in to this RP.
"aka"
Another identifier for the same user, with the same syntax and semantics as when it appears in an id_token [Section 5]. It indicates that the user ported to the Old OP from an even older OP.
"remove"
A boolean value (false or true) indicating whether or not the RP can expect future logins from the user via the Old OP.

When the RP does not recognize the "sub" value (in combination with the Old OP's "iss" value), the RP SHOULD process the "aka" value if present as per Section 5 to determine if the RP knows the user via a login from the even older OP. An RP MUST NOT recursively follow "aka" values indefinitely; a limit of a few recursions is likely to be sufficient for valid use cases.

"remove": true — means the RP MUST no longer accept a login from the Old OP with the given "sub" as valid for this user's account at the RP. Future logins will be via the New OP.

"remove": false — means the RP can expect and SHOULD continue to accept logins to this account via the Old OP, in addition to logins via the New OP. It indicates that logins from the Old OP and New OP are linked to the same user, but not that one is being replaced by the other.

The Old OP MUST include the "remove" member when "sub" is present.

Below is a sample request with a successful response. [The long request line is wrapped over 4 lines just for display purposes.] The RP would lookup "sub=kY6N6…" & "iss=https://oldop…" in its user database to find the existing account that has ported to the New OP.

GET /connect/port_check?
  iss=https://newop.example.net/&
  enc_port_token=eyJ0eX…3JnIn0..48V1_ALb6US04U3b.ciphertext.tag
   HTTP/1.1
Host: oldop.example.net
Authorization: Bearer JOpIXW60qfjKQOlQ8trLBTSzwDl8zGgujLxBVTvWMG8
HTTP/1.1 200 OK
Content-Type: application/json

{
     "sub": "kY6N64H86MBTho9I-JlRnpvaLOwAb5m3yYta6pq1VFM",
  "remove": true
}

Below is a sample response that identifies a previous port. If the RP did not recognize "sub=kY6N6…" & "iss=https://oldop…" it can process the "aka" value to see if that leads back to an account known to the RP.

HTTP/1.1 200 OK
Content-Type: application/json

{
     "sub": "kY6N64H86MBTho9I-JlRnpvaLOwAb5m3yYta6pq1VFM",
  "remove": false,
     "aka": {
               "iss": "https://anotherop.example.net/",
    "enc_port_token": "QSfmp2V30cNqKShhyOjQEwDaQzLNhji5w5nD-EZq3ok"
  }
}

Below is a sample unsuccessful response (which happens to use the Problem Details format specified in [RFC7807]).

HTTP/1.1 400 Bad request
Content-Type: application/problem+json

{
   "type": "https://oldop.example.net/probs/porting_rp",
  "title": "Port_token presented by the wrong RP"
}

7. "user_ported" error code

An Old OP MAY return the "user_ported" error code from the authorization endpoint during an OpenID Connect login flow when the user has ported to a New OP. An RP receiving this error code SHOULD remove any cached hints linking the user to the Old OP (eg a cookie), and SHOULD perform its OP discovery process (for instance, by prompting the user to select an OP, invoking a discovery service, or triggering an account chooser).

The New OP to whom the user ported is not identified in the error response for privacy reasons. It avoids revealing this information before the Old OP has authenticated the RP, and confirmed the user has logged into the RP via the New OP.

8. Security Considerations

Attacker's goal: get access to the victim's RP accounts either by taking over the respective OP or RP account. This section analyses the protocol flow along the lines of the migration process for threat vectors. Note: this list is a starting point and not meant to be comprehensive (yet).

8.1. Impersonation of legitimate user's old OP account

If the attacker can get hold of the authorization grant issued by the old OP for access to the account migration API, the attacker can impersonate the legitimate user in the migration process at the new OP.

8.2. Trick user into login with attacker's account

The attacker could try to cause the legitimate user to login with her new OP with the attacker's account with this new OP. That would cause the new OP to associate the migration data with the attackers account, which in turn allows the attacker to migrate all RP accounts of the legitimate user to his account with the new OP.

8.3. Impersonate old OP/replay migration data

If the attacker gets hold of valid migration data of an account at OP1 and "migrates" the account data to his account with OP1. Scenario: The attacker logs into the new OP with her OP account and sends the new OP to the OP under her control. This OP returns migration data obtained from a legitimate OP in a previous step.

8.4. Bad OP

Attacker could try to trick victim into (unintentionally) migration her OP account to a OP under the attacker's control

8.5. Inject migration data during RP account migration

Attacker gets hold of valid migration data and injects it into the login response. This would only work for implicit grant, not for grant type code.

8.6. Further Considerations

Note: the new OP could also accidentally assign the account migration data to the wrong user account in its database. Care must be taken that the respective functions are thoroughly tested with every change.

9. Privacy Considerations

TBD

10. IANA Considerations

This document registers the "aka" claim in the IANA "JSON Web Token Claims" registry [IANA.JWT_Claims].

This document registers the "sector_id" claim in the IANA "JSON Web Signature and Encryption Header Parameters" registry [IANA.JOSE].

This document registers the "user_ported" error code in the IANA "OAuth Extensions Error Registry" [IANA.OAUTH].

This document registers the "application/openid-connect-porting" media type in the IANA "Media Types" registry [IANA.MEDIA]. It identifies a JSON Web Encryption (JWE) compact serialization that is the encryption of a port token.

11. References

11.1. Normative References

[OpenID.Core] Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., Mortimore, C. and E. Jay, "OpenID Connect Core 1.0", December 2013.
[OpenID.Discovery] Sakimura, N., Bradley, J., Jones, M. and E. Jay, "OpenID Connect Discovery 1.0", August 2015.
[IANA.JWT_Claims] IANA, "JSON Web Token Claims", January 2015.
[IANA.JOSE] IANA, "JSON Object Signing and Encryption (JOSE)", January 2015.
[IANA.OAUTH] IANA, "OAuth Parameters", July 2012.
[IANA.MEDIA] IANA, "Media Types", November 2016.
[RFC7516] Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)", RFC 7516, DOI 10.17487/RFC7516, May 2015.
[RFC6749] Hardt, D., "The OAuth 2.0 Authorization Framework", RFC 6749, DOI 10.17487/RFC6749, October 2012.
[RFC6750] Jones, M. and D. Hardt, "The OAuth 2.0 Authorization Framework: Bearer Token Usage", RFC 6750, DOI 10.17487/RFC6750, October 2012.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997.

11.2. Informative References

[MobileConnect] GSMA, "Mobile Connect", January 2015.
[RFC7807] Nottingham, M. and E. Wilde, "Problem Details for HTTP APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016.

Appendix A. Example flow

This example involves a user (Alice), an RP, and 3 OPs (OP1, OP2, and OP3). Alice originally federated to the RP via OP1; ported to OP2; then (shortly afterwards) ported to OP3. When Alice logs in to RP via OP3 for the first time she is not recognized directly from OP3's id_token. But by following the "aka" trail, the RP automatically establishes the link to an id from OP1 that the RP recognizes as Alice.

Many details are elided. Dotted arrows represent HTTP redirects.


Appendix B. Acknowledgements

The following people in particular have contributed to the development of this specification: Jörg Connotte; John Bradley; Axel Nennker.

Appendix C. Notices

Copyright (c) 2016 The OpenID Foundation.

The OpenID Foundation (OIDF) grants to any Contributor, developer, implementer, or other interested party a non-exclusive, royalty free, worldwide copyright license to reproduce, prepare derivative works from, distribute, perform and display, this Implementers Draft or Final Specification solely for the purposes of (i) developing specifications, and (ii) implementing Implementers Drafts and Final Specifications based on such documents, provided that attribution be made to the OIDF as the source of the material, but that such attribution does not indicate an endorsement by the OIDF.

The technology described in this specification was made available from contributions from various sources, including members of the OpenID Foundation and others. Although the OpenID Foundation has taken steps to help ensure that the technology is available for distribution, it takes no position regarding the validity or scope of any intellectual property or other rights that might be claimed to pertain to the implementation or use of the technology described in this specification or the extent to which any license under such rights might or might not be available; neither does it represent that it has made any independent effort to identify any such rights. The OpenID Foundation and the contributors to this specification make no (and hereby expressly disclaim any) warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to this specification, and the entire risk as to implementing this specification is assumed by the implementer. The OpenID Intellectual Property Rights policy requires contributors to offer a patent promise not to assert certain patent claims against other contributors and against implementers. The OpenID Foundation invites any interested party to bring to its attention any copyrights, patents, patent applications, or other proprietary rights that may cover technology that may be required to practice this specification.

Appendix D. Document history

[[ To be removed from the final specification ]]

-04

-03

-02

-01

-00

Authors' Addresses

James Manger Telstra EMail: james.h.manger@team.telstra.com
Torsten Lodderstedt Deutsche Telekom AG EMail: t.lodderstedt@telekom.de
Arne Georg Gleditsch Telenor EMail: argggh@telenordigital.com