%3CLINGO-SUB%20id%3D%22lingo-sub-510053%22%20slang%3D%22en-US%22%3ESharePoint%20Online%20AAD%20App%20OAuth%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-510053%22%20slang%3D%22en-US%22%3E%0A%20%26lt%3Bmeta%20http-equiv%3D%22Content-Type%22%20content%3D%22text%2Fhtml%3B%20charset%3DUTF-8%22%20%2F%26gt%3B%3CSTRONG%3EFirst%20published%20on%20TECHNET%20on%20Feb%2007%2C%202018%20%3C%2FSTRONG%3E%20%3CBR%20%2F%3E%20This%20post%20is%20a%20contribution%20from%20Vitaly%20Lyamin%2C%20an%20engineer%20with%20the%20SharePoint%20Developer%20Support%20team%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20Accessing%20SharePoint%20API%E2%80%99s%20has%20never%20been%20easier%20(SPOIDCRL%20cookie%2C%20ACS%20OAuth%2C%20AAD%20OAuth).%20Azure%20AD%20apps%20are%20quickly%20becoming%20the%20standard%20way%20of%20accessing%20O365%20API%E2%80%99s%20in%20addition%20to%20other%20API%E2%80%99s.%20Below%20are%20some%20resources%20on%20registering%20apps%20and%20using%20libraries.%20Also%2C%20there%E2%80%99s%20a%20test%20script%20that%20walks%20through%20the%20entire%20authorization%20grant%20flow.%20The%20end%20goal%20with%20all%20OAuth-based%20authorization%20is%20to%20retrieve%20the%20access%20token%20to%20be%20used%20in%20the%20HTTP%20request%20Authorization%20header%20(Authorization%3A%20Bearer%20%3CACCESS%20token%3D%22%22%3E).%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CSTRONG%3ENative%20Client%20App%20%3C%2FSTRONG%3E%20%3CBR%20%2F%3E%20Native%20app%20registrations%20are%20primarily%20for%20devices%20and%20services%20where%20browser%20interaction%20is%20not%20needed.%20One%20of%20the%20biggest%20benefits%20is%20the%20non-interactive%20(active)%20authorization%20using%20credentials%2C%20Federated%20IDP%20assertion%20or%20similar.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CEM%3E%3CSTRONG%3ELinks%20%3C%2FSTRONG%3E%20%3C%2FEM%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-authentication-scenarios%23native-application-to-web-api%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3E%20https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-authentication-scenarios%23native-application-to-web-api%20%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Fazure.microsoft.com%2Fen-us%2Fresources%2Fsamples%2Factive-directory-dotnet-native-headless%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3E%20https%3A%2F%2Fazure.microsoft.com%2Fen-us%2Fresources%2Fsamples%2Factive-directory-dotnet-native-headless%20%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CSTRONG%3E%20Web%20App%20%2F%20API%20%3C%2FSTRONG%3E%20%3CBR%20%2F%3E%20Web%20app%20registrations%20are%20just%20as%20they%20sound%20%E2%80%93%20apps%20on%20the%20web.%20These%20apps%20typically%20use%20the%20authorization%20grant%20and%20refresh%20grant%20flows%20and%20are%20not%20intended%20for%20devices%2Fservices.%20Once%20authorized%20(some%20permissions%20scopes%20require%20admin%20consent)%2C%20the%20access%20token%20is%20retrieved%20from%20the%20OAuth%20token%20endpoint%20using%20the%20authorization%20code.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CSTRONG%3EAuthorization%20URL%20%3C%2FSTRONG%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fauthorize%3Fresource%3D%26lt%3BRESOURCE%26gt%3B%26amp%3Bclient_id%3D%26gt%3BCLIENTID%26gt%3B%26amp%3Bscope%3D%26lt%3BSCOPE%26gt%3B%26amp%3Bredirect_uri%3D%26lt%3BREDIRECTURI%26gt%3B%26amp%3Bresponse_type%3Dcode%26amp%3Bprompt%3Dadmin_consent%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fauthorize%3Fresource%3D%3CRESOURCE%3E%26amp%3Bclient_id%3D%26gt%3BCLIENTID%26gt%3B%26amp%3Bscope%3D%3CSCOPE%3E%26amp%3Bredirect_uri%3D%3CREDIRECTURI%3E%26amp%3Bresponse_type%3Dcode%26amp%3Bprompt%3Dadmin_consent%3C%2FREDIRECTURI%3E%3C%2FSCOPE%3E%3C%2FRESOURCE%3E%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CSTRONG%3E%20Access%20Token%20URL%20%3C%2FSTRONG%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Ftoken%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Ftoken%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CEM%3E%20%3CSTRONG%3E%20Link%20%3C%2FSTRONG%3E%20%3C%2FEM%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-authentication-scenarios%23web-browser-to-web-application%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3E%20https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-authentication-scenarios%23web-browser-to-web-application%20%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CSTRONG%3E%20Libraries%20%3C%2FSTRONG%3E%20%3CBR%20%2F%3E%20ADAL%20libraries%20are%20available%20in%20many%20different%20flavors%20and%20are%20quick%20and%20easy%20to%20implement.%20There%20primary%20purpose%20is%20to%20authorize%20the%20user%2Fservice%20to%20a%20resource%20(e.g.%20SharePoint%20REST%20API%E2%80%99s%2C%20Graph).%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CEM%3E%3CSTRONG%3ELink%20%3C%2FSTRONG%3E%20%3C%2FEM%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-authentication-libraries%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3E%20https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-authentication-libraries%20%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CSTRONG%3E%20Other%20Resources%20%3C%2FSTRONG%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-integrating-applications%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3E%20https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Factive-directory%2Fdevelop%2Factive-directory-integrating-applications%20%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CA%20href%3D%22https%3A%2F%2Fmsdn.microsoft.com%2Fen-us%2Foffice%2Foffice365%2Fhowto%2Fgetting-started-Office-365-APIs%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3E%20https%3A%2F%2Fmsdn.microsoft.com%2Fen-us%2Foffice%2Foffice365%2Fhowto%2Fgetting-started-Office-365-APIs%20%3C%2FA%3E%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20Test%20Script%20(Web%20App)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%3CDIV%3E%3CBR%20%2F%3E%20%26lt%3B%23%20%3CBR%20%2F%3E%20.Synopsis%20%3CBR%20%2F%3E%20Get%20access%20token%20for%20AAD%20web%20app.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20.Description%20%3CBR%20%2F%3E%20Authorizes%20AAD%20app%20and%20retrieves%20access%20token%20using%20OAuth%202.0%20and%20endpoints.%20%3CBR%20%2F%3E%20Refreshes%20the%20token%20if%20within%205%20minutes%20of%20expiration%20or%2C%20optionally%20forces%20refresh.%20%3CBR%20%2F%3E%20Sets%20global%20variable%20(%24Global%3AaccessTokenResult)%20that%20can%20be%20used%20after%20the%20script%20runs.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20.Todo%20%3CBR%20%2F%3E%20Add%20ability%20to%20handle%20refresh%20token%20input%20and%20access%20token%20retrieval%20without%20re-authorization.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20.Example%20%3CBR%20%2F%3E%20The%20following%20returns%20the%20access%20token%20result%20from%20AAD%20with%20admin%20consent%20authorization%20and%20caches%20the%20result.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20PS%26gt%3B%20.%5Caad_web.ps1%20-Clientid%20%22%22%20-Clientsecret%20%22%22%20-Resource%20%22%3CA%20href%3D%22https%3A%2F%2FTENANT.sharepoint.com%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2FTENANT.sharepoint.com%3C%2FA%3E%22%20-Redirecturi%20%22https%3A%2F%2Flocalhost%3A44385%22%20-Scope%20%22%22%20-AdminConsent%20-Cache%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20.Example%20%3CBR%20%2F%3E%20The%20following%20returns%20the%20access%20token%20result%20from%20AAD%20with%20admin%20consent%20authorization%20or%20refreshes%20the%20token.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20PS%26gt%3B%20.%5Caad_web.ps1%20-Clientid%20%22%22%20-Clientsecret%20%22%22%20-Resource%20%22%3CA%20href%3D%22https%3A%2F%2FTENANT.sharepoint.com%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2FTENANT.sharepoint.com%3C%2FA%3E%22%20-Redirecturi%20%22https%3A%2F%2Flocalhost%3A44385%22%20-Scope%20%22%22%20-AdminConsent%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20.Example%20%3CBR%20%2F%3E%20The%20following%20returns%20the%20access%20token%20result%20from%20AAD%20or%20from%20cache%2C%20forces%20refresh%20so%20the%20token%20is%20good%20for%20an%20hour%20and%20outputs%20to%20a%20file%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20PS%26gt%3B%20.%5Caad_web.ps1%20-Clientid%20%22%22%20-Clientsecret%20%22%22%20-Resource%20%22%3CA%20href%3D%22https%3A%2F%2FTENANT.sharepoint.com%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2FTENANT.sharepoint.com%3C%2FA%3E%22%20-Redirecturi%20%22https%3A%2F%2Flocalhost%3A44385%22%20-Scope%20%22%22%20-Refresh%20Force%20%7C%20Out-File%20c%3A%5Ctemp%5Ctoken.txt%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20.PARAMETER%20ClientId%20%3CBR%20%2F%3E%20The%20AAD%20App%20client%20id.%20%3CBR%20%2F%3E%20.PARAMETER%20ClientSecret%20%3CBR%20%2F%3E%20The%20AAD%20App%20client%20secret.%20%3CBR%20%2F%3E%20.PARAMETER%20RedirectUri%20%3CBR%20%2F%3E%20The%20redirect%20uri%20configured%20for%20that%20app.%20%3CBR%20%2F%3E%20.PARAMETER%20Resource%20%3CBR%20%2F%3E%20The%20resource%20the%20app%20is%20attempting%20to%20access%20(i.e.%20%3CA%20href%3D%22https%3A%2F%2FTENANT.sharepoint.com%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2FTENANT.sharepoint.com%3C%2FA%3E)%20%3CBR%20%2F%3E%20.PARAMETER%20Scope%20%3CBR%20%2F%3E%20Permission%20scopes%20for%20the%20app%20(optional).%20%3CBR%20%2F%3E%20.PARAMETER%20AdminConsent%20%3CBR%20%2F%3E%20Will%20perform%20admin%20consent%20(optional).%20%3CBR%20%2F%3E%20.PARAMETER%20Cache%20%3CBR%20%2F%3E%20Cache%20the%20access%20token%20in%20the%20temp%20directory%20for%20subsequent%20retrieval%20(optional).%20%3CBR%20%2F%3E%20.PARAMETER%20Refresh%20%3CBR%20%2F%3E%20Options%20(Yes%2C%20No%2C%20Force).%20Will%20automatically%20enabling%20caching%20if%20%22Yes%22%20or%20%22Force%22%20are%20used.%20%3CBR%20%2F%3E%20Yes%3A%20Refresh%20token%20if%20within%205%20minutes%20of%20expiration%20if%20cached%20token%20found.%20%3CBR%20%2F%3E%20No%3A%20Do%20not%20refresh%20and%20re-authorize.%20%3CBR%20%2F%3E%20Force%3A%20Forfce%20a%20refresh%20if%20cached%20token%20found.%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%23%26gt%3B%20%3CBR%20%2F%3E%20%5BCmdletBinding()%5D%20%3CBR%20%2F%3E%20Param(%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24true)%5D%20%3CBR%20%2F%3E%20%5Bstring%5D%24ClientId%2C%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24true)%5D%20%3CBR%20%2F%3E%20%5Bstring%5D%24ClientSecret%2C%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24true)%5D%20%3CBR%20%2F%3E%20%5Bstring%5D%24RedirectUri%2C%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24true)%5D%20%3CBR%20%2F%3E%20%5Bstring%5D%24Resource%2C%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24false)%5D%20%3CBR%20%2F%3E%20%5Bstring%5D%24Scope%2C%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24false)%5D%20%3CBR%20%2F%3E%20%5Bswitch%5D%24AdminConsent%2C%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24false)%5D%20%3CBR%20%2F%3E%20%5Bswitch%5D%24Cache%2C%20%3CBR%20%2F%3E%20%5BParameter(Mandatory%3D%24false)%5D%20%3CBR%20%2F%3E%20%5BValidateSet(%22Yes%22%2C%22No%22%2C%22Force%22)%5D%20%3CBR%20%2F%3E%20%5BValidateNotNullOrEmpty()%5D%20%3CBR%20%2F%3E%20%5Bstring%5D%24Refresh%20%3D%20%22Yes%22%20%3CBR%20%2F%3E%20)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20Add-Type%20-AssemblyName%20System.Windows.Forms%20%3CBR%20%2F%3E%20Add-Type%20-AssemblyName%20System.Web%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24isCache%20%3D%20%24Cache.IsPresent%20%3CBR%20%2F%3E%20%24isRefresh%20%3D%20((%24Refresh%20-eq%20%22Yes%22)%20-or%20(%24Refresh%20-eq%20%22Force%22))%20%3CBR%20%2F%3E%20%24refreshForce%20%3D%20%24Refresh%20-eq%20%22Force%22%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20if%20(%24isRefresh)%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24isCache%20%3D%20%24true%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%23%20Don't%20edit%20variables%20below%20(unless%20there's%20a%20bug)%20%3CBR%20%2F%3E%20%24clientSecretEncoded%20%3D%20%5Buri%5D%3A%3AEscapeDataString(%24clientSecret)%20%3CBR%20%2F%3E%20%24redirectUriEncoded%20%3D%20%5Buri%5D%3A%3AEscapeDataString(%24redirectUri)%20%3CBR%20%2F%3E%20%24resourceEncoded%20%3D%20%5Buri%5D%3A%3AEscapeDataString(%24resource)%20%3CBR%20%2F%3E%20%24accessTokenUrl%20%3D%20%22%3CA%20href%3D%22https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Ftoken%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Ftoken%3C%2FA%3E%22%20%3CBR%20%2F%3E%20%24cacheFilePath%20%3D%20%5BSystem.IO.Path%5D%3A%3ACombine(%24env%3ATEMP%2C%20%22aad_web_cache_%24clientId.json%22)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24accessTokenResult%20%3D%20%24null%20%3CBR%20%2F%3E%20%24adminConsentText%20%3D%22%22%20%3CBR%20%2F%3E%20if%20(%24adminConsent)%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24adminConsentText%20%3D%20%22%26amp%3Bprompt%3Dadmin_consent%22%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24authorizationUrl%20%3D%20%22%3CA%20href%3D%22https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fauthorize%3Fresource%3D%24resourceEncoded%26amp%3Bclient_id%3D%24clientId%26amp%3Bscope%3D%24scope%26amp%3Bredirect_uri%3D%24redirectUriEncoded%26amp%3Bresponse_type%3Dcode%24adminConsentText%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fauthorize%3Fresource%3D%24resourceEncoded%26amp%3Bclient_id%3D%24clientId%26amp%3Bscope%3D%24scope%26amp%3Bredirect_uri%3D%24redirectUriEncoded%26amp%3Bresponse_type%3Dcode%24adminConsentText%3C%2FA%3E%22%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20function%20Invoke-OAuth()%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24Global%3AauthorizationCode%20%3D%20%24null%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24form%20%3D%20New-Object%20Windows.Forms.Form%20%3CBR%20%2F%3E%20%24form.FormBorderStyle%20%3D%20%5BWindows.Forms.FormBorderStyle%5D%3A%3AFixedSingle%20%3CBR%20%2F%3E%20%24form.Width%20%3D%20640%20%3CBR%20%2F%3E%20%24form.Height%20%3D%20480%20%3CBR%20%2F%3E%20%24form.MaximizeBox%20%3D%20%24false%20%3CBR%20%2F%3E%20%24form.MinimizeBox%20%3D%20%24false%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24web%20%3D%20New-Object%20Windows.Forms.WebBrowser%20%3CBR%20%2F%3E%20%24form.Controls.Add(%24web)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24web.Size%20%3D%20%24form.ClientSize%20%3CBR%20%2F%3E%20%24web.DocumentText%20%3D%20%22%3CA%20href%3D%22https%3A%2F%2Fsecure.aadcdn.microsoftonline-p.com%2Fests%2F2.1.6856.20%2Fcontent%2Fimages%2Fbackgrounds%2F0.jpg%3Fx%3Df5a9a9531b8f4bcc86eabb19472d15d5%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fsecure.aadcdn.microsoftonline-p.com%2Fests%2F2.1.6856.20%2Fcontent%2Fimages%2Fbackgrounds%2F0.jpg%3Fx%3Df5a9a9531b8f4bcc86eabb19472d15d5%3C%2FA%3E)'%26gt%3B%3CH3%20id%3D%22toc-hId-1535063287%22%3E%20id%3D'title'%26gt%3BContinue%20with%20current%20user%20or%20logout%3F%3C%2FH3%3E%3CDIV%3E%3CINPUT%20id%3D%22'cancel'%22%20type%3D%22'button'%22%20value%3D%22'Continue'%22%20%2F%3E%3C%2FDIV%3E%3CBR%20%2F%3E%3CDIV%3E%3CINPUT%20id%3D%22'logout'%22%20type%3D%22'button'%22%20value%3D%22'Logout'%22%20%2F%3E%3C%2FDIV%3E%3CH5%20id%3D%22'loading'%22%20style%3D%22'display%3Anone'%22%20id%3D%22toc-hId--1410120684%22%3EWorking%20on%20it...%3C%2FH5%3E%3CSCRIPT%20type%3D%22'text%2Fjavascript'%22%3Evar%20logout%20%3D%20document.getElementById(%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Blogout%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B)%3Bvar%20cancel%20%3D%20document.getElementById(%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Bcancel%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B)%3Bfunction%20click(element)%7Bdocument.getElementById(%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Btitle%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B).style.display%3D%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Bnone%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%3Bdocument.getElementById(%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Bloading%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B).style.display%3D%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Bblock%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%3Blogout.style.display%3D%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Bnone%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%3Bcancel.style.display%3D%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Bnone%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%3Bif%20(this.id%20%3D%3D%3D%20%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3Blogout%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B)%7Bwindow.location%20%3D%20%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%26amp%3Bamp%3Bamp%3Blt%3BA%20href%3D%22https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Flogout%3Fpost_logout_redirect_uri%3D%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%22%26amp%3Bamp%3Bamp%3Bgt%3Bhttps%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Flogout%3Fpost_logout_redirect_uri%3D%26amp%3Bamp%3Bamp%3Blt%3B%2FA%26amp%3Bamp%3Bamp%3Bgt%3B%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%20%2B%20encodeURIComponent(%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%24authorizationUrl%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B)%3B%7Delse%7Bwindow.location%20%3D%20%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%24authorizationUrl%26amp%3Bamp%3Bamp%3Bamp%3B%2339%3B%3B%7D%7Dlogout.onclick%20%3D%20click%3Bcancel.onclick%20%3D%20click%3B%3C%2FSCRIPT%3E%22%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24web.add_DocumentCompleted(%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24uri%20%3D%20%5Buri%5D%24redirectUri%20%3CBR%20%2F%3E%20%24queryString%20%3D%20%5BSystem.Web.HttpUtility%5D%3A%3AParseQueryString(%24_.url.Query)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20if(%24_.url.authority%20-eq%20%24uri.authority)%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24authorizationCode%20%3D%20%24queryString%5B%22code%22%5D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20if%20(!%5Bstring%5D%3A%3AIsNullOrEmpty(%24authorizationCode))%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24form.DialogResult%20%3D%20%22OK%22%20%3CBR%20%2F%3E%20%24Global%3AauthorizationCode%20%3D%20%24authorizationCode%20%3CBR%20%2F%3E%20%24Global%3AauthorizationCodeTime%20%3D%20%5Bdatetime%5D%3A%3ANow%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24form.close()%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%7D)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24dialogResult%20%3D%20%24form.ShowDialog()%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20if(%24dialogResult%20-eq%20%22OK%22)%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24authorizationCode%20%3D%20%24Global%3AauthorizationCode%20%3CBR%20%2F%3E%20%24headers%20%3D%20%40%7B%22Accept%22%20%3D%20%22application%2Fjson%3Bodata%3Dverbose%22%7D%20%3CBR%20%2F%3E%20%24body%20%3D%20%22client_id%3D%24clientId%26amp%3Bclient_secret%3D%24clientSecretEncoded%26amp%3Bredirect_uri%3D%24redirectUriEncoded%26amp%3Bgrant_type%3Dauthorization_code%26amp%3Bcode%3D%24authorizationCode%22%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24accessTokenResult%20%3D%20Invoke-RestMethod%20-Uri%20%24accessTokenUrl%20-Method%20POST%20-Body%20%24body%20-Headers%20%24headers%20%3CBR%20%2F%3E%20%24Global%3AaccessTokenResult%20%3D%20%24accessTokenResult%20%3CBR%20%2F%3E%20%24Global%3AaccessTokenResultTime%20%3D%20%5Bdatetime%5D%3A%3ANow%20%3CBR%20%2F%3E%20%24accessTokenResultText%20%3D%20(ConvertTo-Json%20%24accessTokenResult)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20if%20(%24isCache%20-and%20!%5Bstring%5D%3A%3AIsNullOrEmpty(%24accessTokenResultText))%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%5Bvoid%5D(Set-Content%20-Path%20%24cacheFilePath%20-Value%20%24accessTokenResultText)%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20Write-Output%20(ConvertTo-Json%20%24accessTokenResultText)%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24web.Dispose()%20%3CBR%20%2F%3E%20%24form.Dispose()%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20function%20Get-CachedAccessTokenResult()%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20if%20(%24isCache%20-and%20%5BSystem.IO.File%5D%3A%3AExists(%24cacheFilePath))%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24accessTokenResultText%20%3D%20Get-Content%20-Raw%20%24cacheFilePath%20%3CBR%20%2F%3E%20if%20(!%5Bstring%5D%3A%3AIsNullOrEmpty(%24accessTokenResultText))%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24accessTokenResult%20%3D%20(ConvertFrom-Json%20%24accessTokenResultText)%20%3CBR%20%2F%3E%20if%20(!%5Bstring%5D%3A%3AIsNullOrEmpty(%24accessTokenResult.access_token))%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24Global%3AaccessTokenResult%20%3D%20%24accessTokenResult%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20return%20%24accessTokenResult%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20return%20%24null%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20function%20Invoke-Refresh()%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%24refreshToken%20%3D%20%24accessTokenResult.refresh_token%20%3CBR%20%2F%3E%20%24headers%20%3D%20%40%7B%22Accept%22%20%3D%20%22application%2Fjson%3Bodata%3Dverbose%22%7D%20%3CBR%20%2F%3E%20%24body%20%3D%20%22client_id%3D%24clientId%26amp%3Bclient_secret%3D%24clientSecretEncoded%26amp%3Bresource%3D%24resourceEncoded%26amp%3Bgrant_type%3Drefresh_token%26amp%3Brefresh_token%3D%24refreshToken%22%20%3CBR%20%2F%3E%20%24accessTokenResult2%20%3D%20Invoke-RestMethod%20-Uri%20%24accessTokenUrl%20-Method%20POST%20-Body%20%24body%20-Headers%20%24headers%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24accessTokenResult.scope%20%3D%20%24accessTokenResult2.scope%20%3CBR%20%2F%3E%20%24accessTokenResult.expires_in%20%3D%20%24accessTokenResult2.expires_in%20%3CBR%20%2F%3E%20%24accessTokenResult.ext_expires_in%20%3D%20%24accessTokenResult2.ext_expires_in%20%3CBR%20%2F%3E%20%24accessTokenResult.expires_on%20%3D%20%24accessTokenResult2.expires_on%20%3CBR%20%2F%3E%20%24accessTokenResult.not_before%20%3D%20%24accessTokenResult2.not_before%20%3CBR%20%2F%3E%20%24accessTokenResult.resource%20%3D%20%24accessTokenResult2.resource%20%3CBR%20%2F%3E%20%24accessTokenResult.access_token%20%3D%20%24accessTokenResult2.access_token%20%3CBR%20%2F%3E%20%24accessTokenResult.refresh_token%20%3D%20%24accessTokenResult2.refresh_token%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24Global%3AaccessTokenResult%20%3D%20%24accessTokenResult%20%3CBR%20%2F%3E%20%24Global%3AaccessTokenResultTime%20%3D%20%5Bdatetime%5D%3A%3ANow%20%3CBR%20%2F%3E%20%24accessTokenResultText%20%3D%20(ConvertTo-Json%20%24accessTokenResult)%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20if%20(!%5Bstring%5D%3A%3AIsNullOrEmpty(%24accessTokenResultText))%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20%5Bvoid%5D(Set-Content%20-Path%20%24cacheFilePath%20-Value%20%24accessTokenResultText)%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20Write-Output%20(ConvertTo-Json%20%24accessTokenResultText)%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%20%24accessTokenResult%20%3D%20Get-CachedAccessTokenResult%20%3CBR%20%2F%3E%20if%20(%24accessTokenResult%20-eq%20%24null)%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20Invoke-OAuth%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20elseif%20(%24refreshForce%20-or%20((%5Bdatetime%5D%3A%3AParse(%221%2F1%2F1970%22)).AddSeconds(%5Bint%5D%24accessTokenResult.expires_on).ToLocalTime()%20-lt%20(%5Bdatetime%5D%3A%3ANow).AddMinutes(5)))%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20if%20(%24isRefresh)%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20Invoke-Refresh%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20else%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20Invoke-OAuth%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20else%20%3CBR%20%2F%3E%20%7B%20%3CBR%20%2F%3E%20Write-Output%20(ConvertTo-Json%20%24Global%3AaccessTokenResult)%20%3CBR%20%2F%3E%20%7D%20%3CBR%20%2F%3E%20%3CBR%20%2F%3E%3C%2FDIV%3E%0A%20%0A%3C%2FACCESS%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-510053%22%20slang%3D%22en-US%22%3EFirst%20published%20on%20TECHNET%20on%20Feb%2007%2C%202018%20This%20post%20is%20a%20contribution%20from%20Vitaly%20Lyamin%2C%20an%20engineer%20with%20the%20SharePoint%20Developer%20Support%20teamAccessing%20SharePoint%20API%E2%80%99s%20has%20never%20been%20easier%20(SPOIDCRL%20cookie%2C%20ACS%20OAuth%2C%20AAD%20OAuth).%3C%2FLINGO-TEASER%3E%3C%2FLINGO-BODY%3E
Microsoft
First published on TECHNET on Feb 07, 2018
This post is a contribution from Vitaly Lyamin, an engineer with the SharePoint Developer Support team

