social plumbing pycon 2010
TRANSCRIPT
-
8/14/2019 Social Plumbing PyCon 2010
1/216
Open Stack: Hacking theSocial Web with Python
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
2/216
Who are you?
Joe Stump Mike Malone
@mjmalone@joestump
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
3/216
A special thanks to ...
David Recordon
@daveman692
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
4/216
Reduce HTTP Requests
Bundle JavaScript and CSSUse sprites for imagesReduce images / outside objects
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
5/216
Introductions
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
6/216
Open data is
increasingly importantas services move
online. Tim Oreilly (OSCON, 2007)
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
7/216
65% of the worldwideinternet audience
visited at least one
social network lastmonth. comScore
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
8/216
Rules of Web 2.0
1. The perpetual beta becomes a process forengaging customers.
2. Share and share-alike data, reusing others andproviding APIs to your own.
3. Ignore the distinction between client and server.
4. On the net, open APIs and standard protocols win.5. Lock-in comes from data accrual, owning a
namespace, or non-standard formats.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
9/216
Rules of Web 2.0
1. The perpetual beta becomes a process forengaging customers.
2. Share and share-alike data, reusing others andproviding APIs to your own.
3. Ignore the distinction between client and server.
4. On the net, open APIs and standard protocols win.5. Lock-in comes from data accrual, owning a
namespace, or non-standard formats.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
10/216
www
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
11/216
Web 2.0
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
12/216
?
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
13/216
c:\
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
14/216
http://
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
15/216
The rise of the API
Websites started integrating data from
other websites
Entire businesses were built on top of APIs
Websites like to be social too
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
16/216
http://
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
17/216
http://xkcd.com/256/
Thursday, 18 February 2010
http://blogs.forrester.com/groundswell/2008/03/the-future-of-s.htmlhttp://blogs.forrester.com/groundswell/2008/03/the-future-of-s.html -
8/14/2019 Social Plumbing PyCon 2010
18/216
Social networks willbe like air. Charlene Li, Forrester
http://blogs.forrester.com/groundswell/2008/03/the-future-of-s.html
Thursday, 18 February 2010
http://blogs.forrester.com/groundswell/2008/03/the-future-of-s.htmlhttp://blogs.forrester.com/groundswell/2008/03/the-future-of-s.html -
8/14/2019 Social Plumbing PyCon 2010
19/216
Each API had its own mechanisms forauthentication
Each API had its own data formats
Few, if any, APIs had the ability to delegateaccess to third parties
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
20/216
http://
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
21/216
I have anidea!
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
22/216
Identity Relationships Content+Activity
Profiles
Accounts
Friends
FollowersColleagues
Photos
BlogsPokes
Favorites
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
23/216
Identity Relationships Activity
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
24/216
The open stackis a set of technologies that work togetherto democratize and decentralize key pieces of the social
web.Open stack technologies allow users to share their identity,relationships, activity, and content across applicationsand platforms.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
25/216
Great! How the helldoes it work?
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
26/216
Cooperation
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
27/216
The nice thing aboutstandards is that you
have so many to
choose from. Andrew S. Tanenbaum
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
28/216
OAuth OpenID WebFinger
FOAF XFN Portable Contacts
LRDD XRD-Simple Yadis
microformats oEmbed SalmonAtom Activity Streams PubSubHubBub
DiSo oExchange JSON
RDF RSS XML
XMPP CSS
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
29/216
The open stack for the socialweb is built entirely on HTTP.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
30/216
HTTP urllib, urllib2, httplib, httplib2
JSON simplejson, json-py, cjson
XMLElementTree, xml.dom, xml.dom.minidom,
xml.sax, lxml, simplexml
Atom FeedParser
HTML BeautifulSoup, html5lib
URI cgi, urllib, urlparse
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
31/216
CryptographicSignatures for Dummies
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
32/216
Alice wants to make sure a message, deliveredover an insecure channel, came from Bob. Bob
and Alice get together and come up with ashared secret. They randomly select
crocodile as their shared secret.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
33/216
>>> key = 'crocodile'>>> message = 'Hi, Alice. You rock!'>>> import hashlib>>> import hmac>>> hashed = hmac.new(key, message, hashlib.sha1).hexdigest()>>> '&'.join((message, hashed))'Hi, Alice. You rock!&d2810dea6e29258ff89bbd6ec7c7ba21b63a8b32'
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
34/216
>>> key = 'crocodile'>>> message = 'Hi, Alice. You rock!'>>> import hashlib>>> import hmac>>> hashed = hmac.new(key, message, hashlib.sha1).hexdigest()>>> '&'.join((message, hashed))'Hi, Alice. You rock!&d2810dea6e29258ff89bbd6ec7c7ba21b63a8b32'
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
35/216
>>> key = 'crocodile'>>> message = 'Hi, Alice. You rock!'>>> import hashlib>>> import hmac>>> hashed = hmac.new(key, message, hashlib.sha1).hexdigest()>>> '&'.join((message, hashed))'Hi, Alice. You rock!&d2810dea6e29258ff89bbd6ec7c7ba21b63a8b32'
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
36/216
>>> key = 'crocodile'>>> message = 'Hi, Alice. You rock!'>>> import hashlib>>> import hmac>>> hashed = hmac.new(key, message, hashlib.sha1).hexdigest()>>> '&'.join((message, hashed))'Hi, Alice. You rock!&d2810dea6e29258ff89bbd6ec7c7ba21b63a8b32'
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
37/216
>>> message, hash = received_message.split('&')>>> print messageHi, Alice. You rock!>>> check_hash = hmac.new(key, message, hashlib.sha1).hexdigest()>>> assert hash == check_hash>>>
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
38/216
>>> message, hash = received_message.split('&')
>>> print messageHi, Alice. You rock!>>> check_hash = hmac.new(key, message, hashlib.sha1).hexdigest()>>> assert hash == check_hash>>>
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
39/216
>>> message, hash = received_message.split('&')
>>> print messageHi, Alice. You rock!>>> check_hash = hmac.new(key, message, hashlib.sha1).hexdigest()>>> assert hash == check_hash>>>
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
40/216
>>> message, hash = received_message.split('&')>>> print message
'Hi, Alice. You stink!'>>> check_hash = hmac.new(key, message, hashlib.sha1).hexdigest()>>> assert hash == check_hashTraceback (most recent call last):File "", line 1, in
AssertionError>>>
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
41/216
>>> message, hash = received_message.split('&')>>> print message
'Hi, Alice. You stink!'>>> check_hash = hmac.new(key, message, hashlib.sha1).hexdigest()>>> assert hash == check_hashTraceback (most recent call last):File "", line 1, in
AssertionError>>>
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
42/216
Identity
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
43/216
A decentralized mechanism
for single sign-on
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
44/216
OpenID was created by Brad Fitzpatrick and
David Recordon while working at Six Apart.Their goal was to create a decentralizedauthentication mechanism for blog comments.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
45/216
Problems Addressed
Allows a user to sign into multiple websites
with a single password. Centralized identity removes potential land
grabs for per-site usernames.
Profile duplication can be avoided if theOpenID provider supports it.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
46/216
Why do I care?
Well over 500,000,000 accounts fromAOL, Google, Yahoo!, MySpace, Facebook,
and others are OpenID enabled.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
47/216
Terminology
An IDENTIFIER is an HTTP URI or an XRI.
A RELYING PARTY (RP) as an applicationwishing to identify a user or proveownership of an identity.
An OPENID PROVIDER (OP) is a serverthat the RP relies on to assert identity of agiven user.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
48/216
Hows it work?
An OpenID is just a URL. My OpenID ishttp://stu.mp.
You claim an OpenID (e.g. login orregister) with relying party.
The relying party relies on the providerto authenticate the user.
Thursday, 18 February 2010
http://stu.mp/http://stu.mp/ -
8/14/2019 Social Plumbing PyCon 2010
49/216
1. USER identifies themselves to a RELYING
PARTY using their OpenID
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
50/216
1. USER identifies themselves to a RELYING
PARTY using their OpenID
http://stu.mp
Thursday, 18 February 2010
http://stu.mp/http://stu.mp/ -
8/14/2019 Social Plumbing PyCon 2010
51/216
OpenID Example
Hi, Im Mike Malone. I think Ruby rules!
2. RELYING PARTY discovers OPENIDPROVIDER
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
52/216
OpenID Example
Hi, Im Mike Malone. I think Ruby rules!
2. RELYING PARTY discovers OPENIDPROVIDER
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
53/216
OpenID Example
Hi, Im Mike Malone. I think Ruby rules!
2. RELYING PARTY discovers OPENIDPROVIDER
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
54/216
OpenID Example
Hi, Im Mike Malone. I think Ruby rules!
2. RELYING PARTY discovers OPENIDPROVIDER
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
55/216
Yadis Discovery
A protocol for discovering services
associated with a Yadis ID, which is a URLor and XRI i-name.
Result of discovery is an XRDS Capabilitiesdocument.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
56/216
You have 3 options
1. Using the custom HTTP response headerX-XRDS-Location
2. Using the HTML meta tag
3. Using content negotiation in HTTPrequests with Accept: application/
xrds+xml
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
57/216
$ curl -I -H "Accept: application/xrds+xml" www.yahoo.com
HTTP/1.1 200 OKDate: Mon, 15 Feb 2010 17:09:24 GMT
P3P: policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM..."
Vary: Accept,Accept-Encoding
Cache-Control: private
X-XRDS-Location: http://open.login.yahooapis.com/openid20/www.yahoo.com/xrds
Content-Type: text/html; charset=utf-8
Age: 0Connection: keep-alive
Server: YTS/1.17.23.1
HTTP Header
Thursday, 18 February 2010
http://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://info.yahoo.com/w3c/p3p.xmlhttp://info.yahoo.com/w3c/p3p.xmlhttp://www.yahoo.com/http://www.yahoo.com/ -
8/14/2019 Social Plumbing PyCon 2010
58/216
$ curl -I -H "Accept: application/xrds+xml" www.yahoo.com
HTTP/1.1 200 OKDate: Mon, 15 Feb 2010 17:09:24 GMT
P3P: policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM..."
Vary: Accept,Accept-Encoding
Cache-Control: private
X-XRDS-Location: http://open.login.yahooapis.com/openid20/www.yahoo.com/xrds
Content-Type: text/html; charset=utf-8
Age: 0Connection: keep-alive
Server: YTS/1.17.23.1
HTTP Header
Thursday, 18 February 2010
http://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://info.yahoo.com/w3c/p3p.xmlhttp://info.yahoo.com/w3c/p3p.xmlhttp://www.yahoo.com/http://www.yahoo.com/ -
8/14/2019 Social Plumbing PyCon 2010
59/216
$ curl -I -H "Accept: application/xrds+xml" www.yahoo.com
HTTP/1.1 200 OKDate: Mon, 15 Feb 2010 17:09:24 GMT
P3P: policyref="http://info.yahoo.com/w3c/p3p.xml", CP="CAO DSP COR CUR ADM..."
Vary: Accept,Accept-Encoding
Cache-Control: private
X-XRDS-Location: http://open.login.yahooapis.com/openid20/www.yahoo.com/xrds
Content-Type: text/html; charset=utf-8
Age: 0Connection: keep-alive
Server: YTS/1.17.23.1
HTTP Header
Thursday, 18 February 2010
http://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://info.yahoo.com/w3c/p3p.xmlhttp://info.yahoo.com/w3c/p3p.xmlhttp://www.yahoo.com/http://www.yahoo.com/ -
8/14/2019 Social Plumbing PyCon 2010
60/216
$ curl http://open.login.yahooapis.com/openid20/www.yahoo.com/xrds
http://specs.openid.net/auth/2.0/server
http://specs.openid.net/extensions/pape/1.0http://openid.net/sreg/1.0
http://openid.net/extensions/sreg/1.1http://openid.net/srv/ax/1.0
http://specs.openid.net/extensions/oauth/1.0 http://specs.openid.net/extensions/ui/1.0/lang-pref
http://specs.openid.net/extensions/ui/1.0/mode/popuphttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/
privatepersonalidentifierhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf
http://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdf
http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttps://open.login.yahooapis.com/openid/op/auth
Thursday, 18 February 2010
https://open.login.yahooapis.com/openid/op/authhttp://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdfhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://specs.openid.net/extensions/ui/1.0/mode/popuphttp://specs.openid.net/extensions/ui/1.0/lang-prefhttp://specs.openid.net/extensions/oauth/1.0http://openid.net/srv/ax/1.0http://openid.net/sreg/1.0http://specs.openid.net/extensions/pape/1.0https://open.login.yahooapis.com/openid/op/authhttps://open.login.yahooapis.com/openid/op/authhttp://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttp://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdfhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://specs.openid.net/extensions/ui/1.0/mode/popuphttp://specs.openid.net/extensions/ui/1.0/mode/popuphttp://specs.openid.net/extensions/ui/1.0/lang-prefhttp://specs.openid.net/extensions/ui/1.0/lang-prefhttp://specs.openid.net/extensions/oauth/1.0http://specs.openid.net/extensions/oauth/1.0http://openid.net/srv/ax/1.0http://openid.net/srv/ax/1.0http://openid.net/extensions/sreg/1.1http://openid.net/extensions/sreg/1.1http://openid.net/sreg/1.0http://openid.net/sreg/1.0http://specs.openid.net/extensions/pape/1.0http://specs.openid.net/extensions/pape/1.0http://specs.openid.net/auth/2.0/serverhttp://specs.openid.net/auth/2.0/serverhttp://openid.net/xmlns/1.0http://openid.net/xmlns/1.0http://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrds -
8/14/2019 Social Plumbing PyCon 2010
61/216
$ curl http://open.login.yahooapis.com/openid20/www.yahoo.com/xrds
http://specs.openid.net/auth/2.0/server
http://specs.openid.net/extensions/pape/1.0http://openid.net/sreg/1.0
http://openid.net/extensions/sreg/1.1http://openid.net/srv/ax/1.0
http://specs.openid.net/extensions/oauth/1.0 http://specs.openid.net/extensions/ui/1.0/lang-pref
http://specs.openid.net/extensions/ui/1.0/mode/popuphttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/
privatepersonalidentifierhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdf
http://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdf
http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttps://open.login.yahooapis.com/openid/op/auth
Thursday, 18 February 2010
https://open.login.yahooapis.com/openid/op/authhttps://open.login.yahooapis.com/openid/op/authhttp://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttp://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdfhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://specs.openid.net/extensions/ui/1.0/mode/popuphttp://specs.openid.net/extensions/ui/1.0/lang-prefhttp://specs.openid.net/extensions/oauth/1.0http://openid.net/srv/ax/1.0http://openid.net/sreg/1.0http://specs.openid.net/extensions/pape/1.0https://open.login.yahooapis.com/openid/op/authhttps://open.login.yahooapis.com/openid/op/authhttp://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttp://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/openid-trust-level1.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdfhttp://www.idmanagement.gov/schema/2009/05/icam/no-pii.pdfhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifierhttp://specs.openid.net/extensions/ui/1.0/mode/popuphttp://specs.openid.net/extensions/ui/1.0/mode/popuphttp://specs.openid.net/extensions/ui/1.0/lang-prefhttp://specs.openid.net/extensions/ui/1.0/lang-prefhttp://specs.openid.net/extensions/oauth/1.0http://specs.openid.net/extensions/oauth/1.0http://openid.net/srv/ax/1.0http://openid.net/srv/ax/1.0http://openid.net/extensions/sreg/1.1http://openid.net/extensions/sreg/1.1http://openid.net/sreg/1.0http://openid.net/sreg/1.0http://specs.openid.net/extensions/pape/1.0http://specs.openid.net/extensions/pape/1.0http://specs.openid.net/auth/2.0/serverhttp://specs.openid.net/auth/2.0/serverhttp://openid.net/xmlns/1.0http://openid.net/xmlns/1.0http://open.login.yahooapis.com/openid20/www.yahoo.com/xrdshttp://open.login.yahooapis.com/openid20/www.yahoo.com/xrds -
8/14/2019 Social Plumbing PyCon 2010
62/216
3. RELYING PARTY redirects USER to
OPENID PROVIDER
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
63/216
3.OPENID PROVIDER redirects USER to
RELYING PARTY to prove claim
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
64/216
Problems with OpenID
Lots of providers and not a lot ofconsumers. Companies want to ownOpenID.
I dont normally go by http://stu.mp onthe internets.
Fails the mom test.
Thursday, 18 February 2010
http://stu.mp/http://stu.mp/ -
8/14/2019 Social Plumbing PyCon 2010
65/216
Implementing a Relying Party
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
66/216
-
8/14/2019 Social Plumbing PyCon 2010
67/216
{% extends 'base.html' %}
{% block body %}
OpenID Example
{% if error %}
Error: {{ error }}
{% endif %}
Login with OpenID:
{% endblock %}
1. USER enters their OpenID into oid/
consumer/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
68/216
{% extends 'base.html' %}
{% block body %}
OpenID Example
{% if error %}
Error: {{ error }}
{% endif %}
Login with OpenID:
{% endblock %}
1. USER enters their OpenID into oid/
consumer/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
69/216
def login(request):
"""
Start the OpenID authentication process.
"""
if request.POST:
# Start OpenID authentication.
openid_url = request.POST.get('openid_identifier', '')
auth_consumer = get_consumer(request)
try:
auth_request = auth_consumer.begin(openid_url)except DiscoveryFailure, ex:
# Some protocol-level failure occurred.
error = "OpenID discovery error: %s" % (ex,)
return HttpResponseRedirect('%s?%s' % (reverse('openid_login'),
urllib.urlencode({'error': error})))
2. RELYING PARTY discovers OPENID PROVIDER
using Yadis in oid/consumer/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
70/216
def login(request):
"""
Start the OpenID authentication process.
"""
if request.POST:
# Start OpenID authentication.
openid_url = request.POST.get('openid_identifier', '')
auth_consumer = get_consumer(request)
try:
auth_request = auth_consumer.begin(openid_url)except DiscoveryFailure, ex:
# Some protocol-level failure occurred.
error = "OpenID discovery error: %s" % (ex,)
return HttpResponseRedirect('%s?%s' % (reverse('openid_login'),
urllib.urlencode({'error': error})))
2. RELYING PARTY discovers OPENID PROVIDER
using Yadis in oid/consumer/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
71/216
def login(request):
"""
Start the OpenID authentication process.
"""
if request.POST:
# Start OpenID authentication.
openid_url = request.POST.get('openid_identifier', '')
auth_consumer = get_consumer(request)
try:
auth_request = auth_consumer.begin(openid_url)except DiscoveryFailure, ex:
# Some protocol-level failure occurred.
error = "OpenID discovery error: %s" % (ex,)
return HttpResponseRedirect('%s?%s' % (reverse('openid_login'),
urllib.urlencode({'error': error})))
2. RELYING PARTY discovers OPENID PROVIDER
using Yadis in oid/consumer/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
72/216
def get_consumer(request):
"""
Get a Consumer object to perform OpenID authentication.
"""
return consumer.Consumer(request.session, DjangoOpenIDStore())
a.Instantiate Consumer with user data-store andglobal data-store in oid/consumer/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
73/216
def get_consumer(request):
"""
Get a Consumer object to perform OpenID authentication.
"""
return consumer.Consumer(request.session, DjangoOpenIDStore())
a.Instantiate Consumer with user data-store andglobal data-store in oid/consumer/views.py
Thursday, 18 February 2010
response = yadisDiscover(uri)
-
8/14/2019 Social Plumbing PyCon 2010
74/216
yadis_url = response.normalized_uri
body = response.response_text
try:
openid_services = OpenIDServiceEndpoint.fromXRDS(yadis_url, body)
except XRDSError:# Does not parse as a Yadis XRDS file
openid_services = []
if not openid_services:
# Either not an XRDS or there are no OpenID services.
if response.isXRDS():
# if we got the Yadis content-type or followed the Yadis# header, re-fetch the document without following the Yadis
# header, with no Accept header.
return discoverNoYadis(uri)
# Try to parse the response as HTML.
#
openid_services = OpenIDServiceEndpoint.fromHTML(yadis_url, body)
return (yadis_url, getOPOrUserServices(openid_services))
b.Discover OPENID PROVIDER endpoint
in oid/consumer/discover.pyThursday, 18 February 2010
response = yadisDiscover(uri)
-
8/14/2019 Social Plumbing PyCon 2010
75/216
yadis_url = response.normalized_uri
body = response.response_text
try:
openid_services = OpenIDServiceEndpoint.fromXRDS(yadis_url, body)
except XRDSError:# Does not parse as a Yadis XRDS file
openid_services = []
if not openid_services:
# Either not an XRDS or there are no OpenID services.
if response.isXRDS():
# if we got the Yadis content-type or followed the Yadis# header, re-fetch the document without following the Yadis
# header, with no Accept header.
return discoverNoYadis(uri)
# Try to parse the response as HTML.
#
openid_services = OpenIDServiceEndpoint.fromHTML(yadis_url, body)
return (yadis_url, getOPOrUserServices(openid_services))
b.Discover OPENID PROVIDER endpoint
in oid/consumer/discover.pyThursday, 18 February 2010
response = yadisDiscover(uri)
di l li d i
-
8/14/2019 Social Plumbing PyCon 2010
76/216
yadis_url = response.normalized_uri
body = response.response_text
try:
openid_services = OpenIDServiceEndpoint.fromXRDS(yadis_url, body)
except XRDSError:# Does not parse as a Yadis XRDS file
openid_services = []
if not openid_services:
# Either not an XRDS or there are no OpenID services.
if response.isXRDS():
# if we got the Yadis content-type or followed the Yadis# header, re-fetch the document without following the Yadis
# header, with no Accept header.
return discoverNoYadis(uri)
# Try to parse the response as HTML.
#
openid_services = OpenIDServiceEndpoint.fromHTML(yadis_url, body)
return (yadis_url, getOPOrUserServices(openid_services))
b.Discover OPENID PROVIDER endpoint
in oid/consumer/discover.pyThursday, 18 February 2010
response = yadisDiscover(uri)
di l li d i
-
8/14/2019 Social Plumbing PyCon 2010
77/216
yadis_url = response.normalized_uri
body = response.response_text
try:
openid_services = OpenIDServiceEndpoint.fromXRDS(yadis_url, body)
except XRDSError:# Does not parse as a Yadis XRDS file
openid_services = []
if not openid_services:
# Either not an XRDS or there are no OpenID services.
if response.isXRDS():
# if we got the Yadis content-type or followed the Yadis# header, re-fetch the document without following the Yadis
# header, with no Accept header.
return discoverNoYadis(uri)
# Try to parse the response as HTML.
#
openid_services = OpenIDServiceEndpoint.fromHTML(yadis_url, body)
return (yadis_url, getOPOrUserServices(openid_services))
b.Discover OPENID PROVIDER endpoint
in oid/consumer/discover.pyThursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
78/216
3. RELYING PARTY redirects to OPENID
PROVIDER in oid/consumer/views.py
if request.is_secure():
realm = 'https://%s' % (request.get_host(),)
else:
realm = 'http://%s' % (request.get_host(),)
return_to = request.build_absolute_uri(reverse('openid_finish'))
return HttpResponseRedirect(auth_request.redirectURL(realm, return_to))
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
79/216
3. RELYING PARTY redirects to OPENID
PROVIDER in oid/consumer/views.py
if request.is_secure():
realm = 'https://%s' % (request.get_host(),)
else:
realm = 'http://%s' % (request.get_host(),)
return_to = request.build_absolute_uri(reverse('openid_finish'))
return HttpResponseRedirect(auth_request.redirectURL(realm, return_to))
Thursday, 18 February 2010
Whats that OpenID
-
8/14/2019 Social Plumbing PyCon 2010
80/216
What s that OpenID
Realm? A pattern that represents the part of the URL-space forwhich an OpenID authentication request is valid
Presented by the provider to the end user during theauthentication request
Gives the user an indication of the scope of theauthentication request
The openid.return_to URL must match the realm
URL scheme and port must match
URL path must be equal or a subdirectory of realm
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
81/216
OpenID Realm
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
82/216
Realm URL Match
http://example.com/ http://example.com/foo
http://example.com/ https://example.com/op/
http://example.com/ http://snarf.example.com/baz/
http://*.example.com/ http://snarf.example.com/baz/
http://example.com:8080/ http://example.com/
http://*.com/ http://example.com/
Thursday, 18 February 2010
http://example.com/http://example.com/http://example.com/http://example.com/http://example.com:8080/http://example.com:8080/http://snarf.example.com/baz/http://snarf.example.com/baz/http://snarf.example.com/bazhttp://snarf.example.com/bazhttp://example.com/http://example.com/https://example.com/op/https://example.com/op/http://example.com/http://example.com/http://exam/http://exam/http://example.com/http://example.com/ -
8/14/2019 Social Plumbing PyCon 2010
83/216
def finish(request):
auth_consumer = get_consumer(request)
return_to = request.build_absolute_uri(reverse('openid_finish'))
response = auth_consumer.complete(request.REQUEST, return_to)
context = {consumer.CANCEL: {
'error': 'OpenID authentication was cancelled.',
},
consumer.FAILURE: {
'error': 'OpenID authentication failed.',
},
consumer.SUCCESS: {
'identity': response.getDisplayIdentifier(),
}
}.get(response.status, {'error': 'Unknown response type.'})
return render_to_response('finish.html',context,RequestContext(request))
4. OPENID PROVIDER redirects RELYING PARTY
which verifies claim in oid/consumer/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
84/216
There are two ways RELYING PARTY can veryifyauthentication with an OPENID PROVIDER
1.Use a pre-established association with the
OpenID provider to check the cryptographicsignature of the response.
2.Contact the OpenID provider directly toverify the response.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
85/216
Questions?!
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
86/216
Implementing an OpenID Provider
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
87/216
1. Decode the response and determine its nature.
2. Decide how to respond to the request.
a. The server can respond automatically to some
request types by passing to
server.handleRequest.
b. For checkid_setup and
checkid_immediate you should decide
whether the user is authorized to claim the
identity in question3. Encode the response according to the OpenID
protocol
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
88/216
def get_server(request):
return Server(DjangoOpenIDStore(),
request.build_absolute_uri(reverse('openid_provider')))
def openid_provider(request, identity=None):
server = get_server(request)
try:
openid_request = server.decodeRequest(request.REQUEST)
except ProtocolError, ex:
return render_to_response('provider/index.html', {
'error': str(ex),
}, RequestContext(request))
1. Decode request and and determine itsnature in oid/provider/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
89/216
def get_server(request):
return Server(DjangoOpenIDStore(),
request.build_absolute_uri(reverse('openid_provider')))
def openid_provider(request, identity=None):
server = get_server(request)
try:
openid_request = server.decodeRequest(request.REQUEST)
except ProtocolError, ex:
return render_to_response('provider/index.html', {
'error': str(ex),
}, RequestContext(request))
1. Decode request and and determine itsnature in oid/provider/views.py
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
90/216
2. Decide how to respond to the reqeust
in oid/provider/views.py
if openid_request.mode in ('checkid_immediate', 'checkid_setup'):
# Do some stuff.
else:
# Got some other kind of request. Let the server take care of it.
response = server.handleRequest(openid_request)
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
91/216
2. Decide how to respond to the reqeust
in oid/provider/views.py
if openid_request.mode in ('checkid_immediate', 'checkid_setup'):
# Do some stuff.
else:
# Got some other kind of request. Let the server take care of it.
response = server.handleRequest(openid_request)
Thursday, 18 February 2010
if openid request mode in ('checkid immediate' 'checkid setup'):
-
8/14/2019 Social Plumbing PyCon 2010
92/216
if openid_request.mode in ( checkid_immediate , checkid_setup ):
# Got a checkid request. Always return yes. In a real server
# we'd check that the user is logged in and ask them if they
# trust the relying party, etc.
if openid_request.idSelect():# If an identity URL wasn't entered at the RP, then we have to
# come up with one. Well ask the user who they want to be.
if 'identity' in request.POST:
identity = reverse('openid_identity', kwargs={
'identity': request.POST['identity']
})
response = openid_request.answer(True,identity=request.build_absolute_uri(identity))
else:
return render_to_response('provider/index.html', {
'trust_root': openid_request.trust_root,
'needs_identity': True,
}, RequestContext(request))else:
response = openid_request.answer(True,
identity=openid_request.identity)
Thursday, 18 February 2010
if openid request mode in ('checkid immediate' 'checkid setup'):
-
8/14/2019 Social Plumbing PyCon 2010
93/216
if openid_request.mode in ( checkid_immediate , checkid_setup ):
# Got a checkid request. Always return yes. In a real server
# we'd check that the user is logged in and ask them if they
# trust the relying party, etc.
if openid_request.idSelect():# If an identity URL wasn't entered at the RP, then we have to
# come up with one. Well ask the user who they want to be.
if 'identity' in request.POST:
identity = reverse('openid_identity', kwargs={
'identity': request.POST['identity']
})
response = openid_request.answer(True,identity=request.build_absolute_uri(identity))
else:
return render_to_response('provider/index.html', {
'trust_root': openid_request.trust_root,
'needs_identity': True,
}, RequestContext(request))else:
response = openid_request.answer(True,
identity=openid_request.identity)
Thursday, 18 February 2010
if openid request mode in ('checkid immediate' 'checkid setup'):
-
8/14/2019 Social Plumbing PyCon 2010
94/216
if openid_request.mode in ( checkid_immediate , checkid_setup ):
# Got a checkid request. Always return yes. In a real server
# we'd check that the user is logged in and ask them if they
# trust the relying party, etc.
if openid_request.idSelect():# If an identity URL wasn't entered at the RP, then we have to
# come up with one. Well ask the user who they want to be.
if 'identity' in request.POST:
identity = reverse('openid_identity', kwargs={
'identity': request.POST['identity']
})
response = openid_request.answer(True,identity=request.build_absolute_uri(identity))
else:
return render_to_response('provider/index.html', {
'trust_root': openid_request.trust_root,
'needs_identity': True,
}, RequestContext(request))else:
response = openid_request.answer(True,
identity=openid_request.identity)
Thursday, 18 February 2010
if openid request mode in ('checkid immediate' 'checkid setup'):
-
8/14/2019 Social Plumbing PyCon 2010
95/216
if openid_request.mode in ( checkid_immediate , checkid_setup ):
# Got a checkid request. Always return yes. In a real server
# we'd check that the user is logged in and ask them if they
# trust the relying party, etc.
if openid_request.idSelect():# If an identity URL wasn't entered at the RP, then we have to
# come up with one. Well ask the user who they want to be.
if 'identity' in request.POST:
identity = reverse('openid_identity', kwargs={
'identity': request.POST['identity']
})
response = openid_request.answer(True,identity=request.build_absolute_uri(identity))
else:
return render_to_response('provider/index.html', {
'trust_root': openid_request.trust_root,
'needs_identity': True,
}, RequestContext(request))else:
response = openid_request.answer(True,
identity=openid_request.identity)
Thursday, 18 February 2010
if openid request mode in ('checkid immediate' 'checkid setup'):
-
8/14/2019 Social Plumbing PyCon 2010
96/216
if openid_request.mode in ( checkid_immediate , checkid_setup ):
# Got a checkid request. Always return yes. In a real server
# we'd check that the user is logged in and ask them if they
# trust the relying party, etc.
if openid_request.idSelect():# If an identity URL wasn't entered at the RP, then we have to
# come up with one. Well ask the user who they want to be.
if 'identity' in request.POST:
identity = reverse('openid_identity', kwargs={
'identity': request.POST['identity']
})
response = openid_request.answer(True,identity=request.build_absolute_uri(identity))
else:
return render_to_response('provider/index.html', {
'trust_root': openid_request.trust_root,
'needs_identity': True,
}, RequestContext(request))else:
response = openid_request.answer(True,
identity=openid_request.identity)
Thursday, 18 February 2010
def render_openid_response(openid_response):
response = HttpResponse(openid_response.body,
st t s openid response code)
-
8/14/2019 Social Plumbing PyCon 2010
97/216
status=openid_response.code)
for header, value in openid_response.headers.iteritems():
response[header] = value
return response
...
response = openid_request.answer(True,
identity=openid_request.identity)
else:
# Got some other kind of request. Let the server take care of it.
response = server.handleRequest(openid_request)
try:
return render_openid_response(server.encodeResponse(response))
except EncodingError, ex:
return render_to_response('provider/index.html', {
'error': cgi.escape(ex.response.encodeToKVForm()),
}, RequestContext(request))
3.Encode the response according to the OpenID
protocol in oid/provider/views.py
Thursday, 18 February 2010
def render_openid_response(openid_response):
response = HttpResponse(openid_response.body,
status openid response code)
-
8/14/2019 Social Plumbing PyCon 2010
98/216
status=openid_response.code)
for header, value in openid_response.headers.iteritems():
response[header] = value
return response
...
response = openid_request.answer(True,
identity=openid_request.identity)
else:
# Got some other kind of request. Let the server take care of it.
response = server.handleRequest(openid_request)
try:
return render_openid_response(server.encodeResponse(response))
except EncodingError, ex:
return render_to_response('provider/index.html', {
'error': cgi.escape(ex.response.encodeToKVForm()),
}, RequestContext(request))
3.Encode the response according to the OpenID
protocol in oid/provider/views.py
Thursday, 18 February 2010
def render_openid_response(openid_response):
response = HttpResponse(openid_response.body,
status openid response code)
-
8/14/2019 Social Plumbing PyCon 2010
99/216
status=openid_response.code)
for header, value in openid_response.headers.iteritems():
response[header] = value
return response
...
response = openid_request.answer(True,
identity=openid_request.identity)
else:
# Got some other kind of request. Let the server take care of it.
response = server.handleRequest(openid_request)
try:
return render_openid_response(server.encodeResponse(response))
except EncodingError, ex:
return render_to_response('provider/index.html', {
'error': cgi.escape(ex.response.encodeToKVForm()),
}, RequestContext(request))
3.Encode the response according to the OpenID
protocol in oid/provider/views.py
Thursday, 18 February 2010
def render_openid_response(openid_response):
response = HttpResponse(openid_response.body,
status openid response code)
-
8/14/2019 Social Plumbing PyCon 2010
100/216
status=openid_response.code)
for header, value in openid_response.headers.iteritems():
response[header] = value
return response
...
response = openid_request.answer(True,
identity=openid_request.identity)
else:
# Got some other kind of request. Let the server take care of it.
response = server.handleRequest(openid_request)
try:
return render_openid_response(server.encodeResponse(response))
except EncodingError, ex:
return render_to_response('provider/index.html', {
'error': cgi.escape(ex.response.encodeToKVForm()),
}, RequestContext(request))
3.Encode the response according to the OpenID
protocol in oid/provider/views.py
Thursday, 18 February 2010
def render_openid_response(openid_response):
response = HttpResponse(openid_response.body,
status=openid response code)
-
8/14/2019 Social Plumbing PyCon 2010
101/216
status=openid_response.code)
for header, value in openid_response.headers.iteritems():
response[header] = value
return response
...
response = openid_request.answer(True,
identity=openid_request.identity)
else:
# Got some other kind of request. Let the server take care of it.
response = server.handleRequest(openid_request)
try:
return render_openid_response(server.encodeResponse(response))
except EncodingError, ex:
return render_to_response('provider/index.html', {
'error': cgi.escape(ex.response.encodeToKVForm()),
}, RequestContext(request))
3.Encode the response according to the OpenID
protocol in oid/provider/views.py
Thursday, 18 February 2010
Wrapping things up
-
8/14/2019 Social Plumbing PyCon 2010
102/216
Wrapping things up
with Yadis1. Put an XRDS capabilities document on
your server that points to your OpenIDProvider
2. Set the X-XRDS-Location header forrequests to your OpenID Provider (e.g.,the root of your site) pointing to yourXRDS capabilities document
Thursday, 18 February 2010
Minimal XRDS
-
8/14/2019 Social Plumbing PyCon 2010
103/216
Minimal XRDS
Documenthttp://specs.openid.net/auth/2.0/serverhttp://127.0.0.1:8000/op/
Thursday, 18 February 2010
Minimal XRDS
http://127.0.0.1:8000/op/http://127.0.0.1:8000/op/http://specs.openid.net/auth/2.0/serverhttp://specs.openid.net/auth/2.0/server -
8/14/2019 Social Plumbing PyCon 2010
104/216
Minimal XRDS
Documenthttp://specs.openid.net/auth/2.0/serverhttp://127.0.0.1:8000/op/
Thursday, 18 February 2010
http://127.0.0.1:8000/op/http://127.0.0.1:8000/op/http://specs.openid.net/auth/2.0/serverhttp://specs.openid.net/auth/2.0/server -
8/14/2019 Social Plumbing PyCon 2010
105/216
Questions?!
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
106/216
BREAK!
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
107/216
Protocol for secure, password-freeAPI authorization. Your valet key
for the web.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
108/216
OAuth was conceived by Blaine Cook, the guywho wrote Twitter, as a standard way to
delegate authorization to a third party. It wasbased on Flickrs API, though theres lots of priorart in this space.
Thursday, 18 February 2010
P bl Add d
-
8/14/2019 Social Plumbing PyCon 2010
109/216
Problems Addressed
Allows a user to share private resourceswith a third party withouthaving to give out
their credentials.
Allows APIs to keep track of third partiesaccessing private resources.
Allows users to revoke access to thirdparties.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
110/216
Why do I care?OAuth is the standard interface for YOS,
OpenSocial, GData, MySpace, and (soon)Facebooks API and Facebook Connect.
Twitter is aggressively moving people fromthe password anti-pattern to OAuth as
well.
Thursday, 18 February 2010
The Password Anti-
-
8/14/2019 Social Plumbing PyCon 2010
111/216
Pattern1. Exposes your password to third-parties.2. Forces third-parties to store your username and
password in plain text.
3. Provides no mechanism for revoking
permission.
4. No support for other authentication mechanisms
(e.g., OpenID, two-factor authentication)
5. Teaches users to be phished
Thursday, 18 February 2010
Terminology
-
8/14/2019 Social Plumbing PyCon 2010
112/216
Terminology
A CLIENT (consumer) is a library orapplication capable of making OAuth
requests wishing to access protectedresources.
A SERVER (provider) is an HTTP servercapable of receiving and authenticatingOAuth requests sent by the client.
Thursday, 18 February 2010
Terminology
-
8/14/2019 Social Plumbing PyCon 2010
113/216
Terminology
A PROTECTED RESOURCE is a restrictedresource or operation that can be accessedvia the server by the client.
A RESOURCE OWNER is the owner of aprotected resource who can access the
server and delegate access to a client.
Thursday, 18 February 2010
Terminology
-
8/14/2019 Social Plumbing PyCon 2010
114/216
Terminology
CREDENTIALS are comprised of a unique keyand a shared secret.
CLIENTCREDENTIALS identify an individualclient.
TEMPORARYCREDENTIALS identify anauthorization request from a client.
TOKENCREDENTIALS uniquely identify a clientaccessing the server on behalf of a resourceowner.
Thursday, 18 February 2010
Terminology in
-
8/14/2019 Social Plumbing PyCon 2010
115/216
gy
TransitionOld New
Consumer Client
Service Provider Server
User Resource Owner
Consumer Key & Secret Client Credentials
Request Token & Secret Temporary CredentialsAccess Token & Secret Token Credentials
Thursday, 18 February 2010
Hows it work?
-
8/14/2019 Social Plumbing PyCon 2010
116/216
How s it work?
A 3rd party sends a signed request to theAPI for a request token.
3rd party redirects user to API providerasking for access. User grants access and is redirected back to
3rd party.
3rd party sends a signed request to the APIto obtain authorized access tokens.
Three-legged OAuth
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
117/216
1.CLIENT requests access from USER
Thursday, 18 February 2010
GET /oauth/request_token HTTP/1.1i
-
8/14/2019 Social Plumbing PyCon 2010
118/216
Host: twitter.comAuthorization: OAuth realm="wefollow",
oauth_consumer_key="qqoivmh7sy8ils15",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",oauth_nonce="0fevohyb",oauth_callback="http%3A%2F%2Fwefollow.com%2Fadd%2Ftags",oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"
HTTP 1.1 200 OKContent-Type: application/x-www-form-urlencoded
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_token_secret=
25sbmeb6ifnkwx6yp23w55svnof6txdy&oauth_callback_confirmed=true
2. CLIENT requests a TEMPORARY TOKENfrom SERVER
Thursday, 18 February 2010
GET /oauth/request_token HTTP/1.1H t t itt
-
8/14/2019 Social Plumbing PyCon 2010
119/216
Host: twitter.comAuthorization: OAuth realm="wefollow",
oauth_consumer_key="qqoivmh7sy8ils15",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",oauth_nonce="0fevohyb",oauth_callback="http%3A%2F%2Fwefollow.com%2Fadd%2Ftags",oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"
HTTP 1.1 200 OKContent-Type: application/x-www-form-urlencoded
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_token_secret=
25sbmeb6ifnkwx6yp23w55svnof6txdy&oauth_callback_confirmed=true
2. CLIENT requests a TEMPORARY TOKENfrom SERVER
Thursday, 18 February 2010
GET /oauth/request_token HTTP/1.1H t t itt
-
8/14/2019 Social Plumbing PyCon 2010
120/216
Host: twitter.comAuthorization: OAuth realm="wefollow",
oauth_consumer_key="qqoivmh7sy8ils15",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",oauth_nonce="0fevohyb",oauth_callback="http%3A%2F%2Fwefollow.com%2Fadd%2Ftags",oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"
HTTP 1.1 200 OKContent-Type: application/x-www-form-urlencoded
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_token_secret=
25sbmeb6ifnkwx6yp23w55svnof6txdy&oauth_callback_confirmed=true
2. CLIENT requests a TEMPORARY TOKENfrom SERVER
Thursday, 18 February 2010
GET /oauth/request_token HTTP/1.1H t t itt
-
8/14/2019 Social Plumbing PyCon 2010
121/216
Host: twitter.comAuthorization: OAuth realm="wefollow",
oauth_consumer_key="qqoivmh7sy8ils15",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",oauth_nonce="0fevohyb",oauth_callback="http%3A%2F%2Fwefollow.com%2Fadd%2Ftags",oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"
HTTP 1.1 200 OKContent-Type: application/x-www-form-urlencoded
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_token_secret=
25sbmeb6ifnkwx6yp23w55svnof6txdy&oauth_callback_confirmed=true
2. CLIENT requests a TEMPORARY TOKENfrom SERVER
Thursday, 18 February 2010
GET /oauth/request_token HTTP/1.1H t t itt
-
8/14/2019 Social Plumbing PyCon 2010
122/216
Host: twitter.comAuthorization: OAuth realm="wefollow",
oauth_consumer_key="qqoivmh7sy8ils15",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",oauth_nonce="0fevohyb",oauth_callback="http%3A%2F%2Fwefollow.com%2Fadd%2Ftags",oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"
HTTP 1.1 200 OKContent-Type: application/x-www-form-urlencoded
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_token_secret=
25sbmeb6ifnkwx6yp23w55svnof6txdy&oauth_callback_confirmed=true
2. CLIENT requests a TEMPORARY TOKENfrom SERVER
Thursday, 18 February 2010
GET /oauth/request_token HTTP/1.1Host: t itter com
-
8/14/2019 Social Plumbing PyCon 2010
123/216
Host: twitter.comAuthorization: OAuth realm="wefollow",
oauth_consumer_key="qqoivmh7sy8ils15",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",oauth_nonce="0fevohyb",oauth_callback="http%3A%2F%2Fwefollow.com%2Fadd%2Ftags",oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"
HTTP 1.1 200 OKContent-Type: application/x-www-form-urlencoded
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_token_secret=
25sbmeb6ifnkwx6yp23w55svnof6txdy&oauth_callback_confirmed=true
2. CLIENT requests a TEMPORARY TOKENfrom SERVER
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
124/216
3. USER is redirected to SERVER to grantaccess to CLIENT
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
125/216
HTTP/1.1 302 Redirect
Location: http://wefollow.com/add/tags?
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro
4. SERVER redirects to USER back to
CLIENTs oauth_callback
Thursday, 18 February 2010
http://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro -
8/14/2019 Social Plumbing PyCon 2010
126/216
HTTP/1.1 302 Redirect
Location: http://wefollow.com/add/tags?
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro
4. SERVER redirects to USER back to
CLIENTs oauth_callback
Thursday, 18 February 2010
http://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro -
8/14/2019 Social Plumbing PyCon 2010
127/216
HTTP/1.1 302 Redirect
Location: http://wefollow.com/add/tags?
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro
4. SERVER redirects to USER back to
CLIENTs oauth_callback
Thursday, 18 February 2010
http://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro -
8/14/2019 Social Plumbing PyCon 2010
128/216
HTTP/1.1 302 Redirect
Location: http://wefollow.com/add/tags?
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro
4. SERVER redirects to USER back to
CLIENTs oauth_callback
Thursday, 18 February 2010
http://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro -
8/14/2019 Social Plumbing PyCon 2010
129/216
HTTP/1.1 302 Redirect
Location: http://wefollow.com/add/tags?
oauth_token=dn8gxxob06rirxzp30e68dsffkd08hfh&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro
4. SERVER redirects to USER back to
CLIENTs oauth_callback
Thursday, 18 February 2010
POST /oauth/access_token HTTP/1.1
Host: twitter.com
Authorization: OAuth realm="wefollow"
http://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbErohttp://wefollow.com/add/tags?oauth_token=4GlxjQKGCHMzvkYx66d84T7Uzmu1IPbT1Z2QAiOKg&oauth_verifier=5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro -
8/14/2019 Social Plumbing PyCon 2010
130/216
Authorization: OAuth realm= wefollow ,
oauth_consumer_key="qqoivmh7sy8ils15",
oauth_token="dn8gxxob06rirxzp30e68dsffkd08hfh",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1266278609",
oauth_nonce="1gfwpizc",
oauth_verifier="5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro",
oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=bjunknhae01qkvxw3litbej17w6h74w1&oauth_token_secret=hx
ffqqtx4vxitjex5evacz6kmplw3ms7
4. CLIENT exchanges TEMPORARYCREDENTIALS for TOKEN CREDENTIALS
Thursday, 18 February 2010
POST /oauth/access_token HTTP/1.1
Host: twitter.com
Authorization: OAuth realm="wefollow"
-
8/14/2019 Social Plumbing PyCon 2010
131/216
Authorization: OAuth realm= wefollow ,
oauth_consumer_key="qqoivmh7sy8ils15",
oauth_token="dn8gxxob06rirxzp30e68dsffkd08hfh",
oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",
oauth_nonce="1gfwpizc",
oauth_verifier="5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro",
oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=bjunknhae01qkvxw3litbej17w6h74w1&oauth_token_secret=hx
ffqqtx4vxitjex5evacz6kmplw3ms7
4. CLIENT exchanges TEMPORARYCREDENTIALS for TOKEN CREDENTIALS
Thursday, 18 February 2010
POST /oauth/access_token HTTP/1.1
Host: twitter.com
Authorization: OAuth realm="wefollow"
-
8/14/2019 Social Plumbing PyCon 2010
132/216
Authorization: OAuth realm= wefollow ,
oauth_consumer_key="qqoivmh7sy8ils15",
oauth_token="dn8gxxob06rirxzp30e68dsffkd08hfh",
oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",
oauth_nonce="1gfwpizc",
oauth_verifier="5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro",
oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=bjunknhae01qkvxw3litbej17w6h74w1&oauth_token_secret=hx
ffqqtx4vxitjex5evacz6kmplw3ms7
4. CLIENT exchanges TEMPORARYCREDENTIALS for TOKEN CREDENTIALS
Thursday, 18 February 2010
POST /oauth/access_token HTTP/1.1
Host: twitter.com
Authorization: OAuth realm="wefollow"
-
8/14/2019 Social Plumbing PyCon 2010
133/216
Authorization: OAuth realm= wefollow ,
oauth_consumer_key="qqoivmh7sy8ils15",
oauth_token="dn8gxxob06rirxzp30e68dsffkd08hfh",
oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",
oauth_nonce="1gfwpizc",
oauth_verifier="5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro",
oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=bjunknhae01qkvxw3litbej17w6h74w1&oauth_token_secret=hx
ffqqtx4vxitjex5evacz6kmplw3ms7
4. CLIENT exchanges TEMPORARYCREDENTIALS for TOKEN CREDENTIALS
Thursday, 18 February 2010
POST /oauth/access_token HTTP/1.1
Host: twitter.com
Authorization: OAuth realm="wefollow"
-
8/14/2019 Social Plumbing PyCon 2010
134/216
Authorization: OAuth realm= wefollow ,
oauth_consumer_key="qqoivmh7sy8ils15",
oauth_token="dn8gxxob06rirxzp30e68dsffkd08hfh",
oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",
oauth_nonce="1gfwpizc",
oauth_verifier="5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro",
oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=bjunknhae01qkvxw3litbej17w6h74w1&oauth_token_secret=hx
ffqqtx4vxitjex5evacz6kmplw3ms7
4. CLIENT exchanges TEMPORARYCREDENTIALS for TOKEN CREDENTIALS
Thursday, 18 February 2010
POST /oauth/access_token HTTP/1.1
Host: twitter.com
Authorization: OAuth realm="wefollow",
-
8/14/2019 Social Plumbing PyCon 2010
135/216
Authorization: OAuth realm wefollow ,
oauth_consumer_key="qqoivmh7sy8ils15",
oauth_token="dn8gxxob06rirxzp30e68dsffkd08hfh",
oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",
oauth_nonce="1gfwpizc",
oauth_verifier="5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro",
oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=bjunknhae01qkvxw3litbej17w6h74w1&oauth_token_secret=hx
ffqqtx4vxitjex5evacz6kmplw3ms7
4. CLIENT exchanges TEMPORARYCREDENTIALS for TOKEN CREDENTIALS
Thursday, 18 February 2010
POST /oauth/access_token HTTP/1.1
Host: twitter.com
Authorization: OAuth realm="wefollow",
-
8/14/2019 Social Plumbing PyCon 2010
136/216
Authorization: OAuth realm wefollow ,
oauth_consumer_key="qqoivmh7sy8ils15",
oauth_token="dn8gxxob06rirxzp30e68dsffkd08hfh",
oauth_signature_method="HMAC-SHA1",oauth_timestamp="1266278609",
oauth_nonce="1gfwpizc",
oauth_verifier="5SXT8kus3yCJbOAkuJTpQ2u9U3Pt67YW0wijyVIbEro",
oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"
HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded
oauth_token=bjunknhae01qkvxw3litbej17w6h74w1&oauth_token_secret=hx
ffqqtx4vxitjex5evacz6kmplw3ms7
4. CLIENT exchanges TEMPORARYCREDENTIALS for TOKEN CREDENTIALS
Thursday, 18 February 2010
OAUTH AUTHENTICATION FLOW v1.0aConsumer / Service ProviderPerson Using Web Browser / Manual Entry
-
8/14/2019 Social Plumbing PyCon 2010
137/216
GrantRequest Token
Direct User toService Provider
RequestRequest Token
Obtain UserAuthorization
Direct User toConsumer
RequestAccess Token
GrantAccess Token
Access ProtectedResources
A
B
C
D
E
F
G
ObtainUnau
thorized
RequestT
oken
UserAuthorizes
Req
uestToken
Exch
angeRequestToken
forAccessToken
Consumer Service Provider A
B
C
D
E
F
G
Consumer Requests
Request Token
Request includes
oauth_consumer_keyoauth_signature_methodoauth_signatureoauth_timestampoauth_nonceoauth_version (optional)oauth_callback
Service Provider
Grants Request Token
Response includes
oauth_tokenoauth_token_secretoauth_callback_confirmed
Consumer Directs User to
Service Provider
Request includes
oauth_token (optional)
Service Provider Directs
User to Consumer
Request includesoauth_tokenoauth_verifier
Consumer Requests
Access Token
Request includes
oauth_consumer_keyoauth_tokenoauth_signature_methodoauth_signatureoauth_timestampoauth_nonceoauth_version (optional)oauth_verifier
Service Provider
GrantsAccess Token
Response includesoauth_tokenoauth_token_secret
Consumer Accesses
Protected Resources
Request includes
oauth_consumer_key
oauth_tokenoauth_signature_methodoauth_signatureoauth_timestampoauth_nonceoauth_version (optional)
http://s3.pixane.com/Oauth_diagram.pdf
Thursday, 18 February 2010
Hows it work?
http://blogs.forrester.com/groundswell/2008/03/the-future-of-s.htmlhttp://blogs.forrester.com/groundswell/2008/03/the-future-of-s.html -
8/14/2019 Social Plumbing PyCon 2010
138/216
How s it work?
A 3rd party is given a key and a sharedsecret by the API provider.
3rd party signs the request using a standardalgorithm defined by OAuth.
API re-signs the request in the samemanner using the known shared secret.
Two-legged OAuth
Thursday, 18 February 2010
Problems with OAuth
-
8/14/2019 Social Plumbing PyCon 2010
139/216
Problems with OAuth
No way to delegate tokens (e.g. Allowing TwitPicto post on behalf of Tweetie).
No way to programmatically register clients. HTTP body is not signed. User experience from non-web applications is
clunky.
Endpoints are specific to an API and server. No standard for specifying permissions.
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
140/216
Implementing an OAuth Client
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
141/216
client = oauth.Client(consumer)
url = 'https://twitter.com/oauth/request_token?%s' % (urllib.urlencode({
'oauth_callback': request.build_absolute_uri(reverse('callback')),
}))
resp, content = client.request(url, 'GET')
if resp['status'] != '200':
raise Exception('Invalid response: %s.' % (resp['status'],))
temporary_credentials = dict(urlparse.parse_qsl(content))
request.session[TEMP_CREDENTIALS_KEY] = temporary_credentials
1. CLIENT requests TEMPORARYCREDENTIALS from the SERVER
Thursday, 18 February 2010
http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
142/216
client = oauth.Client(consumer)
url = 'https://twitter.com/oauth/request_token?%s' % (urllib.urlencode({
'oauth_callback': request.build_absolute_uri(reverse('callback')),
}))
resp, content = client.request(url, 'GET')
if resp['status'] != '200':
raise Exception('Invalid response: %s.' % (resp['status'],))
temporary_credentials = dict(urlparse.parse_qsl(content))
request.session[TEMP_CREDENTIALS_KEY] = temporary_credentials
1. CLIENT requests TEMPORARYCREDENTIALS from the SERVER
Thursday, 18 February 2010
http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
143/216
client = oauth.Client(consumer)
url = 'https://twitter.com/oauth/request_token?%s' % (urllib.urlencode({
'oauth_callback': request.build_absolute_uri(reverse('callback')),
}))
resp, content = client.request(url, 'GET')
if resp['status'] != '200':
raise Exception('Invalid response: %s.' % (resp['status'],))
temporary_credentials = dict(urlparse.parse_qsl(content))
request.session[TEMP_CREDENTIALS_KEY] = temporary_credentials
1. CLIENT requests TEMPORARYCREDENTIALS from the SERVER
Thursday, 18 February 2010
auth_url = 'https://twitter.com/oauth/authorize?%s' % (urllib.urlencode({
'oauth_token': temporary_credentials['oauth_token'],
}))
return HttpResponseRedirect(auth_url)
http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
144/216
p p ( )
1. CLIENT requests TEMPORARYCREDENTIALS from the SERVER
Thursday, 18 February 2010
auth_url = 'https://twitter.com/oauth/authorize?%s' % (urllib.urlencode({
'oauth_token': temporary_credentials['oauth_token'],
}))
return HttpResponseRedirect(auth_url)
http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
145/216
p p ( )
1. CLIENT requests TEMPORARYCREDENTIALS from the SERVER
Thursday, 18 February 2010
auth_url = 'https://twitter.com/oauth/authorize?%s' % (urllib.urlencode({
'oauth_token': temporary_credentials['oauth_token'],
}))
return HttpResponseRedirect(auth_url)
http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
146/216
2. CLIENT requests TEMPORARYCREDENTIALS from the SERVER
Thursday, 18 February 2010
def callback(request):
temp_credentials = request.session.get(TEMP_CREDENTIALS_KEY, None)
if temp_credentials is None:
raise Exception('No temporary credentials.')
-
8/14/2019 Social Plumbing PyCon 2010
147/216
p ( p y )
temp_credentials = oauth.Token(temp_credentials['oauth_token'],
temp_credentials['oauth_token_secret'])consumer = oauth.Consumer(CLIENT_KEY, CLIENT_SECRET)
client = oauth.Client(consumer, temp_credentials)
url = 'https://twitter.com/oauth/access_token?%s' % (urllib.urlencode({
'oauth_verifier': request.GET.get('oauth_verifier', '')
}))
resp, content = client.request(url, 'POST')
access_token = dict(urlparse.parse_qsl(content))
request.session[TOKEN_CREDENTIALS_KEY] = access_token
return HttpResponseRedirect(reverse('consumer'))
3. USER redirected back CLIENTs oauth_callbackwhich exchanges TEMPORARY CREDENTIALS
for TOKEN CREDENTIALS
Thursday, 18 February 2010
def callback(request):
temp_credentials = request.session.get(TEMP_CREDENTIALS_KEY, None)
if temp_credentials is None:
raise Exception('No temporary credentials.')
http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
148/216
p ( p y )
temp_credentials = oauth.Token(temp_credentials['oauth_token'],
temp_credentials['oauth_token_secret'])consumer = oauth.Consumer(CLIENT_KEY, CLIENT_SECRET)
client = oauth.Client(consumer, temp_credentials)
url = 'https://twitter.com/oauth/access_token?%s' % (urllib.urlencode({
'oauth_verifier': request.GET.get('oauth_verifier', '')
}))
resp, content = client.request(url, 'POST')
access_token = dict(urlparse.parse_qsl(content))
request.session[TOKEN_CREDENTIALS_KEY] = access_token
return HttpResponseRedirect(reverse('consumer'))
3. USER redirected back CLIENTs oauth_callbackwhich exchanges TEMPORARY CREDENTIALS
for TOKEN CREDENTIALS
Thursday, 18 February 2010
def callback(request):
temp_credentials = request.session.get(TEMP_CREDENTIALS_KEY, None)
if temp_credentials is None:
raise Exception('No temporary credentials.')
http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
149/216
temp_credentials = oauth.Token(temp_credentials['oauth_token'],
temp_credentials['oauth_token_secret'])consumer = oauth.Consumer(CLIENT_KEY, CLIENT_SECRET)
client = oauth.Client(consumer, temp_credentials)
url = 'https://twitter.com/oauth/access_token?%s' % (urllib.urlencode({
'oauth_verifier': request.GET.get('oauth_verifier', '')
}))
resp, content = client.request(url, 'POST')
access_token = dict(urlparse.parse_qsl(content))
request.session[TOKEN_CREDENTIALS_KEY] = access_token
return HttpResponseRedirect(reverse('consumer'))
3. USER redirected back CLIENTs oauth_callbackwhich exchanges TEMPORARY CREDENTIALS
for TOKEN CREDENTIALS
Thursday, 18 February 2010
def callback(request):
temp_credentials = request.session.get(TEMP_CREDENTIALS_KEY, None)
if temp_credentials is None:
raise Exception('No temporary credentials.')
http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
150/216
temp_credentials = oauth.Token(temp_credentials['oauth_token'],
temp_credentials['oauth_token_secret'])consumer = oauth.Consumer(CLIENT_KEY, CLIENT_SECRET)
client = oauth.Client(consumer, temp_credentials)
url = 'https://twitter.com/oauth/access_token?%s' % (urllib.urlencode({
'oauth_verifier': request.GET.get('oauth_verifier', '')
}))
resp, content = client.request(url, 'POST')
access_token = dict(urlparse.parse_qsl(content))
request.session[TOKEN_CREDENTIALS_KEY] = access_token
return HttpResponseRedirect(reverse('consumer'))
3. USER redirected back CLIENTs oauth_callbackwhich exchanges TEMPORARY CREDENTIALS
for TOKEN CREDENTIALS
Thursday, 18 February 2010
def callback(request):
temp_credentials = request.session.get(TEMP_CREDENTIALS_KEY, None)
if temp_credentials is None:
raise Exception('No temporary credentials.')
http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
151/216
temp_credentials = oauth.Token(temp_credentials['oauth_token'],
temp_credentials['oauth_token_secret'])consumer = oauth.Consumer(CLIENT_KEY, CLIENT_SECRET)
client = oauth.Client(consumer, temp_credentials)
url = 'https://twitter.com/oauth/access_token?%s' % (urllib.urlencode({
'oauth_verifier': request.GET.get('oauth_verifier', '')
}))
resp, content = client.request(url, 'POST')
access_token = dict(urlparse.parse_qsl(content))
request.session[TOKEN_CREDENTIALS_KEY] = access_token
return HttpResponseRedirect(reverse('consumer'))
3. USER redirected back CLIENTs oauth_callbackwhich exchanges TEMPORARY CREDENTIALS
for TOKEN CREDENTIALS
Thursday, 18 February 2010
def callback(request):
temp_credentials = request.session.get(TEMP_CREDENTIALS_KEY, None)
if temp_credentials is None:
raise Exception('No temporary credentials.')
http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
152/216
temp_credentials = oauth.Token(temp_credentials['oauth_token'],
temp_credentials['oauth_token_secret'])consumer = oauth.Consumer(CLIENT_KEY, CLIENT_SECRET)
client = oauth.Client(consumer, temp_credentials)
url = 'https://twitter.com/oauth/access_token?%s' % (urllib.urlencode({
'oauth_verifier': request.GET.get('oauth_verifier', '')
}))
resp, content = client.request(url, 'POST')
access_token = dict(urlparse.parse_qsl(content))
request.session[TOKEN_CREDENTIALS_KEY] = access_token
return HttpResponseRedirect(reverse('consumer'))
3. USER redirected back CLIENTs oauth_callbackwhich exchanges TEMPORARY CREDENTIALS
for TOKEN CREDENTIALS
Thursday, 18 February 2010
try:
credentials = oauth.Token(credentials['oauth_token'],
http://livepage.apple.com/http://livepage.apple.com/http://livepage.apple.com/ -
8/14/2019 Social Plumbing PyCon 2010
153/216
credentials['oauth_token_secret'])
client = oauth.Client(consumer, credentials)
resp, content = client.request('http://twitter.com/account/verify_credentials.json',
'GET'
)
data = json.loads(content)
return HttpResponse('Logged in as %s' % (data['screen_name'],))
except:
del request.session[TOKEN_CREDENTIALS_KEY]
raise
CLIENT can now make OAuth requests toSERVER on behalf of USER using TOKEN
CREDENTIALS
Thursday, 18 February 2010
try:
credentials = oauth.Token(credentials['oauth_token'],
http://twitter.com/account/verify_credentials.json'http://twitter.com/account/verify_credentials.json' -
8/14/2019 Social Plumbing PyCon 2010
154/216
credentials['oauth_token_secret'])
client = oauth.Client(consumer, credentials)
resp, content = client.request('http://twitter.com/account/verify_credentials.json',
'GET'
)
data = json.loads(content)
return HttpResponse('Logged in as %s' % (data['screen_name'],))
except:
del request.session[TOKEN_CREDENTIALS_KEY]
raise
CLIENT can now make OAuth requests toSERVER on behalf of USER using TOKEN
CREDENTIALS
Thursday, 18 February 2010
try:
credentials = oauth.Token(credentials['oauth_token'],
http://twitter.com/account/verify_credentials.json'http://twitter.com/account/verify_credentials.json' -
8/14/2019 Social Plumbing PyCon 2010
155/216
credentials['oauth_token_secret'])
client = oauth.Client(consumer, credentials)
resp, content = client.request('http://twitter.com/account/verify_credentials.json',
'GET'
)
data = json.loads(content)
return HttpResponse('Logged in as %s' % (data['screen_name'],))
except:
del request.session[TOKEN_CREDENTIALS_KEY]
raise
CLIENT can now make OAuth requests toSERVER on behalf of USER using TOKEN
CREDENTIALS
Thursday, 18 February 2010
http://twitter.com/account/verify_credentials.json'http://twitter.com/account/verify_credentials.json' -
8/14/2019 Social Plumbing PyCon 2010
156/216
Questions?!
Thursday, 18 February 2010
-
8/14/2019 Social Plumbing PyCon 2010
157/216
Implementing an OAuth Server
Thursday, 18 February 2010
oauth_request = oauth.Request.from_request(request.method,request.build_absolute_uri(),
headers=headers,parameters=dict(parameters),
query string request environ get('QUERY STRING' '')
-
8/14/2019 Social Plumbing PyCon 2010
158/216
query_string=request.environ.get( QUERY_STRING , ))
try:
consumer = models.Consumer.objects.get(pk=oauth_request['oauth_consumer_key])except (KeyError, models.Consumer.DoesNotExist):
raise HttpUnauthorized('Consumer key missing or invalid.')server = oauth.Server()
server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())...
oauth_token = oauth.Token(token.key, token.secret)...
try:oauth_consumer = oauth.Consumer(consumer.key, consumer.secret)
server.verify_request(oauth_request, oauth_consumer, oauth_token)except ValueError, ex:
return HttpResponseUnauthorized(str(ex))
3. Validate the OAuth request in djoauth/oauth_provider/views.py
Thursday, 18 February 2010
oauth_request = oauth.Request.from_request(request.method,request.build_absolute_uri(),
headers=headers,parameters=dict(parameters),
query string=request environ get('QUERY STRING' '')
-
8/14/2019 Social Plumbing PyCon 2010
159/216
query_string=request.environ.get( QUERY_STRING , ))
try:
consumer = models.Consumer.objects.get(pk=oauth_request['oauth_consumer_key])except (KeyError, models.Consumer.DoesNotExist):
raise HttpUnauthorized('Consumer key missing or invalid.')server = oauth.Server()
server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())...
oauth_token = oauth.Token(token.key, token.secret)...
try:oauth_consumer = oauth.Consumer(consumer.key, consumer.secret)
server.verify_request(oauth_request, oauth_consumer, oauth_token)except ValueError, ex:
return HttpResponseUnauthorized(str(ex))
3. Validate the OAuth request in djoauth/oauth_provider/views.py
Thursday, 18 February 2010
oauth_request = oauth.Request.from_request(request.method,request.build_absolute_uri(),
headers=headers,parameters=dict(parameters),
query string=request environ get('QUERY STRING' '')
-
8/14/2019 Social Plumbing PyCon 2010
160/216
query_string=request.environ.get( QUERY_STRING , ))
try:
consumer = models.Consumer.objects.get(pk=oauth_request['oauth_consumer_key])except (KeyError, models.Consumer.DoesNotExist):
raise HttpUnauthorized('Consumer key missing or invalid.')server = oauth.Server()
server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())...
oauth_token = oauth.Token(token.key, token.secret)...
try:oauth_consumer = oauth.Consumer(consumer.key, consumer.secret)
server.verify_request(oauth_request, oauth_consumer, oauth_token)except ValueError, ex:
return H