Skip to content

Commit

Permalink
Merge pull request #24 from samkuehn/box-backend
Browse files Browse the repository at this point in the history
Add support for box.net
  • Loading branch information
omab committed Sep 9, 2013
2 parents ef7b22d + 547569e commit 5bc14c3
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -39,3 +39,5 @@ local_settings.py
sessions/
_build/
fabfile.py

.DS_Store
2 changes: 2 additions & 0 deletions README.rst
Expand Up @@ -43,6 +43,7 @@ or extend current one):
* Angel_ OAuth2
* Behance_ OAuth2
* Bitbucket_ OAuth1
* Box_ OAuth2
* Dailymotion_ OAuth2
* Disqus_ OAuth2
* Douban_ OAuth1 and OAuth2
Expand Down Expand Up @@ -179,6 +180,7 @@ check `django-social-auth LICENCE`_ for details:
.. _Angel: https://angel.co
.. _Behance: https://www.behance.net
.. _Bitbucket: https://bitbucket.org
.. _Box: https://www.box.com
.. _Dailymotion: https://dailymotion.com
.. _Disqus: https://disqus.com
.. _Douban: http://www.douban.com
Expand Down
24 changes: 24 additions & 0 deletions docs/backends/box.rst
@@ -0,0 +1,24 @@
Box.net
======

Box works similar to Facebook (OAuth2).

- Register an application at `Manage Box Applications`_

- Fill the **Consumer Key** and **Consumer Secret** values in your settings::

SOCIAL_AUTH_BOX_KEY = ''
SOCIAL_AUTH_BOX_SECRET = ''

- By default the token is not permanent, it will last an hour::

To refresh the access token just do::

from social.apps.django_app.utils import load_strategy

strategy = load_strategy(backend='box')
user = User.objects.get(pk=foo)
social = user.social_auth.filter(provider='box')[0]
social.refresh_token(strategy=strategy)

.. _Manage Box Applications: https://app.box.com/developers/services
2 changes: 2 additions & 0 deletions docs/intro.rst
Expand Up @@ -35,6 +35,7 @@ or extend current one):
* Angel_ OAuth2
* Behance_ OAuth2
* Bitbucket_ OAuth1
* Box_ OAuth2
* Dailymotion_ OAuth2
* Disqus_ OAuth2
* Douban_ OAuth1 and OAuth2
Expand Down Expand Up @@ -105,6 +106,7 @@ suits your project. Check `Authentication Pipeline`_ section.
.. _Angel: https://angel.co
.. _Behance: https://www.behance.net
.. _Bitbucket: https://bitbucket.org
.. _Box: https://www.box.com
.. _Dailymotion: https://dailymotion.com
.. _Disqus: https://disqus.com
.. _Douban: http://www.douban.com
Expand Down
1 change: 1 addition & 0 deletions examples/django_example/dj/settings.py
Expand Up @@ -131,6 +131,7 @@
'social.backends.angel.AngelOAuth2',
'social.backends.behance.BehanceOAuth2',
'social.backends.bitbucket.BitbucketOAuth',
'social.backends.box.BoxOAuth2',
'social.backends.linkedin.LinkedinOAuth',
'social.backends.linkedin.LinkedinOAuth2',
'social.backends.github.GithubOAuth2',
Expand Down
1 change: 1 addition & 0 deletions examples/django_example/dj/templates/home.html
Expand Up @@ -14,6 +14,7 @@
<a href="{% url 'social:begin' "angel" %}">Angel OAuth2</a> <br />
<a href="{% url 'social:begin' "behance" %}">Behance OAuth2</a> <br />
<a href="{% url 'social:begin' "bitbucket" %}">Bitbucket OAuth</a> <br />
<a href="{% url 'social:begin' "box" %}">Box.net OAuth2</a> <br />
<a href="{% url 'social:begin' "linkedin" %}">LinkedIn OAuth</a> <br />
<a href="{% url 'social:begin' "github" %}">Github OAuth2</a> <br />
<a href="{% url 'social:begin' "foursquare" %}">Foursquare OAuth2</a> <br />
Expand Down
1 change: 1 addition & 0 deletions examples/flask_example/settings.py
Expand Up @@ -28,6 +28,7 @@
'social.backends.angel.AngelOAuth2',
'social.backends.behance.BehanceOAuth2',
'social.backends.bitbucket.BitbucketOAuth',
'social.backends.box.BoxOAuth2',
'social.backends.linkedin.LinkedinOAuth',
'social.backends.github.GithubOAuth2',
'social.backends.foursquare.FoursquareOAuth2',
Expand Down
1 change: 1 addition & 0 deletions examples/flask_example/templates/home.html
Expand Up @@ -13,6 +13,7 @@
<a href="{{ url_for("social.auth", backend="angel") }}">Angel OAuth2</a> <br />
<a href="{{ url_for("social.auth", backend="behance") }}">Behance OAuth2</a> <br />
<a href="{{ url_for("social.auth", backend="bitbucket") }}">Bitbucket OAuth</a> <br />
<a href="{{ url_for("social.auth", backend="box") }}">Box OAuth2</a> <br />
<a href="{{ url_for("social.auth", backend="linkedin") }}">LinkedIn OAuth</a> <br />
<a href="{{ url_for("social.auth", backend="github") }}">Github OAuth2</a> <br />
<a href="{{ url_for("social.auth", backend="foursquare") }}">Foursquare OAuth2</a> <br />
Expand Down
1 change: 1 addition & 0 deletions examples/webpy_example/app.py
Expand Up @@ -31,6 +31,7 @@
'social.backends.angel.AngelOAuth2',
'social.backends.behance.BehanceOAuth2',
'social.backends.bitbucket.BitbucketOAuth',
'social.backends.box.BoxOAuth2',
'social.backends.linkedin.LinkedinOAuth',
'social.backends.github.GithubOAuth2',
'social.backends.foursquare.FoursquareOAuth2',
Expand Down
1 change: 1 addition & 0 deletions examples/webpy_example/templates/home.html
Expand Up @@ -13,6 +13,7 @@
<a href="/login/angel/">Angel OAuth2</a> <br />
<a href="/login/behance/">Behance OAuth2</a> <br />
<a href="/login/bitbucket/">Bitbucket OAuth</a> <br />
<a href="/login/box/">Box OAuth2</a> <br />
<a href="/login/linkedin/">LinkedIn OAuth</a> <br />
<a href="/login/github/">Github OAuth2</a> <br />
<a href="/login/foursquare/">Foursquare OAuth2</a> <br />
Expand Down
59 changes: 59 additions & 0 deletions social/backends/box.py
@@ -0,0 +1,59 @@
"""
Box.net OAuth support.
This contribution adds support for GitHub OAuth service. The settings
SOCIAL_AUTH_BOX_KEY and SOCIAL_AUTH_BOX_SECRET must be defined with the values
given by Box.net application registration process.
Extended permissions are supported by defining BOX_EXTENDED_PERMISSIONS
setting, it must be a list of values to request.
By default account id and token expiration time are stored in extra_data
field, check OAuthBackend class for details on how to extend it.
"""
from social.backends.oauth import BaseOAuth2