Accessing SharePoint API’s has never been easier (SPOIDCRL cookie, ACS OAuth, AAD OAuth). Azure AD apps are quickly becoming the standard way of accessing O365 API’s in addition to other API’s. Below are some resources on registering apps and using libraries. Also, there’s a test script that walks through the entire authorization grant flow. The end goal with all OAuth-based authorization is to retrieve the access token to be used in the HTTP request Authorization header (Authorization: Bearer <access token>).

Native Client App
Native app registrations are primarily for devices and services where browser interaction is not needed. One of the biggest benefits is the non-interactive (active) authorization using credentials, Federated IDP assertion or similar.

Links
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-...
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-native-headless

Web App / API
Web app registrations are just as they sound – apps on the web. These apps typically use the authorization grant and refresh grant flows and are not intended for devices/services. Once authorized (some permissions scopes require admin consent), the access token is retrieved from the OAuth token endpoint using the authorization code.

Authorization URL
https://login.microsoftonline.com/common/oauth2/authorize?resource=<RESOURCE>&client_id=>CLIENTID>&s...

Access Token URL
https://login.microsoftonline.com/common/oauth2/token

Link
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-...

Libraries
ADAL libraries are available in many different flavors and are quick and easy to implement. There primary purpose is to authorize the user/service to a resource (e.g. SharePoint REST API’s, Graph).

