Introduction​

This article assumes knowledge of how to use the Asset Manager of the Xbase++ Workbench. You will find an introduction to the Asset Manager of the Xbase++ Workbench in the article Xbase++ Workbench Asset Management.

The Xbase++ OAuth2 asset implements the protocols for the OAuth2 Code Grant and Implicit Grant protocol. A key feature of these security protocols is the use of secret information that must be known only to the owner of an application for which authorization is requested. For this reason, it is not possible to deliver an example that is executable out of the box. At least the secret information must be made known to the application.

Included in the OAuth2 asset are examples for the Code Grant and Implicit Grant to access the Microsoft OneDrive API. The examples only need to be modified in very few places to be working. If a different API is to be accessed, the included examples need to be modified to a greater extent.

You can add an OAuth2 example to your project by clicking on the first example link in the asset's readme. The Asset Manager will then add the sample oauth2-codegrant-borwser-example.exe to the project. The example itself is implemented in the source file oauth2-codegrant-browser-example.prg.

Grant class​

The code grant is implemented in the example source file by the OAuth2CodeGrant class. In order for the class to communicate with your application, you must derive a class from OAuth2CodeGrant and implement the methods of the interface to your application. Your methods are usually passed an instance of the DataObject() class whose member variables you must assign as required. For details, please refer to the documentation of the endpoint against which you want to authenticate. In general, this information corresponds to that required by RFC 6794 "The OAuth 2.0 Authorization Framework".

In the asset example for the code grant, the OneDriveCodeGrant() class is derived from OAuth2CodeGrant() and implements these interfaces:

Xbase++:
...
METHOD getRedirectPort

METHOD getHtmlRedirectFailureResponse
METHOD getHtmlRedirectSuccessResponse

METHOD configureRequestCommon
METHOD configureAuthorizationRequest
METHOD configureTokenRequest
METHOD configureRefreshRequest
...

The methods whose names begin with "configure" are called during authorization and access token determination.

Grant Flow​

Access to a resource (in our example, the Microsoft OndDrive API) requires an Access Token. A key characteristic of the Access Token is that it is short-lived. Accordingly, an Access Token must be determined regularly. The OAuth2CodeGrant flow proceeds as follows.

1. Access Token available​

If an access token already exists and if it has not expired yet, then it will be used. This task is taken over by the OAuth2CodeGrant class without you having to write code for this.

2. Read Access Token from wallet​

If no Access Token is available, for example after starting the application, it is checked whether a wallet is available from which the Access Token is read. The Access Token is used if it is still valid.

The wallet is not implemented in our example. But the example source code header contains information on how to do it.

3. Query Access Token from Refresh Token​

OAuth2CodeGrant() will request a new Access Token. For this purpose, it uses a refresh token, which is also stored in the wallet, as well as information that is determined by calling the above mentioned method "configureRequestCommon":

Xbase++:
METHOD OneDriveCodeGrant:configureRequestCommon( oRequest )
   // Todo: Assign your Client Id here
   oRequest:client_id      := ""
   oRequest:redirect_uri   := "http://localhost:"+Var2Char(::getRedirectPort())+"/RedirectionSink/redirected"
RETURN SELF

For the OneDrive API you have to assign your client ID here. For the use of other APIs the redirect_uri can be left like this in many cases. However, you may need to specify it exactly this way when configuring your application: "http://localhost:85/RedirectionSink/redirected"

After part of the configuration has been done in the :configureRefreshRequestCommen() method, it is completed in the :configureRefreshRequest() method.

Xbase++:
METHOD OneDriveCodeGrant:configureRefreshRequest( oRefreshRequest, cRefreshToken )
   oRefreshRequest:ServerUrl     := "https://login.microsoftonline.com/common/oauth2/v2.0/token"
   oRefreshRequest:grant_type    := "refresh_token"
   oRefreshRequest:refresh_token := cRefreshToken
   // According to RFC 6749 Section 6 the member client_secret
   // is not required in a refresh request. However, the member
   // is mandatory for the OneDrive API.
   // Todo: Assign your Client Secret here
   oRefreshRequest:client_secret := ""
