forked from microsoft/ALAppExtensions
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SharePoint Auth - Improved README and use the Token Cache (microsoft#…
…21591) After some fighting to get the SharePoint module to work, I want to improve the SharePoint Authorization module with my findings. 1. Improved the README.md file - Added information and examples of API Permissions and Scope needed to get the authorization to work. - Corrected the syntaxes of the functions 2. Added support for Token Cache when acquiring an Access Token - This removes unnecessary popups of the authorization window, and I believe it is faster - This makes it possible to schedule jobs that works against SharePoint, as long as the user is authenticated now and then (I believe the default life for a Refresh Token is 3 months) 3. Removed the custom caching of Access Token. - "SharePoint Authorization Code" had implemented it's own caching of the Access Token, with a hardcoded lifetime of 1 hour. From what I read, that would probably work for most cases since the lifetime of an Access Token is between 60 and 90 minutes. But as stated [here](https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-configurable-token-lifetimes), the lifetime of an Access Token can be configured to a shorter time, that would not work together with this "custom" token cache. A custom caching must read the Access Token lifetime to be water proof, but the easy fix here is to just remove it. :) - This change leaves the caching to the OAuth2 module. 4. Reworked the logic around the `IsSuccess` return variable. - This had some issues, where `AcquireTokenByAuthorizationCode()` would return true (it is a TryFunction, and some issues with acquiring an access token does not result in an `Error()`), but with an empty Access Token. This made me spend waaay to much time troubleshooting this codeunit... The only thing that really tells us if acquiring an Access Token failed or succeeded is if the Access Token has a value or not. The tests are unaffected by this change.
- Loading branch information
Showing
2 changed files
with
97 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,95 @@ | ||
This module provides functionality for authenticating to the SharePoint REST API. | ||
This module does not permanently store any data. | ||
|
||
### Create Client Credentials | ||
Creates Codeunit implementing "SharePoint Authorization" interface that can later be used with SharePoint module. | ||
# Public Objects | ||
|
||
## "SharePoint Auth." | ||
|
||
### Example | ||
|
||
This example shows how to use the `CreateAuthorizationCode()` function when saving a file to a SharePoint Online library. | ||
|
||
``` | ||
[NonDebuggable] | ||
procedure CreateUserCredentials(AadTenantId: Text; ClientId: Text; UserName: Text; Credential: Text; Scopes: List of [Text]): Interface "SharePoint Authorization"; | ||
internal procedure SaveFile(BaseUrl: Text; LibraryAndFolderPath: Text; Filename: Text; var TempBlob: Codeunit "Temp Blob") | ||
var | ||
SharePointFile: Record "SharePoint File"; | ||
SharePointClient: Codeunit "SharePoint Client"; | ||
SaveFailedErr: Label 'Save to SharePoint failed.\ErrorMessage: %1\HttpRetryAfter: %2\HttpStatusCode: %3\ResponseReasonPhrase: %4', Comment = '%1=GetErrorMessage; %2=GetHttpRetryAfter; %3=GetHttpStatusCode; %4=GetResponseReasonPhrase'; | ||
AadTenantId: Text; | ||
IS: InStream; | ||
Diag: Interface "HTTP Diagnostics"; | ||
begin | ||
AadTenantId := GetAadTenantNameFromBaseUrl(BaseUrl); | ||
SharePointClient.Initialize(BaseUrl, GetSharePointAuthorization(AadTenantId)); | ||
IS := TempBlob.CreateInStream(); | ||
if not SharePointClient.AddFileToFolder(LibraryAndFolderPath, Filename, IS, SharePointFile) then begin | ||
Diag := SharePointClient.GetDiagnostics(); | ||
Error(SaveFailedErr, Diag.GetErrorMessage(), Diag.GetHttpRetryAfter(), Diag.GetHttpStatusCode(), Diag.GetResponseReasonPhrase()); | ||
end; | ||
end; | ||
local procedure GetSharePointAuthorization(AadTenantId: Text): Interface "SharePoint Authorization" | ||
var | ||
SharePointAuth: Codeunit "SharePoint Auth."; | ||
Scopes: List of [Text]; | ||
ClientId: Text; | ||
[NonDebuggable] | ||
ClientSecret: Text; | ||
begin | ||
GetAppRegistration(ClientId, ClientSecret); | ||
Scopes.Add('00000003-0000-0ff1-ce00-000000000000/.default'); | ||
exit(SharePointAuth.CreateAuthorizationCode(AadTenantId, ClientId, ClientSecret, Scopes)); | ||
end; | ||
local procedure GetAadTenantNameFromBaseUrl(BaseUrl: Text): Text | ||
var | ||
Uri: Codeunit Uri; | ||
MySiteHostSuffixTxt: Label '-my.sharepoint.com', Locked = true; | ||
SharePointHostSuffixTxt: Label '.sharepoint.com', Locked = true; | ||
OnMicrosoftTxt: Label '.onmicrosoft.com', Locked = true; | ||
UrlInvalidErr: Label 'The Base Url %1 does not seem to be a valid SharePoint Online Url.', Comment = '%1=BaseUrl'; | ||
Host: Text; | ||
begin | ||
// SharePoint Online format: https://tenantname.sharepoint.com/SiteName/LibraryName/ | ||
// SharePoint My Site format: https://tenantname-my.sharepoint.com/personal/user_name/ | ||
Uri.Init(BaseUrl); | ||
Host := Uri.GetHost(); | ||
if not Host.EndsWith(SharePointHostSuffixTxt) then | ||
Error(UrlInvalidErr, BaseUrl); | ||
if Host.EndsWith(MySiteHostSuffixTxt) then | ||
exit(CopyStr(Host, 1, StrPos(Host, MySiteHostSuffixTxt) - 1) + OnMicrosoftTxt); | ||
exit(CopyStr(Host, 1, StrPos(Host, SharePointHostSuffixTxt) - 1) + OnMicrosoftTxt); | ||
end; | ||
``` | ||
|
||
### Create Client Credentials | ||
|
||
Creates a Codeunit implementing "SharePoint Authorization" interface that can later be used with SharePoint module. | ||
|
||
This implementation is using the OAuth 2.0 Authorization Code Flow, which means that the access to SharePoint will be performed with the credentials of the currently logged on user, i.e. the user permissions will apply. | ||
|
||
The App Registration specified by the ClientId parameter should be assigned the _Delegated_ API Permissions that is needed for the intended operations in the "SharePoint Client" codeunit. | ||
|
||
Examples of delegated API Permissions on the **SharePoint** resource: | ||
| Resource | API Permission | Used for | | ||
| -------- | -------------- | -------- | | ||
| SharePoint | AllSites.Write | Reading and writing files on a regular SharePoint site | | ||
| SharePoint | MyFiles.Write | Reading and writing files on the SharePoint My Site (The Personal OneDrive site) | | ||
|
||
### CreateAuthorizationCode | ||
|
||
Creates an authorization mechanism with authentication code. | ||
|
||
'00000003-0000-0ff1-ce00-000000000000/.default' can be used as the `Scope` parameter, where the guid is the Application Id for Office 365 SharePoint Online. | ||
|
||
#### Syntax | ||
|
||
``` | ||
[NonDebuggable] | ||
procedure CreateUserCredentials(AadTenantId: Text; ClientId: Text; UserName: Text; Credential: Text; Scope: Text): Interface "SharePoint Authorization"; | ||
procedure CreateAuthorizationCode(AadTenantId: Text; ClientId: Text; ClientSecret: Text; Scopes: List of [Text]): Interface "SharePoint Authorization"; | ||
``` | ||
|
||
|
||
``` | ||
[NonDebuggable] | ||
procedure CreateAuthorizationCode(AadTenantId: Text; ClientId: Text; ClientSecret: Text; Scope: Text): Interface "SharePoint Authorization"; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters