OAuth authorization code flow from ABAP on-premise
Introduction
Using the authorization code flow directly from ABAP is rather not a frequent case. If your ABAP is a backend then (usually) client credentials might be used to communicate with another backend, as it does not involve user interaction. But if it required - then authorization code flow can be run and user has to approve or reject the access to a protected resource. In a web application you would then see a form for login and authorize requesting app to access protected resources. Having this from classic ABAP is not so straightforward.
Sample API + Auth0 as an OAuth server
To have a working sample I am using Auth0 free features
for registering an OAuth protected backend, which I will call from ABAP.
Using their Quick Samples I also prepared
a dumb, but working API which
will be protected and requires a valid JWT obtained from Auth0 (OAuth server).
The API is deployed to Vercel and it is quite simple - if
we are authorized, a call to /authorized
path should return Secured Resource
text.
var express = require("express");
var app = express();
var { expressjwt: jwt } = require("express-jwt");
var jwks = require("jwks-rsa");
var port = process.env.PORT || 8080;
var jwtCheck = jwt({
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: "https://dev-ycxqrx2b.us.auth0.com/.well-known/jwks.json",
}),
audience: "https://samples-oauth-ac-backend.vercel.app",
issuer: "https://dev-ycxqrx2b.us.auth0.com/",
algorithms: ["RS256"],
});
app.use(jwtCheck);
app.get("/authorized", function (req, res) {
res.send("Secured Resource");
});
app.listen(port);
The API is deployed under https://samples-oauth-ac-backend.vercel.app. Accessing without an access token is giving correctly "Unauthorized" response:

I have registered it in Auth0 as an API and Application. Please check the previous post about the details as the steps are the same.
ABAP code
- I am using ABAP Developer edition 1909
- for simplicity exception handling in the code is very basic
- please remember to import the SSL certificate to STRUST for OAuth0 site - head to the token URL in your browser, then download the certs & import. Check the abapGit docs for instructions
First I created a destination to my protected API:

(for simplicity of the example I'm not using scopes).
Then I registered a new profile in ABAP OAuth client (again please check the previous post for more details):

It has authorization code
set as a grant type.
As we need user interaction to permit/deny access to a protected resource,
we need to call a browser, which will run a page delivered by ABAP OAuth client, which in turn
will show Auth0 form to login and grant access to my protected Vercel API. The path
for it is /sap/bc/sec/oauth2/client/grant/authorization?profile=Z_AUTH0_AC_PROFILE
.

After logon I will see that the token was issued:

When I close the dialog, the code continues and displays the output from my secured endpoint:

CLASS zcl_oauth_ac_client DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
run.
PRIVATE SECTION.
METHODS:
prepare_http_client,
request_token,
call_backend,
set_token RAISING cx_oa2c.
DATA:
http_client TYPE REF TO if_http_client.
ENDCLASS.
CLASS zcl_oauth_ac_client IMPLEMENTATION.
METHOD run.
prepare_http_client( ).
TRY.
set_token( ).
call_backend( ).
CATCH cx_oa2c_at_not_available
cx_oa2c_at_expired.
request_token( ).
RETRY.
CATCH cx_root INTO DATA(exception).
WRITE: / exception->get_text( ).
ENDTRY.
ENDMETHOD.
METHOD call_backend.
" Configure the rest of your call
me->http_client->request->set_header_field(
name = '~request_method'
value = 'GET' ).
me->http_client->send(
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2 ).
IF sy-subrc <> 0.
" handle error...
RETURN.
ENDIF.
http_client->receive(
EXCEPTIONS
http_communication_failure = 1
http_invalid_state = 2
http_processing_failed = 3 ).
IF sy-subrc <> 0.
" handle error...
RETURN.
ENDIF.
DATA(response) = http_client->response->get_cdata( ).
cl_demo_output=>display_text( response ).
ENDMETHOD.
METHOD prepare_http_client.
cl_http_client=>create_by_destination(
EXPORTING
destination = 'VERCEL_BACKEND_AC'
IMPORTING
client = me->http_client
EXCEPTIONS
argument_not_found = 1
destination_not_found = 2
destination_no_authority = 3
plugin_not_active = 4
OTHERS = 5 ).
IF sy-subrc <> 0.
" handle error...
ENDIF.
ENDMETHOD.
METHOD request_token.
cl_abap_browser=>show_url(
url = 'https://localhost:50001/sap/bc/sec/oauth2/client/grant/authorization?profile=Z_AUTH0_AC_PROFILE' ).
ENDMETHOD.
METHOD set_token.
DATA(outh_client) = cl_oauth2_client=>create(
i_profile = 'Z_AUTH0_AC_PROFILE'
i_configuration = 'Z_AUTH0_AC_PROFILE' ).
outh_client->set_token(
io_http_client = me->http_client
i_param_kind = if_oauth2_client=>c_param_kind_header_field ).
ENDMETHOD.
ENDCLASS.
Links
- sample API - https://github.com/wozjac/samples-oauth-ac-backend
- ABAP code - https://github.com/wozjac/samples-oauth-ac-abap
- Auth0 - https://auth0.com