Skip to content

Commit 652dcbf

Browse files
authored
Merge pull request #193 from AzureAD/acquire_token_by_refresh_token
acquire_token_by_refresh_token() for RT migration
2 parents e3e9740 + e3fa226 commit 652dcbf

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

msal/application.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,36 @@ def _validate_ssh_cert_input_data(self, data):
700700
"you must include a string parameter named 'key_id' "
701701
"which identifies the key in the 'req_cnf' argument.")
702702

703+
def acquire_token_by_refresh_token(self, refresh_token, scopes):
704+
"""Acquire token(s) based on a refresh token (RT) obtained from elsewhere.
705+
706+
You use this method only when you have old RTs from elsewhere,
707+
and now you want to migrate them into MSAL.
708+
Calling this method results in new tokens automatically storing into MSAL.
709+
710+
You do NOT need to use this method if you are already using MSAL.
711+
MSAL maintains RT automatically inside its token cache,
712+
and an access token can be retrieved
713+
when you call :func:`~acquire_token_silent`.
714+
715+
:param str refresh_token: The old refresh token, as a string.
716+
717+
:param list scopes:
718+
The scopes associate with this old RT.
719+
Each scope needs to be in the Microsoft identity platform (v2) format.
720+
See `Scopes not resources <https://docs.microsoft.com/en-us/azure/active-directory/develop/migrate-python-adal-msal#scopes-not-resources>`_.
721+
722+
:return:
723+
* A dict contains "error" and some other keys, when error happened.
724+
* A dict contains no "error" key means migration was successful.
725+
"""
726+
return self.client.obtain_token_by_refresh_token(
727+
refresh_token,
728+
decorate_scope(scopes, self.client_id),
729+
rt_getter=lambda rt: rt,
730+
on_updating_rt=False,
731+
)
732+
703733

704734
class PublicClientApplication(ClientApplication): # browser app or mobile app
705735

sample/migrate_rt.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
The configuration file would look like this:
3+
4+
{
5+
"authority": "https://login.microsoftonline.com/organizations",
6+
"client_id": "your_client_id",
7+
"scope": ["User.ReadBasic.All"],
8+
// You can find the other permission names from this document
9+
// https://docs.microsoft.com/en-us/graph/permissions-reference
10+
}
11+
12+
You can then run this sample with a JSON configuration file:
13+
14+
python sample.py parameters.json
15+
"""
16+
17+
import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1]
18+
import json
19+
import logging
20+
21+
import msal
22+
23+
24+
# Optional logging
25+
# logging.basicConfig(level=logging.DEBUG) # Enable DEBUG log for entire script
26+
# logging.getLogger("msal").setLevel(logging.INFO) # Optionally disable MSAL DEBUG logs
27+
28+
def get_preexisting_rt_and_their_scopes_from_elsewhere():
29+
# Maybe you have an ADAL-powered app like this
30+
# https://github.com/AzureAD/azure-activedirectory-library-for-python/blob/1.2.3/sample/device_code_sample.py#L72
31+
# which uses a resource rather than a scope,
32+
# you need to convert your v1 resource into v2 scopes
33+
# See https://docs.microsoft.com/azure/active-directory/develop/azure-ad-endpoint-comparison#scopes-not-resources
34+
# You may be able to append "/.default" to your v1 resource to form a scope
35+
# See https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#the-default-scope
36+
37+
# Or maybe you have an app already talking to Microsoft identity platform v2,
38+
# powered by some 3rd-party auth library, and persist its tokens somehow.
39+
40+
# Either way, you need to extract RTs from there, and return them like this.
41+
return [
42+
("old_rt_1", ["scope1", "scope2"]),
43+
("old_rt_2", ["scope3", "scope4"]),
44+
]
45+
46+
47+
# We will migrate all the old RTs into a new app powered by MSAL
48+
config = json.load(open(sys.argv[1]))
49+
app = msal.PublicClientApplication(
50+
config["client_id"], authority=config["authority"],
51+
# token_cache=... # Default cache is in memory only.
52+
# You can learn how to use SerializableTokenCache from
53+
# https://msal-python.rtfd.io/en/latest/#msal.SerializableTokenCache
54+
)
55+
56+
# We choose a migration strategy of migrating all RTs in one loop
57+
for old_rt, scopes in get_preexisting_rt_and_their_scopes_from_elsewhere():
58+
result = app.acquire_token_by_refresh_token(old_rt, scopes)
59+
if "error" in result:
60+
print("Discarding unsuccessful RT. Error: ", json.dumps(result, indent=2))
61+
62+
print("Migration completed")
63+
64+
# From now on, those successfully-migrated RTs are saved inside MSAL's cache,
65+
# and becomes available in normal MSAL coding pattern, which is NOT part of migration.
66+
# You can refer to:
67+
# https://github.com/AzureAD/microsoft-authentication-library-for-python/blob/1.2.0/sample/device_flow_sample.py#L42-L60

0 commit comments

Comments
 (0)