cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

Call of External OData service gives 403 Forbidden response

paul_dunk
Explorer
0 Kudos
1,853

I am attempting to call the Okta 'Get User with ID' service from an ABAP program. The call is returning a 403 status with a reason of Forbidden and I cannot determine why. Any help or guidance on what I am doing wrong is appreciated. Also, if I have selected the wrong tags then please feel free to direct me to the correct tags. Thanks.

I am able to make this call from Postman. The documentation for the call can be found at https://developer.okta.com/docs/reference/api/users/. The Request example is:

curl -v -X GET \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${api_token}" \
"https://${yourOktaDomain}/api/v1/users/00ub0oNGTSWTBKOLGLNR"

Below is the pertinent ABAP code with the Okta token replaced with 'xxxxxxxx', our domain replaced with <myOktaDomain>, my Okta ID and SAP user name replaced with 'XXXXXX' and my password onto the SAP client replaced with 'xxxxxxxx'.

A few more details:

According to Okta, this is not using an OAuth authentication.

Our SAP instance is behind the firewall.

A similar, but simpler, call to a weather service works.

REPORT y_okta_call.

DATA: lo_http_client     TYPE REF TO if_http_client,
      lv_service         TYPE string,
      lv_result          TYPE string.
DATA: go_http_client TYPE REF TO if_http_client,
      go_rest_client TYPE REF TO cl_rest_http_client.

*Declarations for Data references
DATA:lo_json TYPE REF TO /ui2/cl_json.

DATA: lr_json TYPE REF TO /ui2/cl_json.

lv_service = 'https://<myOktaDomain>.okta.com/api/v1/users/XXXXXX'.

lv_service = lv_service && '?&format=json'.

cl_http_client=>create_by_url(
  EXPORTING
    url                = lv_service
  IMPORTING
    client             = go_http_client
  EXCEPTIONS
    argument_not_found = 1
    plugin_not_active  = 2
    internal_error     = 3
    OTHERS             = 4 ).

CONSTANTS gc_auth TYPE string VALUE 'Authorization'.
CONSTANTS gc_content_type TYPE string VALUE 'Content-Type'.
CONSTANTS gc_content_value TYPE string VALUE 'application/json'.
CONSTANTS gc_accept TYPE string VALUE 'Accept'.
CONSTANTS gc_accept_value TYPE string VALUE 'application/json'.
CONSTANTS: gc_e TYPE dd26e-enqmode VALUE 'E'.

DATA gv_auth_val TYPE string.

gv_auth_val = 'SSWS xxxxxxxx'.

CALL METHOD go_http_client->request->set_header_field
  EXPORTING
    name  = gc_accept
    value = gc_accept_value.

CALL METHOD go_http_client->request->set_header_field
  EXPORTING
    name  = gc_content_type
    value = gc_content_value.

CALL METHOD go_http_client->request->set_header_field
  EXPORTING
    name  = gc_auth
    value = gv_auth_val.

go_http_client->authenticate( username = 'XXXXXX' password = 'xxxxxxxx' ).

" set http protocol version
go_http_client->request->set_version(

if_http_request=>co_protocol_version_1_0 ) .

*Create REST Client object
CREATE OBJECT go_rest_client
  EXPORTING
    io_http_client = go_http_client.

TRY.

    go_rest_client->if_rest_client~get( ).

    DATA(lo_response) = go_rest_client->if_rest_client~get_response_entity( ).

    DATA(lv_http_status) = lo_response->get_header_field( '~status_code' ).

    IF lv_http_status NE 200.

*HTTP Request Failed
      DATA(lv_reason) = lo_response->get_header_field( '~status_reason' ).
*STOP Processing
      RETURN.

    ENDIF.

*Receive the response data in JSON.
    DATA(lv_json_data) = lo_response->get_string_data( ).

    IF go_http_client IS BOUND.

      go_http_client->refresh_response( ).

      "Close HTTP session (Exception HTTP_NO_MEMORY)
      go_http_client->close( ).

    ENDIF.

*Collect into the exception table.
  CATCH cx_rest_client_exception INTO DATA(lo_rest_client_exception).

ENDTRY.

Accepted Solutions (0)

Answers (2)

Answers (2)

Florian
SAP Champion
SAP Champion
0 Kudos

Don't you get an token back and afterwards you can run the request for the user?

I would say you have to run the auth-process and afterwards you can run your user-request.

At least I read the documentation that way..

https://developer.okta.com/docs/reference/api/authn/#primary-authentication-with-trusted-application

paul_dunk
Explorer
0 Kudos

Thanks Florian. This is very insightful. I cannot claim to be an expert on this and I had wondered the same. But I believe the Authentication API is for logging a user onto Okta: "The API is targeted for developers who want to build their own end-to-end login experience to replace the built-in Okta login experience". We are able to call Okta from SuccessFactors and I have confirmed with the SuccessFactors developer that they do not pass a device token to an Authentication API to receive back a token. They simply call the "Get User with ID" and pass our api token. I see that I did not do a good job of pasting the Request Example into the question. Here it is again:

curl -v -X GET \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${api_token}" \
"https://${yourOktaDomain}/api/v1/users/00ub0oNGTSWTBKOLGLNR"
zaberca_david
Explorer
0 Kudos

Hello Paul,

I don't see in Okta documentation why you are setting go_http_client->authenticate.

This method, I think, overrides your "Authorization" header with "Basic username:password(converted to base64)" and therefore your token is lost.

Try removing that line:

o_http_client->authenticate( username ='XXXXXX' password ='xxxxxxxx').
paul_dunk
Explorer
0 Kudos

Thanks very much for your reply. When I remove that line of code I receive the following popup when the line of code

go_rest_client->if_rest_client~get( ) is processed:

And then I get the 403 Forbidden result. But thanks again for your reply.