Skip to content

Commit 53eddff

Browse files
committed
introduced authentication context for graph client, a few fixes for onedrive tests
1 parent f5230e8 commit 53eddff

File tree

10 files changed

+281
-121
lines changed

10 files changed

+281
-121
lines changed

examples/auth/interactive.py

+2
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@
1616
client = GraphClient.with_token_interactive(test_tenant, test_client_id)
1717
me = client.me.get().execute_query()
1818
print("Welcome, {0}!".format(me.given_name))
19+
site = client.sites.root.get().execute_query()
20+
print("Site Url: {0}!".format(site.web_url))

examples/auth/with_user_creds.py

+4-19
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,11 @@
44
https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication
55
"""
66

7-
import msal
8-
97
from office365.graph_client import GraphClient
10-
from tests import test_client_id, test_tenant, test_user_credentials
11-
12-
13-
def acquire_token():
14-
authority_url = "https://login.microsoftonline.com/{0}".format(test_tenant)
15-
app = msal.PublicClientApplication(
16-
authority=authority_url, client_id=test_client_id
17-
)
18-
19-
result = app.acquire_token_by_username_password(
20-
username=test_user_credentials.userName,
21-
password=test_user_credentials.password,
22-
scopes=["https://graph.microsoft.com/.default"],
23-
)
24-
return result
25-
8+
from tests import test_client_id, test_password, test_tenant, test_username
269

27-
client = GraphClient(acquire_token)
10+
client = GraphClient.with_username_and_password(
11+
test_tenant, test_client_id, test_username, test_password
12+
)
2813
me = client.me.get().execute_query()
2914
print(me.user_principal_name)
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
Username Password Authentication flow
3+
4+
https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Username-Password-Authentication
5+
"""
6+
7+
import msal
8+
9+
from office365.graph_client import GraphClient
10+
from tests import test_client_id, test_tenant, test_user_credentials
11+
12+
13+
def acquire_token():
14+
authority_url = "https://login.microsoftonline.com/{0}".format(test_tenant)
15+
app = msal.PublicClientApplication(
16+
authority=authority_url, client_id=test_client_id
17+
)
18+
19+
result = app.acquire_token_by_username_password(
20+
username=test_user_credentials.userName,
21+
password=test_user_credentials.password,
22+
scopes=["https://graph.microsoft.com/.default"],
23+
)
24+
return result
25+
26+
27+
client = GraphClient(acquire_token)
28+
me = client.me.get().execute_query()
29+
print(me.user_principal_name)

office365/graph_client.py

+29-100
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
from office365.outlook.calendar.rooms.list import RoomList
5656
from office365.planner.planner import Planner
5757
from office365.reports.root import ReportRoot
58-
from office365.runtime.auth.token_response import TokenResponse
58+
from office365.runtime.auth.entra.authentication_context import AuthenticationContext
5959
from office365.runtime.client_runtime_context import ClientRuntimeContext
6060
from office365.runtime.http.http_method import HttpMethod
6161
from office365.runtime.http.request_options import RequestOptions
@@ -77,11 +77,16 @@
7777
class GraphClient(ClientRuntimeContext):
7878
"""Graph Service client"""
7979

80-
def __init__(self, acquire_token_callback):
81-
# type: (Callable[[], dict]) -> None
80+
def __init__(self, acquire_token_callback=None, auth_context=None):
81+
# type: (Callable[[], dict], AuthenticationContext) -> None
8282
super(GraphClient, self).__init__()
8383
self._pending_request = None
84-
self._acquire_token_callback = acquire_token_callback
84+
if acquire_token_callback is not None:
85+
self._auth_context = AuthenticationContext().with_access_token(
86+
acquire_token_callback
87+
)
88+
else:
89+
self._auth_context = auth_context
8590

