Skip to content

Commit 61affb0

Browse files
authored
Auth: Add OAuth 2.0 authentication support (atlassian-api#666)
* Auth: Add OAuth 2.0 authentication support The current implementation only supports authentication with OAuth 1.0. They are mostly used by the server variants. The cloud services now use OAuth 2.0. With this commit it is now supported without breaking the support for OAuth 1.0. * Auth: Documentation for OAuth 2.0 authentication * Auth: Example for OAuth 2.0 authentication Adds an example Flask server to show how to use the new OAuth 2.0 authentication. * Auth: Remove trailing whitespace * Auth: Reformatting according to qa
1 parent 49858f3 commit 61affb0

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

atlassian/rest_client.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import requests
66
from oauthlib.oauth1 import SIGNATURE_RSA
7-
from requests_oauthlib import OAuth1
7+
from requests_oauthlib import OAuth1, OAuth2
88
from six.moves.urllib.parse import urlencode
99

1010
from atlassian.request_utils import get_default_logger
@@ -45,6 +45,7 @@ def __init__(
4545
verify_ssl=True,
4646
session=None,
4747
oauth=None,
48+
oauth2=None,
4849
cookies=None,
4950
advanced_mode=None,
5051
kerberos=None,
@@ -70,6 +71,8 @@ def __init__(
7071
self._create_basic_session(username, password)
7172
elif oauth is not None:
7273
self._create_oauth_session(oauth)
74+
elif oauth2 is not None:
75+
self._create_oauth2_session(oauth2)
7376
elif kerberos is not None:
7477
self._create_kerberos_session(kerberos)
7578
elif cookies is not None:
@@ -110,6 +113,19 @@ def _create_oauth_session(self, oauth_dict):
110113
)
111114
self._session.auth = oauth
112115

116+
def _create_oauth2_session(self, oauth_dict):
117+
"""
118+
Use OAuth 2.0 Authentication
119+
:param oauth_dict: Dictionary containing access information. Must at
120+
least contain "client_id" and "token". "token" is a dictionary and
121+
must at least contain "access_token" and "token_type".
122+
:return:
123+
"""
124+
if "client" not in oauth_dict:
125+
oauth_dict["client"] = None
126+
oauth = OAuth2(oauth_dict["client_id"], oauth_dict["client"], oauth_dict["token"])
127+
self._session.auth = oauth
128+
113129
def _update_header(self, key, value):
114130
"""
115131
Update header for exist session

docs/index.rst

+20
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,26 @@ Alternatively OAuth can be used:
117117
url='http://localhost:8080',
118118
oauth=oauth_dict)
119119
120+
OAuth 2.0 is also supported:
121+
122+
.. code-block:: python
123+
124+
from atlassian.bitbucket.cloud import Cloud
125+
126+
# token is a dictionary and must at least contain "access_token"
127+
# and "token_type".
128+
oauth2_dict = {
129+
"client_id": client_id,
130+
"token": token}
131+
132+
bitbucket_cloud = Cloud(
133+
url='https://api.bitbucket.org/',
134+
oauth2=oauth2_dict)
135+
136+
# For a detailed example see bitbucket_oauth2.py in
137+
# examples/bitbucket
138+
139+
120140
Or Kerberos *(installation with kerberos extra necessary)*:
121141

122142
.. code-block:: python
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Example server with Flask demonstrating use of OAuth 2.0. Server
2+
# needs to be deployed. Example code is requesting access token from
3+
# Bitbucket. User has to grant access rights. After authorization the
4+
# token and the available workspaces are returned.
5+
6+
from requests_oauthlib import OAuth2Session
7+
from atlassian.bitbucket.cloud import Cloud
8+
from flask import Flask, request, redirect, session
9+
10+
app = Flask(__name__)
11+
app.secret_key = ""
12+
13+
# Bitbucket OAuth URLs
14+
authorization_base_url = "https://bitbucket.org/site/oauth2/authorize"
15+
token_url = "https://bitbucket.org/site/oauth2/access_token"
16+
17+
# 1. Create OAuth consumer
18+
# Go to "Your profile and setting" -> "All workspaces" -> "Manage".
19+
# Go to "Apps and features" -> "OAuth consumers".
20+
# Click "Add consumer". Fill in Name and Callback URL. Set permissions
21+
# as needed. Click save and copy client id and secret.
22+
client_id = ""
23+
client_secret = ""
24+
25+
26+
# 2. Redirect to Bitbucket for authorization
27+
# The server request to {server_url}/login is redirected to Bitbucket.
28+
# The user is asked to grant access permissions.
29+
@app.route("/login")
30+
def login():
31+
bitbucket = OAuth2Session(client_id)
32+
authorization_url, state = bitbucket.authorization_url(authorization_base_url)
33+
session["oauth_state"] = state
34+
return redirect(authorization_url)
35+
36+
37+
# 3. Bitbucket sends callback with token
38+
# Bitbucket is calling the Callback URL specified in the OAuth
39+
# consumer. This should be set to {server_url}/callback. The callback
40+
# contains the access token.
41+
@app.route("/callback")
42+
def callback():
43+
bitbucket = OAuth2Session(client_id, state=session["oauth_state"])
44+
token = bitbucket.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url)
45+
46+
return "Token: {}<p />Workspaces: {}".format(token, ", ".join(get_workspaces(token)))
47+
48+
49+
# 4. Token used for Bitbucket Python API
50+
# Bitbucket Cloud API library is called with token information. User is
51+
# authenticated with OAuth 2.0.
52+
def get_workspaces(token):
53+
oauth2 = {"client_id": client_id, "token": token}
54+
55+
bitbucket = Cloud(url="https://api.bitbucket.org/", oauth2=oauth2, cloud=True)
56+
57+
return [ws.name for ws in bitbucket.workspaces.each()]

0 commit comments

Comments
 (0)