Link
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-...

Other Resources
https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-app...
https://msdn.microsoft.com/en-us/office/office365/howto/getting-started-Office-365-APIs

Test Script (Web App)



<#
.Synopsis
Get access token for AAD web app.

.Description
Authorizes AAD app and retrieves access token using OAuth 2.0 and endpoints.
Refreshes the token if within 5 minutes of expiration or, optionally forces refresh.
Sets global variable ($Global:accessTokenResult) that can be used after the script runs.

.Todo
Add ability to handle refresh token input and access token retrieval without re-authorization.

.Example
The following returns the access token result from AAD with admin consent authorization and caches the result.

PS> .\aad_web.ps1 -Clientid "" -Clientsecret "" -Resource "https://TENANT.sharepoint.com" -Redirecturi "https://localhost:44385" -Scope "" -AdminConsent -Cache

.Example
The following returns the access token result from AAD with admin consent authorization or refreshes the token.

PS> .\aad_web.ps1 -Clientid "" -Clientsecret "" -Resource "https://TENANT.sharepoint.com" -Redirecturi "https://localhost:44385" -Scope "" -AdminConsent

.Example
The following returns the access token result from AAD or from cache, forces refresh so the token is good for an hour and outputs to a file

PS> .\aad_web.ps1 -Clientid "" -Clientsecret "" -Resource "https://TENANT.sharepoint.com" -Redirecturi "https://localhost:44385" -Scope "" -Refresh Force | Out-File c:\temp\token.txt

