Skip to content

Commit 5451d36

Browse files
authored
Merge pull request #10 from AzureAD/refine-docs
Per discussion in today's meeting, we decided to merge this PR now.
2 parents 0cdaf52 + 7fef8f9 commit 5451d36

File tree

4 files changed

+108
-29
lines changed

4 files changed

+108
-29
lines changed

docs/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
author = u'Microsoft'
2525

2626
# The short X.Y version
27-
version = u''
27+
from msal import __version__ as version
2828
# The full version, including alpha/beta/rc tags
29-
release = u'0.1.0'
29+
release = version
3030

3131

3232
# -- General configuration ---------------------------------------------------
@@ -176,4 +176,4 @@
176176
epub_exclude_files = ['search.html']
177177

178178

179-
# -- Extension configuration -------------------------------------------------
179+
# -- Extension configuration -------------------------------------------------

msal/application.py

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,41 @@ def __init__(
5555
client_credential=None, authority=None, validate_authority=True,
5656
token_cache=None,
5757
verify=True, proxies=None, timeout=None):
58-
"""
59-
:param client_credential: It can be a string containing client secret,
60-
or an X509 certificate container in this form:
58+
"""Create an instance of application.
59+
60+
:param client_id: Your app has a clinet_id after you register it on AAD.
61+
:param client_credential:
62+
For :class:`PublicClientApplication`, you simply use `None` here.
63+
For :class:`ConfidentialClientApplication`,
64+
it can be a string containing client secret,
65+
or an X509 certificate container in this form::
6166
6267
{
6368
"private_key": "...-----BEGIN PRIVATE KEY-----...",
6469
"thumbprint": "A1B2C3D4E5F6...",
6570
}
71+
72+
:param str authority:
73+
A URL that identifies a token authority. It should be of the format
74+
https://login.microsoftonline.com/your_tenant
75+
By default, we will use https://login.microsoftonline.com/common
76+
:param bool validate_authority: (optional) Turns authority validation
77+
on or off. This parameter default to true.
78+
:param TokenCache cache:
79+
Sets the token cache used by this ClientApplication instance.
80+
By default, an in-memory cache will be created and used.
81+
:param verify: (optional)
82+
It will be passed to the
83+
`verify parameter in the underlying requests library
84+
<http://docs.python-requests.org/en/v2.9.1/user/advanced/#ssl-cert-verification>`_
85+
:param proxies: (optional)
86+
It will be passed to the
87+
`proxies parameter in the underlying requests library
88+
<http://docs.python-requests.org/en/v2.9.1/user/advanced/#proxies>`_
89+
:param timeout: (optional)
90+
It will be passed to the
91+
`timeout parameter in the underlying requests library
92+
<http://docs.python-requests.org/en/v2.9.1/user/advanced/#timeouts>`_
6693
"""
6794
self.client_id = client_id
6895
self.client_credential = client_credential
@@ -123,13 +150,14 @@ def get_authorization_request_url(
123150
**kwargs):
124151
"""Constructs a URL for you to start a Authorization Code Grant.
125152
126-
:param scopes:
153+
:param list[str] scopes: (Required)
127154
Scopes requested to access a protected API (a resource).
128155
:param str state: Recommended by OAuth2 for CSRF protection.
129-
:param login_hint:
156+
:param str login_hint:
130157
Identifier of the user. Generally a User Principal Name (UPN).
131-
:param redirect_uri:
158+
:param str redirect_uri:
132159
Address to return to upon receiving a response from the authority.
160+
:return: The authorization url as a string.
133161
"""
134162
""" # TBD: this would only be meaningful in a new acquire_token_interactive()
135163
:param additional_scope: Additional scope is a concept only in AAD.
@@ -161,7 +189,8 @@ def acquire_token_by_authorization_code(
161189
"""The second half of the Authorization Code Grant.
162190
163191
:param code: The authorization code returned from Authorization Server.
164-
:param scopes:
192+
:param list[str] scopes: (Required)
193+
Scopes requested to access a protected API (a resource).
165194
166195
If you requested user consent for multiple resources, here you will
167196
typically want to provide a subset of what you required in AuthCode.
@@ -175,6 +204,11 @@ def acquire_token_by_authorization_code(
175204
recipient, called audience.
176205
So the developer need to specify a scope so that we can restrict the
177206
token to be issued for the corresponding audience.
207+
208+
:return: A dict representing the json response from AAD:
209+
210+
- A successful response would contain "access_token" key,
211+
- an error response would contain "error" and usually "error_description".
178212
"""
179213
# If scope is absent on the wire, STS will give you a token associated
180214
# to the FIRST scope sent during the authorization request.
@@ -190,13 +224,15 @@ def acquire_token_by_authorization_code(
190224
def get_accounts(self, username=None):
191225
"""Get a list of accounts which previously signed in, i.e. exists in cache.
192226
193-
An account can later be used in acquire_token_silent() to find its tokens.
194-
Each account is a dict. For now, we only document its "username" field.
195-
Your app can choose to display those information to end user,
196-
and allow them to choose one of them to proceed.
227+
An account can later be used in :func:`~acquire_token_silent`
228+
to find its tokens.
197229
198230
:param username:
199231
Filter accounts with this username only. Case insensitive.
232+
:return: A list of account objects.
233+
Each account is a dict. For now, we only document its "username" field.
234+
Your app can choose to display those information to end user,
235+
and allow user to choose one of his/her accounts to proceed.
200236
"""
201237
# The following implementation finds accounts only from saved accounts,
202238
# but does NOT correlate them with saved RTs. It probably won't matter,
@@ -224,15 +260,17 @@ def acquire_token_silent(
224260
or by finding a valid refresh token from cache and then automatically
225261
use it to redeem a new access token.
226262
227-
The return value will be an new or cached access token, or None.
228-
229-
:param scopes: Scopes, represented as a list of strings
263+
:param list[str] scopes: (Required)
264+
Scopes requested to access a protected API (a resource).
230265
:param account:
231-
one of the account object returned by get_accounts(),
266+
one of the account object returned by :func:`~get_accounts`,
232267
or use None when you want to find an access token for this client.
233268
:param force_refresh:
234269
If True, it will skip Access Token look-up,
235270
and try to find a Refresh Token to obtain a new Access Token.
271+
:return:
272+
- A dict containing "access_token" key, when cache lookup succeeds.
273+
- None when cache lookup does not yield anything.
236274
"""
237275
assert isinstance(scopes, list), "Invalid parameter type"
238276
the_authority = Authority(authority) if authority else self.authority
@@ -286,18 +324,33 @@ def __init__(self, client_id, client_credential=None, **kwargs):
286324
client_id, client_credential=None, **kwargs)
287325

288326
def initiate_device_flow(self, scopes=None, **kwargs):
327+
"""Initiate a Device Flow instance,
328+
which will be used in :func:`~acquire_token_by_device_flow`.
329+
330+
:param list[str] scopes:
331+
Scopes requested to access a protected API (a resource).
332+
:return: A dict representing a newly created Device Flow object.
333+
334+
- A successful response would contain "user_code" key, among others
335+
- an error response would contain some other readable key/value pairs.
336+
"""
289337
return self.client.initiate_device_flow(
290338
scope=decorate_scope(scopes or [], self.client_id),
291339
**kwargs)
292340

293341
def acquire_token_by_device_flow(self, flow, **kwargs):
294342
"""Obtain token by a device flow object, with customizable polling effect.
295343
296-
Args:
297-
flow (dict):
298-
A dict previously generated by initiate_device_flow(...).
299-
You can exit the polling loop early, by changing the value of
300-
its "expires_at" key to 0, at any time.
344+
:param dict flow:
345+
A dict previously generated by :func:`~initiate_device_flow`.
346+
By default, this method's polling effect will block current thread.
347+
You can abort the polling loop at any time,
348+
by changing the value of the flow's "expires_at" key to 0.
349+
350+
:return: A dict representing the json response from AAD:
351+
352+
- A successful response would contain "access_token" key,
353+
- an error response would contain "error" and usually "error_description".
301354
"""
302355
return self.client.obtain_token_by_device_flow(
303356
flow,
@@ -308,7 +361,18 @@ def acquire_token_by_device_flow(self, flow, **kwargs):
308361

309362
def acquire_token_by_username_password(
310363
self, username, password, scopes=None, **kwargs):
311-
"""Gets a token for a given resource via user credentails."""
364+
"""Gets a token for a given resource via user credentails.
365+
366+
:param str username: Typically a UPN in the form of an email address.
367+
:param str password: The password.
368+
:param list[str] scopes:
369+
Scopes requested to access a protected API (a resource).
370+
371+
:return: A dict representing the json response from AAD:
372+
373+
- A successful response would contain "access_token" key,
374+
- an error response would contain "error" and usually "error_description".
375+
"""
312376
scopes = decorate_scope(scopes, self.client_id)
313377
if not self.authority.is_adfs:
314378
user_realm_result = self.authority.user_realm_discovery(username)
@@ -348,7 +412,16 @@ def _acquire_token_by_username_password_federated(
348412
class ConfidentialClientApplication(ClientApplication): # server-side web app
349413

350414
def acquire_token_for_client(self, scopes, **kwargs):
351-
"""Acquires token from the service for the confidential client."""
415+
"""Acquires token from the service for the confidential client.
416+
417+
:param list[str] scopes: (Required)
418+
Scopes requested to access a protected API (a resource).
419+
420+
:return: A dict representing the json response from AAD:
421+
422+
- A successful response would contain "access_token" key,
423+
- an error response would contain "error" and usually "error_description".
424+
"""
352425
# TBD: force_refresh behavior
353426
return self.client.obtain_token_for_client(
354427
scope=scopes, # This grant flow requires no scope decoration

msal/token_cache.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ def base64decode(raw): # This can handle a padding-less raw input
1818
class TokenCache(object):
1919
"""This is considered as a base class containing minimal cache behavior.
2020
21-
Although this class already maintains tokens using unified schema,
22-
it does not serialize/persist them. See subclass SerializableTokenCache
23-
for more details.
21+
Although it maintains tokens using unified schema across all MSAL libraries,
22+
this class does not serialize/persist them.
23+
See subclass :class:`SerializableTokenCache` for details on serialization.
2424
"""
2525

2626
class CredentialType:
@@ -169,7 +169,8 @@ class SerializableTokenCache(TokenCache):
169169
"""This serialization can be a starting point to implement your own persistence.
170170
171171
This class does NOT actually persist the cache on disk/db/etc..
172-
Depends on your need, the following file-based persistence may be sufficient:
172+
Depending on your need,
173+
the following simple recipe for file-based persistence may be sufficient::
173174
174175
import atexit
175176
cache = SerializableTokenCache()
@@ -181,6 +182,10 @@ class SerializableTokenCache(TokenCache):
181182
)
182183
app = ClientApplication(..., token_cache=cache)
183184
...
185+
186+
:var bool has_state_changed:
187+
Indicates whether the cache state has changed since last
188+
:func:`~serialize` or :func:`~deserialize` call.
184189
"""
185190
def add(self, event):
186191
super(SerializableTokenCache, self).add(event)

tests/test_application.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def test_device_flow(self):
109109
self.app = PublicClientApplication(
110110
CONFIG["client_id"], authority=CONFIG["authority"])
111111
flow = self.app.initiate_device_flow(scopes=CONFIG.get("scope"))
112+
assert "user_code" in flow, str(flow) # Provision or policy might block DF
112113
logging.warn(flow["message"])
113114

114115
duration = 30

0 commit comments

Comments
 (0)