blob: 3e512a1e5d48fd4c3bac081d61fc201ee68b45ba [file] [log] [blame]
Joe Gregorioccc79542011-02-19 00:05:26 -05001#!/usr/bin/python2.4
2#
3# Copyright 2010 Google Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17
Joe Gregorio0bc70912011-05-24 15:30:49 -040018"""Oauth2client tests
Joe Gregorioccc79542011-02-19 00:05:26 -050019
Joe Gregorio0bc70912011-05-24 15:30:49 -040020Unit tests for oauth2client.
Joe Gregorioccc79542011-02-19 00:05:26 -050021"""
22
23__author__ = 'jcgregorio@google.com (Joe Gregorio)'
24
Joe Gregorio8b4c1732011-12-06 11:28:29 -050025import base64
Joe Gregorio562b7312011-09-15 09:06:38 -040026import datetime
Joe Gregorioe1de4162011-02-23 11:30:29 -050027import httplib2
Joe Gregorioccc79542011-02-19 00:05:26 -050028import unittest
29import urlparse
Joe Gregorioe1de4162011-02-23 11:30:29 -050030
Joe Gregorioccc79542011-02-19 00:05:26 -050031try:
32 from urlparse import parse_qs
33except ImportError:
34 from cgi import parse_qs
35
36from apiclient.http import HttpMockSequence
Joe Gregorio549230c2012-01-11 10:38:05 -050037from oauth2client.anyjson import simplejson
Joe Gregorioccc79542011-02-19 00:05:26 -050038from oauth2client.client import AccessTokenCredentials
39from oauth2client.client import AccessTokenCredentialsError
40from oauth2client.client import AccessTokenRefreshError
JacobMoshenko8e905102011-06-20 09:53:10 -040041from oauth2client.client import AssertionCredentials
Joe Gregorioccc79542011-02-19 00:05:26 -050042from oauth2client.client import FlowExchangeError
43from oauth2client.client import OAuth2Credentials
44from oauth2client.client import OAuth2WebServerFlow
Joe Gregoriof2326c02012-02-09 12:18:44 -050045from oauth2client.client import OOB_CALLBACK_URN
Joe Gregorio8b4c1732011-12-06 11:28:29 -050046from oauth2client.client import VerifyJwtTokenError
47from oauth2client.client import _extract_id_token
Joe Gregorioccc79542011-02-19 00:05:26 -050048
49
50class OAuth2CredentialsTests(unittest.TestCase):
51
52 def setUp(self):
53 access_token = "foo"
54 client_id = "some_client_id"
55 client_secret = "cOuDdkfjxxnv+"
56 refresh_token = "1/0/a.df219fjls0"
Joe Gregorio562b7312011-09-15 09:06:38 -040057 token_expiry = datetime.datetime.utcnow()
Joe Gregorioccc79542011-02-19 00:05:26 -050058 token_uri = "https://www.google.com/accounts/o8/oauth2/token"
59 user_agent = "refresh_checker/1.0"
60 self.credentials = OAuth2Credentials(
61 access_token, client_id, client_secret,
62 refresh_token, token_expiry, token_uri,
63 user_agent)
64
65 def test_token_refresh_success(self):
66 http = HttpMockSequence([
67 ({'status': '401'}, ''),
68 ({'status': '200'}, '{"access_token":"1/3w","expires_in":3600}'),
69 ({'status': '200'}, 'echo_request_headers'),
70 ])
71 http = self.credentials.authorize(http)
72 resp, content = http.request("http://example.com")
Joe Gregorio654f4a22012-02-09 14:15:44 -050073 self.assertEqual('Bearer 1/3w', content['Authorization'])
Joe Gregorioccc79542011-02-19 00:05:26 -050074
75 def test_token_refresh_failure(self):
76 http = HttpMockSequence([
77 ({'status': '401'}, ''),
78 ({'status': '400'}, '{"error":"access_denied"}'),
79 ])
80 http = self.credentials.authorize(http)
81 try:
82 http.request("http://example.com")
83 self.fail("should raise AccessTokenRefreshError exception")
84 except AccessTokenRefreshError:
85 pass
86
87 def test_non_401_error_response(self):
88 http = HttpMockSequence([
89 ({'status': '400'}, ''),
90 ])
91 http = self.credentials.authorize(http)
92 resp, content = http.request("http://example.com")
93 self.assertEqual(400, resp.status)
94
Joe Gregorio562b7312011-09-15 09:06:38 -040095 def test_to_from_json(self):
96 json = self.credentials.to_json()
97 instance = OAuth2Credentials.from_json(json)
Joe Gregorio654f4a22012-02-09 14:15:44 -050098 self.assertEqual(OAuth2Credentials, type(instance))
Joe Gregorio1daa71b2011-09-15 18:12:14 -040099 instance.token_expiry = None
100 self.credentials.token_expiry = None
101
Joe Gregorio654f4a22012-02-09 14:15:44 -0500102 self.assertEqual(instance.__dict__, self.credentials.__dict__)
Joe Gregorio562b7312011-09-15 09:06:38 -0400103
Joe Gregorioccc79542011-02-19 00:05:26 -0500104
105class AccessTokenCredentialsTests(unittest.TestCase):
106
107 def setUp(self):
108 access_token = "foo"
109 user_agent = "refresh_checker/1.0"
110 self.credentials = AccessTokenCredentials(access_token, user_agent)
111
112 def test_token_refresh_success(self):
113 http = HttpMockSequence([
114 ({'status': '401'}, ''),
115 ])
116 http = self.credentials.authorize(http)
117 try:
118 resp, content = http.request("http://example.com")
119 self.fail("should throw exception if token expires")
120 except AccessTokenCredentialsError:
121 pass
122 except Exception:
123 self.fail("should only throw AccessTokenCredentialsError")
124
125 def test_non_401_error_response(self):
126 http = HttpMockSequence([
127 ({'status': '400'}, ''),
128 ])
129 http = self.credentials.authorize(http)
Joe Gregorio83cd4392011-06-20 10:11:35 -0400130 resp, content = http.request('http://example.com')
Joe Gregorioccc79542011-02-19 00:05:26 -0500131 self.assertEqual(400, resp.status)
132
Joe Gregorio83cd4392011-06-20 10:11:35 -0400133 def test_auth_header_sent(self):
134 http = HttpMockSequence([
135 ({'status': '200'}, 'echo_request_headers'),
136 ])
137 http = self.credentials.authorize(http)
138 resp, content = http.request('http://example.com')
Joe Gregorio654f4a22012-02-09 14:15:44 -0500139 self.assertEqual('Bearer foo', content['Authorization'])
Joe Gregorioccc79542011-02-19 00:05:26 -0500140
Joe Gregorio8b4c1732011-12-06 11:28:29 -0500141
JacobMoshenko8e905102011-06-20 09:53:10 -0400142class TestAssertionCredentials(unittest.TestCase):
143 assertion_text = "This is the assertion"
144 assertion_type = "http://www.google.com/assertionType"
145
146 class AssertionCredentialsTestImpl(AssertionCredentials):
147
148 def _generate_assertion(self):
149 return TestAssertionCredentials.assertion_text
150
151 def setUp(self):
152 user_agent = "fun/2.0"
153 self.credentials = self.AssertionCredentialsTestImpl(self.assertion_type,
154 user_agent)
155
156 def test_assertion_body(self):
157 body = urlparse.parse_qs(self.credentials._generate_refresh_request_body())
Joe Gregorio654f4a22012-02-09 14:15:44 -0500158 self.assertEqual(self.assertion_text, body['assertion'][0])
159 self.assertEqual(self.assertion_type, body['assertion_type'][0])
JacobMoshenko8e905102011-06-20 09:53:10 -0400160
161 def test_assertion_refresh(self):
162 http = HttpMockSequence([
163 ({'status': '200'}, '{"access_token":"1/3w"}'),
164 ({'status': '200'}, 'echo_request_headers'),
165 ])
166 http = self.credentials.authorize(http)
167 resp, content = http.request("http://example.com")
Joe Gregorio654f4a22012-02-09 14:15:44 -0500168 self.assertEqual('Bearer 1/3w', content['Authorization'])
JacobMoshenko8e905102011-06-20 09:53:10 -0400169
170
Joe Gregorio8b4c1732011-12-06 11:28:29 -0500171class ExtractIdTokenText(unittest.TestCase):
172 """Tests _extract_id_token()."""
173
174 def test_extract_success(self):
175 body = {'foo': 'bar'}
176 payload = base64.urlsafe_b64encode(simplejson.dumps(body)).strip('=')
177 jwt = 'stuff.' + payload + '.signature'
178
179 extracted = _extract_id_token(jwt)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500180 self.assertEqual(extracted, body)
Joe Gregorio8b4c1732011-12-06 11:28:29 -0500181
182 def test_extract_failure(self):
183 body = {'foo': 'bar'}
184 payload = base64.urlsafe_b64encode(simplejson.dumps(body)).strip('=')
185 jwt = 'stuff.' + payload
186
187 self.assertRaises(VerifyJwtTokenError, _extract_id_token, jwt)
188
Joe Gregorioccc79542011-02-19 00:05:26 -0500189class OAuth2WebServerFlowTest(unittest.TestCase):
190
191 def setUp(self):
192 self.flow = OAuth2WebServerFlow(
193 client_id='client_id+1',
194 client_secret='secret+1',
195 scope='foo',
196 user_agent='unittest-sample/1.0',
197 )
198
199 def test_construct_authorize_url(self):
Joe Gregoriof2326c02012-02-09 12:18:44 -0500200 authorize_url = self.flow.step1_get_authorize_url('OOB_CALLBACK_URN')
Joe Gregorioccc79542011-02-19 00:05:26 -0500201
202 parsed = urlparse.urlparse(authorize_url)
203 q = parse_qs(parsed[4])
Joe Gregorio654f4a22012-02-09 14:15:44 -0500204 self.assertEqual('client_id+1', q['client_id'][0])
205 self.assertEqual('code', q['response_type'][0])
206 self.assertEqual('foo', q['scope'][0])
207 self.assertEqual('OOB_CALLBACK_URN', q['redirect_uri'][0])
208 self.assertEqual('offline', q['access_type'][0])
Joe Gregorio69a0aca2011-11-03 10:47:32 -0400209
210 def test_override_flow_access_type(self):
211 """Passing access_type overrides the default."""
212 flow = OAuth2WebServerFlow(
213 client_id='client_id+1',
214 client_secret='secret+1',
215 scope='foo',
216 user_agent='unittest-sample/1.0',
217 access_type='online'
218 )
Joe Gregoriof2326c02012-02-09 12:18:44 -0500219 authorize_url = flow.step1_get_authorize_url('OOB_CALLBACK_URN')
Joe Gregorio69a0aca2011-11-03 10:47:32 -0400220
221 parsed = urlparse.urlparse(authorize_url)
222 q = parse_qs(parsed[4])
Joe Gregorio654f4a22012-02-09 14:15:44 -0500223 self.assertEqual('client_id+1', q['client_id'][0])
224 self.assertEqual('code', q['response_type'][0])
225 self.assertEqual('foo', q['scope'][0])
226 self.assertEqual('OOB_CALLBACK_URN', q['redirect_uri'][0])
227 self.assertEqual('online', q['access_type'][0])
Joe Gregorioccc79542011-02-19 00:05:26 -0500228
229 def test_exchange_failure(self):
230 http = HttpMockSequence([
JacobMoshenko8e905102011-06-20 09:53:10 -0400231 ({'status': '400'}, '{"error":"invalid_request"}'),
Joe Gregorioccc79542011-02-19 00:05:26 -0500232 ])
233
234 try:
235 credentials = self.flow.step2_exchange('some random code', http)
236 self.fail("should raise exception if exchange doesn't get 200")
237 except FlowExchangeError:
238 pass
239
240 def test_exchange_success(self):
241 http = HttpMockSequence([
242 ({'status': '200'},
243 """{ "access_token":"SlAV32hkKG",
244 "expires_in":3600,
245 "refresh_token":"8xLOxBtZp8" }"""),
246 ])
247
248 credentials = self.flow.step2_exchange('some random code', http)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500249 self.assertEqual('SlAV32hkKG', credentials.access_token)
250 self.assertNotEqual(None, credentials.token_expiry)
251 self.assertEqual('8xLOxBtZp8', credentials.refresh_token)
Joe Gregorioccc79542011-02-19 00:05:26 -0500252
Joe Gregorioccc79542011-02-19 00:05:26 -0500253 def test_exchange_no_expires_in(self):
254 http = HttpMockSequence([
255 ({'status': '200'}, """{ "access_token":"SlAV32hkKG",
256 "refresh_token":"8xLOxBtZp8" }"""),
257 ])
258
259 credentials = self.flow.step2_exchange('some random code', http)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500260 self.assertEqual(None, credentials.token_expiry)
Joe Gregorioccc79542011-02-19 00:05:26 -0500261
Joe Gregorio8b4c1732011-12-06 11:28:29 -0500262 def test_exchange_id_token_fail(self):
263 http = HttpMockSequence([
264 ({'status': '200'}, """{ "access_token":"SlAV32hkKG",
265 "refresh_token":"8xLOxBtZp8",
266 "id_token": "stuff.payload"}"""),
267 ])
268
269 self.assertRaises(VerifyJwtTokenError, self.flow.step2_exchange,
270 'some random code', http)
271
272 def test_exchange_id_token_fail(self):
273 body = {'foo': 'bar'}
274 payload = base64.urlsafe_b64encode(simplejson.dumps(body)).strip('=')
Joe Gregoriobd512b52011-12-06 15:39:26 -0500275 jwt = (base64.urlsafe_b64encode('stuff')+ '.' + payload + '.' +
276 base64.urlsafe_b64encode('signature'))
Joe Gregorio8b4c1732011-12-06 11:28:29 -0500277
278 http = HttpMockSequence([
279 ({'status': '200'}, """{ "access_token":"SlAV32hkKG",
280 "refresh_token":"8xLOxBtZp8",
281 "id_token": "%s"}""" % jwt),
282 ])
283
284 credentials = self.flow.step2_exchange('some random code', http)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500285 self.assertEqual(credentials.id_token, body)
Joe Gregorio8b4c1732011-12-06 11:28:29 -0500286
Joe Gregorioccc79542011-02-19 00:05:26 -0500287
288if __name__ == '__main__':
289 unittest.main()