RETURN self

At this point we also see that the use of the OneDrive API requires the assignment of the client secret. Other OAuth2 peers may not need this or may deny access if client_secret is set here. When using an API other than OneDrive, the ServerUrl must also be adjusted according to its documentation.

The OAuth2CodeGrant class now receives a new Access Token and, if applicable, a new Refresh Token. Both are stored in the wallet for later use.

4. Authorization​

If the application was not yet authorized or the assignment of a new Access Token fails, an authorization must be performed. This is done in 3 steps where the first step again involves the methods ":configureRequestCommen()" and additionally the method ":configureAuthorizationRequest()".

4.1 Authorization request​

The Method ":configureRequestCommone()" is called first and then ":configureAuthorizationRequest()":

Xbase++:
METHOD OneDriveCodeGrant:configureAuthorizationRequest( oAuthorizationRequest )
   oAuthorizationRequest:ServerUrl     := "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
   oAuthorizationRequest:response_type := "code"
   oAuthorizationRequest:state         := UUIDToChar( UUIDCreate() )
   oAuthorizationRequest:scope          := "Files.ReadWrite offline_access"
RETURN SELF

Nothing needs to be adjusted here to use the OneDrive API. Other endpoints will certainly need a different :ServerUrl and :Scope.

4.2 Token request​

After a successful authorization request the token request is executed. As preparation the method :configureTokenRequest() is executed.

Xbase++:
METHOD OneDriveCodeGrant:configureTokenRequest( oTokenRequest, oAuthorizationResult )
   oTokenRequest:ServerUrl       := "https://login.microsoftonline.com/common/oauth2/v2.0/token"
   // Todo: Assign your Client Secret here
   oTokenRequest:client_secret   := ""
   oTokenRequest:grant_type      := "authorization_code"
   oTokenRequest:code            := oAuthorizationResult:Code
RETURN .T.

At this point the client secret must be assigned. For the Microsoft OneDrive API, the ServerUrl can remain unchanged. For other endpoints, the URL must be set accordingly.

As a result, the Access Token and the Refresh Token are stored in the wallet.

User Interaction​

For the authorization (step 4.1 above) a UI is needed that implements the LoginFormAdapter interface. In our example this is the class LoginForm() which is passed in the procedure Main of the CodeGrant class:

Xbase++:
oCodeGrant:setLoginFormAdapter( LoginForm():new() )

Here in this example LoginForm starts WebBrowser in its : open() method. In the :close() method, the incoming consent is waited for before continuing with step 4.2 (see above).

OAuth2Client​

Finally, a client is needed for the communication to request an Access / Refresh token but also to access the OneDrive API. In our example, this is the OneDriveClient class which inherits from OAuth2Client. OAuth2Client in turn inherits from the HttpRequest class. The OneDriveClient class thus combines two properties. On the one hand, it is suitable for calling the OneDrive RESTful API. On the other hand, it maintains the required access token in the request. The framework implicitly requests a new access token if necessary, or possibly a new refresh token or even initiates a new authorization.

The instances of the OneDriveCodeGrant() and OneDriveClient() classes must be made known to each other. This is done in the Main function.

Xbase++:
oOneDriveClient := OneDriveClient():new()
oOneDriveClient:setOAuth2Grant( oCodeGrant )

In this example, the OneDriveClient additionally implements a method to access the RESTful API interface "/me/drive" with a GET request. To access further API interfaces, additional methods can be implemented analogously.

Summary​

The OAuth2 asset enables you to communicate with endpoints that require the OAuth2 protocol for authorization. The details of the required OAuth2 authorization parameters can be found in the documentation of the endpoint.

In this article, the OAuth2 asset example for the code grant flow was used to show how the OneDrive API can be addressed. For this, only the secret and the client id had to be entered in the source code of the example code.

Communication with other OAuth2 endpoints is possible in the same manner. For this purpose, only the values in the callback methods of the OAuthCodeGrant class must be set accordingly.

For authorization with the OAuth2 Implicit Grant, the same applies as shown here. You can refer to the corresponding example from the asset readme.

References​