OAuth 2 authentication in some cases include flows that are using redirects (e.g. when asking for tokens using authorization code
: https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow):
An example of such process can look as follows:
//Example of external service URL that opens authentication dialog
//- Usually you are including additional parameters like client id or secret
//- You need to provide a redirect URL that will be used after successful confirmation
https://mock.api.com/AuthorizationUrl?client_id=someid&client_secret=Test2eyJhbGciOiJI&redirect_uri=http://restwithus-21:80/BC/?page=4075365&provider_id=1&endpoint_id=185
//After confirmation the browser is redirected back to the provided URL with appended access code or token
http://restwithus-21:80/BC/?page=4075365&provider_id=1&endpoint_id=185&token=Test2eyJhbGciOiJI
First add the OAuth Redirect
operation to Operation ID RWU
enum:
enumextension 50002 "Test Operation ID RWU" extends "Operation ID RWU"
{
value(50008; "TestProvider_OAuthRedirect")
{
Caption = 'OAuth Redirect (Test Provider)';
}
}
Now open the provider operations with Operations from the menu and create a new operation called OAuth2 Redirect
.
GET
./AuthorizationUrl
in the Endpoint field – i.e. the part of the URL without Provider URL (see Setting up API Provider).Open
, meaning you can update the operation.Next open Related / URL Parameters from menu and fill in URL parameters that you need to open the authentication window.
Client ID
and Client Secret
into provider Variables.RedirectUri
needs to be filled from code. This is the Business Central URL called after successful confirmation.Now open operation Details and check the option OAuth2 Redirect.
This ensures that the URL won't be called directly during operation EXECUTE and you can use RESTwithUS functions to wait for the redirect and process it.
Next step is to prepare the setup for processing the redirected URL after successful confirmation. For the most secure way create a new codeunit that implements IOperationId OAuth RWU
interface.
The codeunit must implement function OnRedirectURLRecieved
that will be called after successful redirect. Parameters of this function contain exact redirect URL and parsed query string in a dictionary format.
codeunit 50099 "OAuth Redirect Process" implements "IOperationId OAuth RWU"
{
procedure OnRedirectURLRecieved(_ProviderIDEnum: Enum "Provider ID RWU"; _OperationIDEnum: Enum "Operation ID RWU"; _QueryStringPairs: Dictionary of [Text, Text]; _RedirectURL: Text)
var
APIScriptRWU: Codeunit "API Script RWU";
begin
//If you use the same implementation for multiple redirect operations, you can switch them by operation ID
case _OperationIDEnum of
"Operation ID RWU"::TestProvider_OAuthRedirect:
begin
//Save access code sent in "token" URL parameter
APIScriptRWU.CREATE_TOKEN(_ProviderIDEnum, EntityID::TestProvider_Session, '', _QueryStringPairs.Get('token'), 0D, 0T);
end;
//...If needed, process other operation's redirect URLs
end;
//Clean batch entries in production environment (there are sensitive information)
APIScriptRWU.CLEAN_BATCH_ENTRIES(_ProviderIDEnum, _OperationIDEnum);
end;
}
See Token authentication guideline for more information about handling tokens in RESTwithUS.
Now modify the Operation ID RWU
enum extension for current operation and add the interface implementation.
enumextension 50002 "Test Operation ID RWU" extends "Operation ID RWU"
{
value(50008; "TestProvider_OAuthRedirect")
{
Caption = 'OAuth Redirect (Test Provider)';
Implementation = "IOperationId OAuth RWU" = "OAuth Redirect Process";
}
}
If you do not use the interface implementation, the redirect URL can still be processed with subscription to RESTwithUS event (see code below). You may need to use this way in older Business Central versions, but it is not recommended for sensitive information, since any other application can subscribe to this event, too.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"OperationId OAuth Default RWU", 'OnRedirectURLRecievedDefault', '', true, true)]
local procedure OnRedirectURLRecievedDefault(_ProviderIDEnum: Enum "Provider ID RWU"; _OperationIDEnum: Enum "Operation ID RWU"; _QueryStringPairs: Dictionary of [Text, Text]; _RedirectURL: Text)
begin
//Save the token to database here
end;
Let's now run the operation. Compared to standard RESTwithUS operation there are a few key differences:
redirect_uri
parameter. You can get the URL with GET_ENDPOINT_REDIRECTURL function. The redirect URL points back to your Business Central and contains provider and operation ids.var
APIScriptRWU: Codeunit "API Script RWU";
ProviderID: Enum "Provider ID RWU";
OperationID: Enum "Operation ID RWU";
RedirectUrl: Text;
AuthorizationUrl: Text;
//Set up the operation
APIScriptRWU.INIT(OperationID::TestProvider_OAuthRedirect);
APIScriptRWU.ENDPOINT(ProviderID::TestProvider, OperationID::TestProvider_OAuthRedirect);
//Create the redirect URL back to Business Central and add it to URL parameters
RedirectUrl := APIScriptRWU.GET_ENDPOINT_REDIRECTURL(ProviderID::TestProvider, OperationID::TestProvider_OAuthRedirect);
APIScriptRWU.ADD_VARIABLE('','RedirectUri',RedirectUrl);
//Execute the operation to create request batch entries and get the authorization page URL
//- RESTwithUS won't send a request to the URL with "OAuth2 Redirect" checked in the operation details
APIScriptRWU.EXECUTE();
AuthorizationUrl := APIScriptRWU.GET_REQUEST_URL();
//Open the authorization URL in popup window and start waiting for the redirect
APIScriptRWU.WAITING_FOR_REDIRECT(ProviderID::TestProvider, OperationID::TestProvider_OAuthRedirect, true);
APIScriptRWU.START_OAUTH_AUTHORIZATION(ProviderID::TestProvider, OperationID::TestProvider_OAuthRedirect, AuthorizationUrl);
If you run the code, RESTwithUS will open a new page in Business Central, that will wait for the confirmation to end. In the same time a popup window will open with the external service authentication form.
Please allow popups from this page in browser.
After the user completes the confirmation in the popup window, he will be redirected back to Business Central landing page.
The redirect URL will be now processed by your code (e.g. the token returned by the service will be saved for further use). RESTwithUS will also add the redirect URL to operation Batch Entries.
Usually the popup window is automatically closed after successful authentication.
Go to Operations, select the operation from the list and open Batch Entries from the menu:
As you can see, both URLs are saved there and you can use this information for debugging purposes.
Request
endpoint URL.Response
endpoint URL.In production environment it is safer to delete these batch entries after redirect, because they can contain sensitive information.
Plus you can check the Session
entity for the saved token.
The default RESTwithUS implementation will open new page in Business Central first – and then open the popup. For a cleaner experience (e.g. in a setup wizard) you may want to open the popup directly. In such case you need to make a few changes in the page.
First add OAuth Redirect RWU
addin to the page. The addin should have TimerTic
function that will periodically check if the redirect already occured and close the popup window afterwards.
usercontrol(OAuthRedirect; "OAuth Redirect RWU")
{
ApplicationArea = All;
trigger TimerTic()
var
APIScriptRWU: Codeunit "API Script RWU";
begin
if not APIScriptRWU.WAITING_FOR_REDIRECT(ProviderID::TestProvider, OperationID::TestProvider_OAuthRedirect) then begin
CurrPage.OAuthRedirect.CloseWindow();
CurrPage.Update(false);
end;
end;
}
And instead of calling START_OAUTH_AUTHORIZATION
function in the default code you need to open the popup window manually.
//Open the authorization URL in popup window and start waiting for the redirect
APIScriptRWU.WAITING_FOR_REDIRECT(ProviderID::TestProvider, OperationID::TestProvider_OAuthRedirect, true);
CurrPage.OAuthRedirect.LaunchURLinNewWindow(AuthorizationUrl);
CurrPage.OAuthRedirect.StartTimer();
There are issues with using add-ins from another extension in Business Central 14. Basically, the add-in works fine if you include it in your own extension and call it from there – but if you embed add-in from another extension to your page, you might run into issues.
For Business Central 14 the recommended way is to:
The event for processing redirect URL is slightly different, too:
[EventSubscriber(ObjectType::Codeunit, Codeunit::"OAuth Management RWU", 'OnAfterRedirectURLRecieved', '', true, true)]
local procedure OnAfterRedirectURLRecieved(_ProviderIDEnum: Enum "Provider ID RWU"; _OperationIDEnum: Enum "Operation ID RWU"; _QueryStringPairs: Dictionary of [Text, Text]; _RedirectURL: Text)
begin
//Save the token to database here
end;