.PARAMETER ClientId
The AAD App client id.
.PARAMETER ClientSecret
The AAD App client secret.
.PARAMETER RedirectUri
The redirect uri configured for that app.
.PARAMETER Resource
The resource the app is attempting to access (i.e. https://TENANT.sharepoint.com)
.PARAMETER Scope
Permission scopes for the app (optional).
.PARAMETER AdminConsent
Will perform admin consent (optional).
.PARAMETER Cache
Cache the access token in the temp directory for subsequent retrieval (optional).
.PARAMETER Refresh
Options (Yes, No, Force). Will automatically enabling caching if "Yes" or "Force" are used.
Yes: Refresh token if within 5 minutes of expiration if cached token found.
No: Do not refresh and re-authorize.
Force: Forfce a refresh if cached token found.

#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$ClientId,
[Parameter(Mandatory=$true)]
[string]$ClientSecret,
[Parameter(Mandatory=$true)]
[string]$RedirectUri,
[Parameter(Mandatory=$true)]
[string]$Resource,
[Parameter(Mandatory=$false)]
[string]$Scope,
[Parameter(Mandatory=$false)]
[switch]$AdminConsent,
[Parameter(Mandatory=$false)]
[switch]$Cache,
[Parameter(Mandatory=$false)]
[ValidateSet("Yes","No","Force")]
[ValidateNotNullOrEmpty()]
[string]$Refresh = "Yes"
)

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Web