class BoxOAuth2(BaseOAuth2):
"""Box.net OAuth authentication backend"""
name = 'box'
AUTHORIZATION_URL = 'https://www.box.com/api/oauth2/authorize'
ACCESS_TOKEN_METHOD = 'POST'
ACCESS_TOKEN_URL = 'https://www.box.com/api/oauth2/token'
REVOKE_TOKEN_URL = 'https://www.box.com/api/oauth2/revoke'
SCOPE_SEPARATOR = ','
EXTRA_DATA = [
('refresh_token', 'refresh_token', True),
('id', 'id'),
('expires', 'expires'),
]

def do_auth(self, access_token, response=None, *args, **kwargs):
response = response or {}
data = self.user_data(access_token)

data['access_token'] = response.get('access_token')
data['refresh_token'] = response.get('refresh_token')
data['expires'] = response.get('expires_in')
kwargs.update({'backend': self, 'response': data})
return self.strategy.authenticate(*args, **kwargs)

def get_user_details(self, response):
"""Return user details Box.net account"""
return {'username': response.get('login'),
'email': response.get('login') or '',
'first_name': response.get('name')}

def user_data(self, access_token, *args, **kwargs):
"""Loads user data from service"""
params = self.setting('PROFILE_EXTRA_PARAMS', {})
params['access_token'] = access_token
return self.get_json('https://api.box.com/2.0/users/me',
params=params)

def refresh_token(self, token, *args, **kwargs):
params = self.refresh_token_params(token, *args, **kwargs)
request = self.request(self.REFRESH_TOKEN_URL or self.ACCESS_TOKEN_URL,
data=params, headers=self.auth_headers(),
method='POST')
return self.process_refresh_token_response(request, *args, **kwargs)
66 changes: 66 additions & 0 deletions tests/backends/box_test.py
@@ -0,0 +1,66 @@
from sure import expect
from tests.oauth import OAuth2Test
import json


class BoxOAuth2Test(OAuth2Test):
backend_path = 'social.backends.box.BoxOAuth2'
user_data_url = 'https://api.box.com/2.0/users/me'
expected_username = 'sean+awesome@box.com'
access_token_body = json.dumps({
"access_token": "T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl",
"expires_in": 3600,
"restricted_to": [],
"token_type": "bearer",
"refresh_token": "J7rxTiWOHMoSC1isKZKBZWizoRXjkQzig5C6jFgCVJ9bUnsUfGMinKBDLZWP9BgR"
})
user_data_body = json.dumps({
"type": "user",
"id": "181216415",
"name": "sean rose",
"login": "sean+awesome@box.com",
"created_at": "2012-05-03T21:39:11-07:00",
"modified_at": "2012-11-14T11:21:32-08:00",
"role": "admin",
"language": "en",
"space_amount": 11345156112,
"space_used": 1237009912,
"max_upload_size": 2147483648,
"tracking_codes": [],
"can_see_managed_users": True,
"is_sync_enabled": True,
"status": "active",
"job_title": "",
"phone": "6509241374",
"address": "",
"avatar_url": "https://www.box.com/api/avatar/large/181216415",
"is_exempt_from_device_limits": False,
"is_exempt_from_login_verification": False,
"enterprise": {
"type": "enterprise",
"id": "17077211",
"name": "seanrose enterprise"
}
})
refresh_token_body = json.dumps({
"access_token": "T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl",
"expires_in": 3600,
"restricted_to": [],
"token_type": "bearer",
"refresh_token": "J7rxTiWOHMoSC1isKZKBZWizoRXjkQzig5C6jFgCVJ9bUnsUfGMinKBDLZWP9BgR"
})

def test_login(self):
self.do_login()

def test_partial_pipeline(self):
self.do_partial_pipeline()

def refresh_token_arguments(self):
uri = self.strategy.build_absolute_uri('/complete/box/')
return {'redirect_uri': uri}

def test_refresh_token(self):
user, social = self.do_refresh_token()
expect(social.extra_data['access_token']).to.equal('T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl')

0 comments on commit 5bc14c3

Please sign in to comment.