Skip to content

Commit dcfdf08

Browse files
committed
Add option to use simplified device flow for Outlook token generation.
1 parent 268acff commit dcfdf08

File tree

1 file changed

+33
-4
lines changed

1 file changed

+33
-4
lines changed

scripts/sasl-xoauth2-tool.in

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import http
77
import http.server
88
import json
99
import logging
10+
import msal
1011
import os
1112
import subprocess
1213
import sys
@@ -179,9 +180,31 @@ def outlook_get_initial_tokens(client_id:str, client_secret:str, tenant:str, cod
179180
raise Exception(f"Tokens not found in response: {content}")
180181

181182

182-
def get_token_outlook(client_id:str, client_secret:str, tenant:str, output_file:IO[str]) -> None:
183-
code = outlook_get_authorization_code(client_id, tenant)
184-
tokens = outlook_get_initial_tokens(client_id, client_secret, tenant, code)
183+
def outlook_get_initial_tokens_by_device_flow(client_id:str, tenant:str) -> Dict[str,Union[str,int]]:
184+
authority = f"https://login.microsoftonline.com/{tenant}"
185+
app = msal.PublicClientApplication(client_id, authority=authority)
186+
flow = app.initiate_device_flow([OUTLOOK_SCOPE])
187+
if "user_code" not in flow:
188+
raise Exception("Failed to create device flow. Ensure that public client flows are enabled for the application. Flow: %s" % json.dumps(flow, indent=4))
189+
print(flow["message"])
190+
sys.stdout.flush()
191+
result = app.acquire_token_by_device_flow(flow)
192+
if "access_token" not in result or "refresh_token" not in result:
193+
raise Exception("Failed to acquire token. Result: %s" % json.dumps(result, indent=4))
194+
print("Acquired token.")
195+
return {
196+
'access_token': result["access_token"],
197+
'refresh_token': result["refresh_token"],
198+
'expiry': 0,
199+
}
200+
201+
202+
def get_token_outlook(client_id:str, client_secret:str, tenant:str, use_device_flow:bool, output_file:IO[str]) -> None:
203+
if use_device_flow:
204+
tokens = outlook_get_initial_tokens_by_device_flow(client_id, tenant)
205+
else:
206+
code = outlook_get_authorization_code(client_id, tenant)
207+
tokens = outlook_get_initial_tokens(client_id, client_secret, tenant, code)
185208
json.dump(tokens, output_file, indent=4)
186209

187210
##########
@@ -199,12 +222,13 @@ def subcommand_get_token(args:argparse.Namespace) -> None:
199222
if args.service == 'outlook':
200223
if not args.tenant:
201224
parser.error("'outlook' service requires 'tenant' argument.")
202-
if not args.client_secret:
225+
if not args.client_secret and not args.use_device_flow:
203226
args.client_secret = input('Please enter OAuth2 client secret (not always required; Azure docs are unclear): ')
204227
get_token_outlook(
205228
args.client_id,
206229
args.client_secret,
207230
args.tenant,
231+
args.use_device_flow,
208232
args.output_file,
209233
)
210234
elif args.service == 'gmail':
@@ -244,6 +268,11 @@ sp_get_token.add_argument(
244268
'--scope',
245269
help="required for 'gmail'",
246270
)
271+
sp_get_token.add_argument(
272+
"--use-device-flow",
273+
action=argparse.BooleanOptionalAction,
274+
help="use simplified device flow for Outlook/Azure",
275+
)
247276
sp_get_token.add_argument(
248277
'output_file', nargs='?', type=argparse.FileType('w'), default='-',
249278
help="output file, '-' for stdout",

0 commit comments

Comments
 (0)