$isCache = $Cache.IsPresent
$isRefresh = (($Refresh -eq "Yes") -or ($Refresh -eq "Force"))
$refreshForce = $Refresh -eq "Force"

if ($isRefresh)
{
$isCache = $true
}

# Don't edit variables below (unless there's a bug)
$clientSecretEncoded = [uri]::EscapeDataString($clientSecret)
$redirectUriEncoded = [uri]::EscapeDataString($redirectUri)
$resourceEncoded = [uri]::EscapeDataString($resource)
$accessTokenUrl = "https://login.microsoftonline.com/common/oauth2/token"
$cacheFilePath = [System.IO.Path]::Combine($env:TEMP, "aad_web_cache_$clientId.json")

$accessTokenResult = $null
$adminConsentText =""
if ($adminConsent)
{
$adminConsentText = "&prompt=admin_consent"
}

$authorizationUrl = "https://login.microsoftonline.com/common/oauth2/authorize?resource=$resourceEncoded&client_id=$clien..."

function Invoke-OAuth()
{
$Global:authorizationCode = $null

$form = New-Object Windows.Forms.Form
$form.FormBorderStyle = [Windows.Forms.FormBorderStyle]::FixedSingle
$form.Width = 640
$form.Height = 480
$form.MaximizeBox = $false
$form.MinimizeBox = $false

$web = New-Object Windows.Forms.WebBrowser
$form.Controls.Add($web)

$web.Size = $form.ClientSize
$web.DocumentText = "<html><body style='text-align:center;overflow:hidden;background-image:url(https://secure.aadcdn.microsoftonline-p.com/ests/2.1.6856.20/content/images/backgrounds/0.jpg?x=f5a9... id='title'>Continue with current user or logout?</h3><div><input id='cancel' type='button' value='Continue' /></div><br /><div><input id='logout' type='button' value='Logout' /></div><h5 id='loading' style='display:none'>Working on it...</h5><script type='text/javascript'>var logout = document.getElementById('logout');var cancel = document.getElementById('cancel');function click(element){document.getElementById('title').style.display='none';document.getElementById('loading').style.display='block';logout.style.display='none';cancel.style.display='none';if (this.id === 'logout'){window.location = 'https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=' + encodeURIComponent('$authorizationUrl');}else{window.location = '$authorizationUrl';}}logout.onclick = click;cancel.onclick = click;</script></body></html>"

$web.add_DocumentCompleted(
{
$uri = [uri]$redirectUri
$queryString = [System.Web.HttpUtility]::ParseQueryString($_.url.Query)

if($_.url.authority -eq $uri.authority)
{
$authorizationCode = $queryString["code"]

if (![string]::IsNullOrEmpty($authorizationCode))
{
$form.DialogResult = "OK"
$Global:authorizationCode = $authorizationCode
$Global:authorizationCodeTime = [datetime]::Now
}

$form.close()
}
})

$dialogResult = $form.ShowDialog()

if($dialogResult -eq "OK")
{
$authorizationCode = $Global:authorizationCode
$headers = @{"Accept" = "application/json;odata=verbose"}
$body = "client_id=$clientId&client_secret=$clientSecretEncoded&redirect_uri=$redirectUriEncoded&grant_type=authorization_code&code=$authorizationCode"

$accessTokenResult = Invoke-RestMethod -Uri $accessTokenUrl -Method POST -Body $body -Headers $headers
$Global:accessTokenResult = $accessTokenResult
$Global:accessTokenResultTime = [datetime]::Now
$accessTokenResultText = (ConvertTo-Json $accessTokenResult)

if ($isCache -and ![string]::IsNullOrEmpty($accessTokenResultText))
{
[void](Set-Content -Path $cacheFilePath -Value $accessTokenResultText)
}

Write-Output (ConvertTo-Json $accessTokenResultText)
}

$web.Dispose()
$form.Dispose()
}

