1
Copyright 2010 Canonical Ltd. This software is licensed under the
2
GNU Affero General Public License version 3 (see the file LICENSE).
4
= Pre-authorized consumers =
6
In some cases we want consumers to be pre-authorized so that the
7
authorization step of the protocol is skipped. This is the case for
8
canonical's training site: we want the canonical shop to pre-authorize
9
the training site when a user buys some sort of training.
11
To do so the shop has to craft a link which takes the user to the
12
training site through Launchpad. This is what the link looks like:
14
https://openid.launchpad.net/+pre-authorize-rp?trust_root=...&callback=...
16
Where the value of trust_root should be the root URL of the consumer to
17
be pre-authorized and callback is the URL to which the user will be
18
redirected after the consumer is pre-authorized.
20
As an example we'll pre-authorize our own OpenID consumer:
22
# First, some setup to ensure old data doesn't mess with the results
23
>>> from identityprovider.models import OpenIDRPConfig, OpenIDAuthorization
24
>>> OpenIDRPConfig.objects.all().delete()
25
>>> OpenIDAuthorization.objects.all().delete()
27
>>> from urllib import urlencode
28
>>> args = urlencode({
29
... 'trust_root': 'http://launchpad.dev/',
30
... 'callback': 'http://openid.launchpad.dev/+edit'})
31
>>> browser.handleErrors = True
32
>>> browser.addHeader('REFERER', 'http://launchpad.dev/')
33
>>> browser.open('http://openid.launchpad.dev/+pre-authorize-rp?%s' % args)
35
# Since the user is not logged in, he'll have to login first.
37
'http://openid.launchpad.dev/+login...'
38
>>> browser.getControl(name='email', index=0).value = 'mark@example.com'
39
>>> browser.getControl(name='password').value = 'test'
40
>>> browser.handleErrors = False
41
>>> browser.getControl(name='continue').click()
43
# Now he's redirected to the callback page and the RP has been
46
'http://openid.launchpad.dev/+edit'
48
From now on, and until that authorization expires, the user won't need
49
to authorize that consumer when he wants to log into it.
51
# >>> from zope.interface import implements
52
# >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
53
# >>> from canonical.signon.layers import OpenIDLayer
55
# >>> class OpenIDTestRequest(LaunchpadTestRequest):
56
# ... implements(OpenIDLayer)
58
# >>> old_request = OpenIDTestRequest(
59
# ... SERVER_URL='http://openid.launchpad.dev',
60
# ... PATH_INFO='/+id/name16_oid')
61
# >>> login('foo.bar@canonical.com', old_request)
63
# First we will set up the helper view that lets us test the OpenID
65
>>> from openid.consumer.discover import OPENID_2_0_TYPE
66
>>> from openid.consumer.consumer import Consumer
67
>>> from openid.fetchers import setDefaultFetcher
68
>>> from openid.store.memstore import MemoryStore
69
>>> from canonical.signon.testing.openidhelpers import (
70
... make_identifier_select_endpoint, PublisherFetcher)
71
>>> setDefaultFetcher(PublisherFetcher())
73
# The authentication process is started by the relying party issuing a
74
# checkid_setup request, sending the user to Launchpad.
75
>>> openid_store = MemoryStore()
76
>>> consumer = Consumer(session={}, store=openid_store)
77
>>> request = consumer.beginWithoutDiscovery(
78
... make_identifier_select_endpoint(OPENID_2_0_TYPE))
79
>>> browser.open(request.redirectURL(
80
... 'http://launchpad.dev/', 'http://launchpad.dev/+openid-consumer'))
82
The user (mark) was already logged into Launchpad, so at this point,
83
instead of being presented with a form asking if they want to
84
authenticate to the relying party, they are sent directly to the RP.
87
http://launchpad.dev/+openid-consumer?...
89
If the HTTP referrer and the trust_root are not in our config's
90
openid_preauthorization_acl, we will not pre-authorize.
92
>>> browser.open('http://openid.launchpad.dev/+logout')
93
>>> browser.open('http://openid.launchpad.dev/+login')
94
>>> browser.getControl(name='email', index=0).value = 'test@canonical.com'
95
>>> browser.getControl(name='password').value = 'test'
96
>>> browser.getControl(name='continue').click()
98
>>> args = urlencode({
99
... 'trust_root': 'http://blueprints.launchpad.dev/',
100
... 'callback': 'http://openid.launchpad.dev/+edit'})
101
>>> browser.open('http://openid.launchpad.dev/+pre-authorize-rp?%s' % args)
102
Traceback (most recent call last):
104
HTTPError: HTTP Error 400: BAD REQUEST
106
>>> browser.open(request.redirectURL(
107
... 'http://blueprints.launchpad.dev/', 'http://blueprints.launchpad.dev/+openid-consumer'))
108
>>> print browser.title
109
Authenticate to http://blueprints.launchpad.dev/
111
>>> browser.open('http://openid.launchpad.dev/+logout')
112
>>> browser.open('http://openid.launchpad.dev/+login')
113
>>> browser.getControl(name='email', index=0).value = 'test@canonical.com'
114
>>> browser.getControl(name='password').value = 'test'
115
>>> browser.getControl(name='continue').click()
117
>>> args = urlencode({
118
... 'trust_root': 'http://example.com/',
119
... 'callback': 'http://launchpad.dev/people/+me'})
121
... 'http://openid.launchpad.dev/+pre-authorize-rp?%s' % args)
122
Traceback (most recent call last):
124
HTTPError: HTTP Error 400: BAD REQUEST
126
>>> browser.open(request.redirectURL(
127
... 'http://example.com/', 'http://example.com/+openid-consumer'))
128
>>> print browser.title
129
Authenticate to http://example.com/
131
>>> args = urlencode({
132
... 'trust_root': 'http://example.com/'})
134
... 'http://openid.launchpad.dev/+pre-authorize-rp?%s' % args)
135
Traceback (most recent call last):
137
HTTPError: HTTP Error 400: BAD REQUEST
139
>>> browser.open(request.redirectURL(
140
... 'http://example.com/', 'http://example.com/+openid-consumer'))
141
>>> print browser.title
142
Authenticate to http://example.com/