OAuth enables a very simple type of delegation, a user can delegate permissions between two services that they have accounts on. In other words, OAuth lets a user delegate permission to themself. But full delegation allows arbitrary users of arbitrary services to give permissions to each other. In this article I summarize the two key extensions to OAuth needed to enable it to do full delegation. The first is ’on behalf of’ (e.g. a service saying ”I am making this request on behalf of user X”) and the second is a very simple directory service. The rest of the article tries to use something like plain English to explain how these features could work in OAuth.
2 The first step on the road to general delegation - target user
3 The next step on the road to general delegation - on behalf of
4 Making on behalf of actually work - discovery
5 Another nice feature of discovery
A Some odds and ends
B How exactly did Live calendar provision with Yahoo calendar?
B.1 Establishing a secure communication channel
B.2 A shared application protocol
C How did Yochi’s discovery server come to list Yahoo calendar as Yochi’s calendar service?
D And why was Live calendar allowed to discover who Yochi’s calendar service is anyway?
E What if Leon wanted to ask Yochi for permission to see his free/busy time?
F Where can I go to learn more? (Read: I’m having trouble sleeping)
Delegation is all about transferring permissions. At its most general a delegation expresses something like ”User X of service Y gives permission Z to user A of service B”. Today OAuth cannot express this statement because OAuth always assumes that X = A. In other words what OAuth can say is ”The user of service Y gives permission Z to their own account on service B.”
With general delegation a user of Sharepoint Online can give permission to a user of Google Docs to see the Sharepoint Online user’s documents from inside of Google Docs. Or, as explored below, a user of Yahoo calendar can give permission to a user of Live calendar to see the Yahoo calendar user’s free/busy time from inside of Live calendar. With full delegation we can finally start having truly open permissions.
Imagine that we added one feature to OAuth, the ability in a permission request to specify who the target user is. This would enable us to make the following statement in OAuth ”The user of service Y gives permission Z to user A of service B”. But how do we identify user A? We already have a pretty good solution to how to identify users, e-mail addresses. So let’s go with that.
But now imagine the user experience. Yochi, who uses Yahoo Calendar, wants to give Leon, who uses Live Calendar, permission to see his free/busy time. Using the new ’target user’ functionality Yochi can say to Yahoo Calendar ”Hey, Yahoo Calendar, give email@example.com who uses Live Calendar the right to see my free/busy time.” Yahoo calendar can now make a three legged OAuth request to Live Calendar and specify that the permission to be granted, free/busy time read capability, is to be given to the user firstname.lastname@example.org.
But how does Live Calendar know who is giving the permission? In other words, how does Live Calendar know that Yochi is the one granting the permission to Leon? If all we add is Target User then what will have to happen is that Yochi will have to be sent to a browser pointed at Live Calendar who will then require Yochi to prove his identity. Personally I think that oauth can handle that part just fine but let’s just say we use OpenID. So now Yochi has to login to Live Calendar via OpenID to prove to Live Calendar who he is.
The good news is that all the mechanics needed to make the previous work exists. This is important because it will take a while for the machinery I describe below to be ubiquitous so this is a reasonable fall back experience. But we can do better, much better.
In the previous section we added to OAuth the ability to specify the user who is the target of a request using e-mail. What if we could also specify the identity of the user the request is on behalf of? In other words, imagine we add an ’OnBehalfOf’ field to OAuth? Now we have reached the fully generic semantics we talked about above. We can make a statement of the form ”Yahoo Calendar, on behalf of Yochi@bogus.example.org, gives permission to see his free/busy time to email@example.com using the service Live Calendar.” The beauty of the previous statement is that Yochi no longer needs to login to Live calendar to prove his identity. So long as Live calendar believes that Yahoo calendar is authorized to act on behalf of Yochi the entire permissioning step can be handled with no additional UX.
But this begs the question, why should Live calendar believe that Yahoo calendar has the right to speak on Yochi’s behalf?
There are a number of ways that Live calendar can figure out if Yahoo calendar is allowed to speak on behalf of Yochi. But a fairly straight forward way that turns out to have some really nice capabilities (which I’ll get to in the next section) is a discovery server. Imagine if Live calendar could take Yochi’s e-mail address, Yochi@bogus.example.org and make a GET request of the form https://bogus.example.org/.well-known/finger-service?user=Yochi@bogus.example.org&service=URN:Services:calendar and get back the URL for Yochi’s calendar service? Notice that as directories go this is a fairly tame one. Queries consist of two fields (a user e-mail and a service ID) and the response is one or more URIs. In this case if the URI returned to the query points at Yahoo calendar than Live Calendar knows that Yahoo Calendar really is authorized to make statements on Yochi’s behalf, at least in regards to calendaring.
The calendar scenario is predicated on Yochi being able to specify that Leon’s calendar is at Live Calendar. But how did Yochi know that? Did he ask Leon where he keeps his calendar and then type in some URL pointing at Live Calendar into Yahoo Calendar? Did he pick from a drop down? Ideally Yochi could just say to Yahoo calendar ”Hey, my friend Leon’s e-mail address is firstname.lastname@example.org, give him permission to see my free/busy time” and then Yahoo calendar would handle the rest. Thanks to the discovery service that’s possible.
Yahoo calendar can go to https://email@example.com&service=URN:Services:calendar and get back the URL for Leon’s calendar service, in this case, Live calendar. And then Yahoo can make an on-behalf of request and set up the permission. So Yochi didn’t need to know where Leon’s calendar service was at. He just needed to know Leon’s e-mail address.
The goal was to enable generic delegation so that any user of any service can give permissions to any user of any other service. To make this happen we needed to introduce two key features. First was on behalf of (and the related target user). Doing this required little more than adding two fields to OAuth requests along with the idea that users are identified by e-mail addresses. Second was discovery. By enabling a simple discovery server (two strings in, a list of URIs out) we both enable users to find other user’s services as well as allow services to figure out which services are allowed to act on behalf of which other services.
These two features give us the foundation for open permissions on the Internet.
My goal with the previous article was to provide a very high level overview of how full delegation could work in OAuth. In this appendix I provide a similar high level overview of a number of ancillary features needed to fill out the experience.
In most OAuth contexts there is an assumption that two services have provisioned with each other out of band and that’s why they can securely talk to each other. But requiring everyone to provision out of band with everyone else is not a recipe for a scalable web. What I think we want is the ability for any two arbitrary services to be able to provision a relationship dynamically. There are two parts to provisioning from a technical perspective. One part is establishing a secure communication channel. The other part is establishing a shared application protocol.
Establishing a secure communication channel generally means creating a mutually authenticated connection, that is, a connection where both ends have authenticated each other. Public key cryptography would be a great technology to use here were it not that most of the languages/platforms used on the Internet do not have good public key support. So instead I propose that we use SSL (ironically built on public key cryptography but in a sufficiently low level way that there exist high quality wide spread support) to establish a shared symmetric key between two services. This key can then be used to sign things like request/access tokens (thus authenticating the sender) over a SSL connection (thus authenticated the receiver).
Establishing the shared symmetric key requires two round trips. For example. let’s say that Yahoo Calendar wants to provision with Live Calendar. Note that this provisioning would only need to happen once. The symmetric key, once established, could be used with all on behalf of requests between the services independent of the users involved. In other words the symmetric key is provisioned between the services directly.
- Yahoo calendar (after using discovery to find the Live calendar provisioning endpoint) would send a HTTPS request to Live calendar’s provisioning endpoint including a cryptographically secure random number.
- Live calendar (after using discovery to confirm the Yahoo calendar provisioning endpoint) would send a HTTPS request to Yahoo calendar’s provisioning endpoint including both the cryptographically secure random number and the symmetric key that is to be used between the services.
That’s it. The two services have now securely established a shared key that can be used to sign tokens and prove identity. This same mechanism can also be used to generate new keys when it’s time to do a key rollover.
As for the second part, the shared application protocol, the Internet is full of those. Are we talking about a file or photo sharing service? WebDAV. Are we talking about a content management system? Delta-V. calendar? CalDAV. You have a problem? DAV has a protocol. :)
In the scenario Yochi’s discovery service is at bogus.example.com but Yochi’s calendaring service is at Yahoo. How did Yahoo tell bogus.example.com to list Yahoo Calendar as Yochi’s discovery service? Making this work is really just an application of bog standard OAuth. After setting up his Yahoo Calendar, the Yahoo Calendar service would redirect Yochi with a standard OAuth delegation request to Yochi’s discovery service to set itself as Yochi’s calendaring service location. Yochi would confirm to his discovery service that he agrees (again, this is bog standard OAuth) and the change would be made.
It’s not a good idea to have discovery services sharing information about user’s services to just anyone. So in our scenario the discovery service should have demanded authentication from Live. But it seems doubtful that Yochi would have thought to give Live permission to see his discovery data. What is more likely is that Yochi set up his discovery service to allow anyone in his address book to see his service locations and Leon is in his address book. So what should happen is that Live should make an ’on behalf of’ request to Yochi’s discovery server in Leon’s name. But how does Yochi’s discovery server know that Live Calendar is allowed to act on behalf of Leon?
The answer is that Yochi’s discovery service will make a request to Leon’s discovery service on behalf of Yochi to find out Leon’s calendar service and see if it’s Live Calendar. Leon’s discovery service can easily validate that Yochi’s discovery service is allowed to act on Yochi’s behalf by matching Yochi’s e-mail address to the discovery service location (e.g. they are both bogus.example.org) so it knows everything is o.k. Since Yochi is in Leon’s address book, Leon’s discovery service will respond to the request confirming that Live is allowed to act on behalf of Leon. And now the scenario is complete. Yochi’s discovery service knows that Live can act on behalf of Leon (at least in this context) and since Leon is in Yochi’s address book the request for calendar location information from Live is positively responded to.
This article has worked on the scenario where Yochi wants to inform Leon that he has been granted a permission. But a more general case is that Leon realizes he needs access to Yochi’s free/busy time. This just requires adding a command to OAuth ”asking for permission” whose semantics are ”User X of service Y asks for permission Z from user A of service B”. But all the details of discovery, permissioning, etc. are the same as above. Once user A reviews the request and approves it then the process of notifying User X that they have received the permission is exactly as given above.
I’ve tried my best to keep this article focused on the big picture and in something reasonably approximate to English. But I’ve written a series of articles going into the gory details of how all this can be made to work. Those articles are:
- Open Permissions Matter for an Open Web
- An argument for why open permissions matter and an exploration of the components needed to make them happen.
- The Outline of a Profile for Granting Permissions Using OAuth WRAP
- Provides a sequence diagram and supporting material explaining how the basic protocol exchanges would work.
- Thoughts on Building a Finger Service
- What I call a discovery service in this article was more traditionally called a finger service. This article explores key issues in designing a dirt simple finger service.
- Using OAuth WRAP and Finger for Ad-Hoc User Authentication
- This is where I first introduce how to do ad-hoc provisioning (e.g. using two request/response pairs to establish a shared symmetric key). But I do it in the context of showing how OAuth can be used as a replacement for OpenID v1.
- Thoughts on Updating Finger Services
- All the gory details on how services can update user’s discovery services.