function Get-CachedAccessTokenResult()
{
if ($isCache -and [System.IO.File]::Exists($cacheFilePath))
{
$accessTokenResultText = Get-Content -Raw $cacheFilePath
if (![string]::IsNullOrEmpty($accessTokenResultText))
{
$accessTokenResult = (ConvertFrom-Json $accessTokenResultText)
if (![string]::IsNullOrEmpty($accessTokenResult.access_token))
{
$Global:accessTokenResult = $accessTokenResult

return $accessTokenResult
}
}
}

return $null
}

function Invoke-Refresh()
{
$refreshToken = $accessTokenResult.refresh_token
$headers = @{"Accept" = "application/json;odata=verbose"}
$body = "client_id=$clientId&client_secret=$clientSecretEncoded&resource=$resourceEncoded&grant_type=refresh_token&refresh_token=$refreshToken"
$accessTokenResult2 = Invoke-RestMethod -Uri $accessTokenUrl -Method POST -Body $body -Headers $headers

$accessTokenResult.scope = $accessTokenResult2.scope
$accessTokenResult.expires_in = $accessTokenResult2.expires_in
$accessTokenResult.ext_expires_in = $accessTokenResult2.ext_expires_in
$accessTokenResult.expires_on = $accessTokenResult2.expires_on
$accessTokenResult.not_before = $accessTokenResult2.not_before
$accessTokenResult.resource = $accessTokenResult2.resource
$accessTokenResult.access_token = $accessTokenResult2.access_token
$accessTokenResult.refresh_token = $accessTokenResult2.refresh_token

$Global:accessTokenResult = $accessTokenResult
$Global:accessTokenResultTime = [datetime]::Now
$accessTokenResultText = (ConvertTo-Json $accessTokenResult)

if (![string]::IsNullOrEmpty($accessTokenResultText))
{
[void](Set-Content -Path $cacheFilePath -Value $accessTokenResultText)
}

Write-Output (ConvertTo-Json $accessTokenResultText)
}

$accessTokenResult = Get-CachedAccessTokenResult
if ($accessTokenResult -eq $null)
{
Invoke-OAuth
}
elseif ($refreshForce -or (([datetime]::Parse("1/1/1970")).AddSeconds([int]$accessTokenResult.expires_on).ToLocalTime() -lt ([datetime]::Now).AddMinutes(5)))
{
if ($isRefresh)
{
Invoke-Refresh
}
else
{
Invoke-OAuth
}
}
else
{
Write-Output (ConvertTo-Json $Global:accessTokenResult)
}