/* ----------------------------------------------------
Utility macros to manage and refresh the access token
for using Microsoft OneDrive APIs from SAS,
using PROC HTTP.
Authors: Joseph Henry, SAS
Chris Hemedinger, SAS
Copyright 2018, SAS Institute Inc.
-------------------------------------------------------*/
/*
Utility macro to process the JSON token
file that was created at authorization time.
This will fetch the access token, refresh token,
and expiration datetime for the token so we know
if we need to refresh it.
*/
%macro process_token_file(file);
libname oauth json fileref=&file.;
data _null_;
set oauth.root;
call symputx('access_token', access_token,'G');
call symputx('refresh_token', refresh_token,'G');
/* convert epoch value to SAS datetime */
call symputx('expires_on',(input(expires_on,best32.)+'01jan1970:00:00'dt),'G');
run;
%mend;
/*
Utility macro that retrieves the initial access token
by redeeming the authorization code that you're granted
during the interactive step using a web browser
while signed into your Microsoft OneDrive / Azure account.
This step also creates the initial token.json that will be
used on subsequent steps/sessions to redeem a refresh token.
*/
%macro get_token(client_id,
code,
resource,
outfile,
redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient,
tenant=common,
debug=0);
proc http url="https://login.microsoft.com/&tenant_id./oauth2/token"
method="POST"
in="%nrstr(&client_id)=&client_id.%nrstr(&code)=&code.%nrstr(&redirect_uri)=&redirect_uri%nrstr(&grant_type)=authorization_code%nrstr(&resource)=&resource."
out=&outfile.;
%if &debug>=0 %then
%do;
debug level=&debug.;
%end;
%else %if &_DEBUG_. ge 1 %then
%do;
debug level=&_DEBUG_.;
%end;
run;
%process_token_file(&outfile);
%mend;
/*
Utility macro to redeem the refresh token
and get a new access token for use in subsequent
calls to the OneDrive service.
*/
%macro refresh(client_id,
refresh_token,
resource,
outfile,
redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient,
tenant=common,
debug=0);
proc http url="https://login.microsoft.com/&tenant_id./oauth2/token"
method="POST"
in="%nrstr(&client_id)=&client_id.%nrstr(&refresh_token=)&refresh_token%nrstr(&redirect_uri)=&redirect_uri.%nrstr(&grant_type)=refresh_token%nrstr(&resource)=&resource."
out=&outfile.;
%if &debug. ge 0 %then
%do;
debug level=&debug.;
%end;
%else %if %symexist(_DEBUG_) AND &_DEBUG_. ge 1 %then
%do;
debug level=&_DEBUG_.;
%end;
run;
%process_token_file(&outfile);
%mend refresh;
So what part are you having problems with?
Since your code includes macros you should set:
options mprint;
before running the macros to capture the code generated by them.
Are there errors in the log?: Post the code and log in a code box opened with the "</>" to maintain formatting of error messages.
No output? Post any log in a code box.
Unexpected output? Provide input data in the form of data step code pasted into a code box, the actual results and the expected results. Instructions here: https://communities.sas.com/t5/SAS-Communities-Library/How-to-create-a-data-step-version-of-your-dat... will show how to turn an existing SAS data set into data step code that can be pasted into a forum code box using the "</>" icon or attached as text to show exactly what you have and that we can test code against.
ERROR
{"error":{"code":"OrganizationFromTenantGuidNotFound","message":"The tenant for tenant guid '0e888a36-e21e-42e0-9cd2-bed9a933d9ee' d
oes not exist.","innerError":{"oAuthEventOperationId":"173eae30-7d2d-47fe-a7c2-89eaea6d822c","oAuthEventcV":"4BaJutuPZOOnZzJRr+gHNg.
1.1.1","errorUrl":"https://aka.ms/autherrors#error-InvalidTenant","requestId":"434b4ffd-4897-4dd0-84db-9b6cfd211a69","d...
-13T05:16:41"}}}
After a Modified Code
/*
Utility macro to process the JSON token
file that was created at authorization time.
This will fetch the access token, refresh token,
and expiration datetime for the token so we know
if we need to refresh it.
*/
%macro process_token_file(file);
libname oauth json fileref=&file.;
data _null_;
set oauth.root;
call symputx('access_token', access_token,'G');
call symputx('refresh_token', refresh_token,'G');
/* convert epoch value to SAS datetime */
call symputx('expires_on',(input(expires_on,best32.)+'01jan1970:00:00'dt),'G');
run;
%mend;
/*
Utility macro that retrieves the initial access token
by redeeming the authorization code that you're granted
during the interactive step using a web browser
while signed into your Microsoft OneDrive / Azure account.
This step also creates the initial token.json that will be
used on subsequent steps/sessions to redeem a refresh token.
*/
%macro get_token(client_id,
client_secret,
code,
scope,
outfile,
redirect_uri=https://deployopt.demo.com/SASLogon/login/callback/azure,
tenant=0e888a36-e21e-42e0-9cd2-bed9a933d9ee,
debug=0);
proc http url="https://login.microsoftonline.com/0e888a36-e21e-42e0-9cd2-bed9a933d9ee/oauth2/v2.0/token"
method="POST"
in="%nrstr(&client_id)=&client_id.%nrstr(&client_secret)=&client_secret.%nrstr(&code)=&code.%nrstr(&redirect_uri)=&redirect_uri%nrstr(&grant_type)=authorization_code%nrstr(&scope)=&scope."
out=&outfile.;
%if &debug>=0 %then
%do;
debug level=&debug.;
%end;
%else %if &_DEBUG_. ge 1 %then
%do;
debug level=&_DEBUG_.;
%end;
run;
%process_token_file(&outfile);
%mend;
/*
Utility macro to redeem the refresh token
and get a new access token for use in subsequent
calls to the OneDrive service.
*/
%macro refresh(client_id,
client_secret,
refresh_token,
scope,
outfile,
redirect_uri=https://deployopt.demo.com/SASLogon/login/callback/azure,
tenant=common,
debug=0);
proc http url="https://login.microsoftonline.com/0e888a36-e21e-42e0-9cd2-bed9a933d9ee/oauth2/v2.0/token"
method="POST"
in="%nrstr(&client_id)=&client_id.%nrstr(&client_secret)=&client_secret.%nrstr(&refresh_token=)&refresh_token%nrstr(&redirect_uri)=&redirect_uri.%nrstr(&grant_type)=refresh_token%nrstr(&scope)=&scope."
out=&outfile.;
%if &debug. ge 0 %then
%do;
debug level=&debug.;
%end;
%else %if %symexist(_DEBUG_) AND &_DEBUG_. ge 1 %then
%do;
debug level=&_DEBUG_.;
%end;
run;
%process_token_file(&outfile);
%mend refresh;
/* generate Token */
%let EMAIL_ADDRESS = shivraj.pawar@woodpeckerind.com;
filename send temp;
data null;
file send;
put '{"message": {'
'"subject": "Test Email",'
'"body": {'
'"contentType": "Text",'
'"content": "This is a test email."'
'},'
'"toRecipients": ['
'{'
'"emailAddress": {'
'"address": "' "&EMAIL_ADDRESS." '"'
'}'
'}'
']'
'}}';
run;
filename resp temp;
proc http
method="POST"
url="https://graph.microsoft.com/v1.0/me/sendMail"
in=send
out=resp; /* Store the response in a file */
headers "Content-Type"="application/json"
"Authorization"="Bearer &access_token.";
run;
/* Check the response for success or error */
data _null_;
infile resp;
input;
put _infile_;
run;
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!