How to Think About OAuth
I’ve been “following” Tim Bray for a very long time. Probably since Chris Sells organized an XML conference at the amazing Skamania Lodge.
He recently wrote down some of his thoughts about OAuth in his blog, ongoing by Tim Bray · How to Think About OAuth. About the same time I was asked by a coworker if LTI used OAuth and that made me think about what we mean by “uses OAuth”.
Yes, LTI uses 2-legged OAuth 1.0; but many people think of the 3-legged variety when they ask about OAuth, and they will probably have to do a little bit of hand coding to support LTI.
Tim makes the point that unlike some standards and specs, OAuth is “a framework not a protocol, it has irritating problems, and it’s really very useful.” He goes on to explain that, “The end-game of an OAuth2 invocation is an ‘Access Token’, just a string of characters.”
That is also the end-game for OAuth 1.0 and it is often called “3-legged OAuth”. But some OAuth users also proposed 2-legged OAuth to digitally sign each request from a Consumer to the Service Provider’s resource endpoint. No access token is created or needed. LTI uses a scheme very similar to the proposal. Unfortunately, the proposal was never adopted by the IETF.
What LTI Really Does
The purpose of each LTI request is to launch a tool or consume a service. LTI Tool Consumers use LTI requests to launch tools provided by LTI Tool Providers. LTI Tool Providers use LTI requests to consume services provided by LTI Tool Consumers (such as the Outcomes service). Because LTI requests go in both directions between the LTI Tool Consumer and LTI Tool Provider, they are both OAuth Consumers and OAuth Service Providers.
Before the first LTI request is sent, both parties agree on a consumer_key and consumer_secret. The consumer_key will be used by both parties to identify who they are talking to. The consumer_secret will be used to digitally sign every request going in either direction.
After both parties agree on the consumer_key and consumer_secret, the rest is very straightforward. For example, when an LTI Tool Consumer wants to launch a tool or resource provided by the LTI Tool Provider, the consumer performs these steps:
- Start forming an HTTP(S) request with basic LTI name/value pairs that mark this request as being an “LTI” request.
- Add more name/value pairs to the request that will help the provider authorize the request.
- Sign the request with a slight variation of the 2-legged OAuth 1.0 specification described here.
- POST the request to the provider.
The provider in turn will perform these steps:
- Receive the request.
- Verify the authenticity of the request using the 2-legged Auth 1.0 specification described here.
- Verify the authorization of the request using the name/value pairs in the request.
- Launch the tool or resource.
2-Legged OAuth with OAuth 2.0
Most .NET OAuth libraries that I come across do not support 2-legged OAuth 1.0. The one exception is OAuth.net, but it is no longer being updated.
Most modern OAuth libraries–including DotNetOpenAuth which is available on NuGet and used in several Visual Student project templates–have been improving their support for OAuth 2.0 not 1.0. Unfortunately, the draft OAuth 2.0 specification does not talk about 2-legged OAuth 2.0; so it is unlikely that modern OAuth libraries will include support for 2-legged OAuth that is compatible with LTI.
[This article does a great job explaining 2-legged OAuth 1.0 and some alternatives in OAuth 2.0.]
* I just ran into a post from one of the authors of DotNetOpenAuth in which he argues that what LTI (and other so-called 2-legged OAuth folks) are using is really 0-legged OAuth (as in zero-legs) because it does not include any of 3 OAuth legs.
Maybe a better answer is no, LTI uses OAuth signatures, but that’s it.
Thanks for writing this, helped me become aware of different oauth1 usage patterns and what LTI uses. Would be nice if you updated this blog, pretty sure LTI v1 does not use oauth legs, a LTI Provider never sends any type of token back to the consumer, just verifies the request was sent by the trusted Consumer, by checking the request was oauth1 signed and contains the correct consumer key and secret.
Thank you for the comment Robert. At the very end of the post I agree with you.