8691
@staticmethod
8792
def with_certificate(
@@ -98,27 +103,10 @@ def with_certificate(
98103
:param Any token_cache: Default cache is in memory only,
99104
Refer https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
100105
"""
101-
if scopes is None:
102-
scopes = ["https://graph.microsoft.com/.default"]
103-
authority_url = "https://login.microsoftonline.com/{0}".format(tenant)
104-
import msal
105-
106-
app = msal.ConfidentialClientApplication(
107-
client_id,
108-
authority=authority_url,
109-
client_credential={
110-
"thumbprint": thumbprint,
111-
"private_key": private_key,
112-
},
113-
token_cache=token_cache, # Default cache is in memory only.
114-
# You can learn how to use SerializableTokenCache from
115-
# https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
116-
)
117-
118-
def _acquire_token():
119-
return app.acquire_token_for_client(scopes=scopes)
120-
121-
return GraphClient(_acquire_token)
106+
auth_ctx = AuthenticationContext(
107+
tenant=tenant, scopes=scopes, token_cache=token_cache
108+
).with_certificate(client_id, thumbprint, private_key)
109+
return GraphClient(auth_context=auth_ctx)
122110

123111
@staticmethod
124112
def with_client_secret(
@@ -135,22 +123,11 @@ def with_client_secret(
135123
:param Any token_cache: Default cache is in memory only,
136124
Refer https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
137125
"""
138-
if scopes is None:
139-
scopes = ["https://graph.microsoft.com/.default"]
140-
authority_url = "https://login.microsoftonline.com/{0}".format(tenant)
141-
import msal
142-
143-
app = msal.ConfidentialClientApplication(
144-
client_id,
145-
authority=authority_url,
146-
client_credential=client_secret,
147-
token_cache=token_cache,
148-
)
149126

150-
def _acquire_token():
151-
return app.acquire_token_for_client(scopes=scopes)
152-
153-
return GraphClient(_acquire_token)
127+
auth_ctx = AuthenticationContext(
128+
tenant=tenant, scopes=scopes, token_cache=token_cache
129+
).with_client_secret(client_id, client_secret)
130+
return GraphClient(auth_context=auth_ctx)
154131

155132
@staticmethod
156133
def with_token_interactive(tenant, client_id, username=None, scopes=None):
@@ -164,32 +141,10 @@ def with_token_interactive(tenant, client_id, username=None, scopes=None):
164141
:param str username: Typically a UPN in the form of an email address.
165142
:param list[str] or None scopes: Scopes requested to access an API
166143
"""
167-
if scopes is None:
168-
scopes = ["https://graph.microsoft.com/.default"]
169-
authority_url = "https://login.microsoftonline.com/{0}".format(tenant)
170-
import msal
171-
172-
app = msal.PublicClientApplication(client_id, authority=authority_url)
173-
174-
def _acquire_token():
175-
# The pattern to acquire a token looks like this.
176-
result = None
177-
178-
# Firstly, check the cache to see if this end user has signed in before
179-
accounts = app.get_accounts(username=username)
180-
if accounts:
181-
chosen = accounts[0] # Assuming the end user chose this one to proceed
182-
# Now let's try to find a token in cache for this account
183-
result = app.acquire_token_silent(scopes, account=chosen)
184-
185-
if not result:
186-
result = app.acquire_token_interactive(
187-
scopes,
188-
login_hint=username,
189-
)
190-
return result
191-
192-
return GraphClient(_acquire_token)
144+
auth_ctx = AuthenticationContext(
145+
tenant=tenant, scopes=scopes
146+
).with_token_interactive(client_id, username)
147+
return GraphClient(auth_context=auth_ctx)
193148

194149
@staticmethod
195150
def with_username_and_password(tenant, client_id, username, password, scopes=None):
@@ -203,31 +158,10 @@ def with_username_and_password(tenant, client_id, username, password, scopes=Non
203158
:param str password: The password.
204159
:param list[str] or None scopes: Scopes requested to access an API
205160
"""
206-
if scopes is None:
207-
scopes = ["https://graph.microsoft.com/.default"]
208-
authority_url = "https://login.microsoftonline.com/{0}".format(tenant)
209-
import msal
210-
211-
app = msal.PublicClientApplication(
212-
authority=authority_url,
213-
client_id=client_id,
214-
)
215-
216-
def _acquire_token():
217-
result = None
218-
accounts = app.get_accounts(username=username)
219-
if accounts:
220-
result = app.acquire_token_silent(scopes, account=accounts[0])
221-
222-
if not result:
223-
result = app.acquire_token_by_username_password(
224-
username=username,
225-
password=password,
226-
scopes=scopes,
227-
)
228-
return result
229-
230-
return GraphClient(_acquire_token)
161+
auth_ctx = AuthenticationContext(
162+
tenant=tenant, scopes=scopes
163+
).with_username_and_password(client_id, username, password)
164+
return GraphClient(auth_context=auth_ctx)
231165

232166
def execute_batch(self, items_per_batch=20, success_callback=None):
233167
"""Constructs and submit a batch request
@@ -238,7 +172,7 @@ def execute_batch(self, items_per_batch=20, success_callback=None):
238172
:param (List[ClientObject|ClientResult])-> None success_callback: A success callback
239173
"""
240174
batch_request = ODataV4BatchRequest(V4JsonFormat())
241-
batch_request.beforeExecute += self._authenticate_request
175+
batch_request.beforeExecute += self._auth_context.authenticate_request
242176
while self.has_pending_request:
243177
qry = self._get_next_query(items_per_batch)
244178
batch_request.execute_query(qry)
@@ -250,7 +184,9 @@ def pending_request(self):
250184
# type: () -> GraphRequest
251185
if self._pending_request is None:
252186
self._pending_request = GraphRequest()
253-
self._pending_request.beforeExecute += self._authenticate_request
187+
self._pending_request.beforeExecute += (
188+
self._auth_context.authenticate_request
189+
)
254190
self._pending_request.beforeExecute += self._build_specific_query
255191
return self._pending_request
256192

@@ -266,13 +202,6 @@ def _build_specific_query(self, request):
266202
elif isinstance(self.current_query, DeleteEntityQuery):
267203
request.method = HttpMethod.Delete
268204

269-
def _authenticate_request(self, request):
270-
# type: (RequestOptions) -> None
271-
"""Authenticate request."""
272-
token_json = self._acquire_token_callback()
273-
token = TokenResponse.from_json(token_json)
274-
request.ensure_header("Authorization", "Bearer {0}".format(token.accessToken))
275-
276205
@property
277206
def admin(self):
278207
"""A container for administrator functionality for SharePoint and OneDrive."""

office365/runtime/auth/entra/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)