Configure service to service authentication with GIP

To successfully authenticate a service (server side) to connect (as a service client) to another service protected by an access token, you will need to implement OAuth this way.

Before you begin:

The Genero Identity Provider (GIP) must be configured and enabled before it can be used - see Configure the primary Genero Identity Provider.

You will need your GAS running.

About this task:

In service-to-service – also known as "machine-to-machine" – implementation, an application authenticates itself with GIP using its Client ID and Client Secret. GIP validates this information and returns an access token with the scopes (permissions) you have registered for the Genero web service (server side) it can access, and the scopes for the web service client that the web service can access.

The stub files for both the service (server side) and the client service must be generated with the appropriate code to handle OAuth access, taking the access token into account. To do this, you must:
  1. Get an access token directly from the IdP using the GetToken tool.
  2. Run the fglrestful tool with the --oauth=yes option and provide the access token in the URL to generate the stub file.

You define scopes in your Genero REST web service (server side) and web service client source files (4gl) with the WSScope attribute. For more information about WSScope, refer to the WSScope topic in the Genero Business Development Language User Guide

The access token is provided to your application and web services via the GAS environment at runtime. To get the access token at runtime:
  1. In your client application, you must register the access token with the GWS engine using OAuthAPI.Init.
  2. In your service (server side), you must retrieve the access token, using either the fgl_getenv("OIDC_ACCESS_TOKEN") instruction or from the WSContext variable, to initiate the client service using OAuthAPI.InitService.

Create the web service (server side)

  1. Register the scopes for the service or APIs providing the service with the GIP using the Console App.
    Follow the steps described in Manage web service access scopes.
    You should already have defined scopes in your Genero web service source file (4gl) with the WSScope attribute to provide security globally for the service (on the WSInfo record) or for its functions. You should have a list of such WSScope entries.
  2. Generate the stub for your Genero web service (client).

    You must have the web service (client) secured by the GIP and have its scopes (permissions) registered. You must also have deployed the web service on the GAS before carrying out these steps.

    1. Get an access token from the GIP to access the secured web service.

      Use the GetToken tool. For further information on using GetToken, go to GetToken.

      Line breaks have been added to the command example to improve readability.
      fglrun GetToken password -u user -p mypw 
      --idp https://host:port/gas/ws/r/services/GeneroIdentityProvider
      --savetofile myWsClientToken.json myWSClientScope
      Where:
      • The password command is used to get an access token on behalf of an authenticated user.
      • The user name and password provided (-u user -p mypw) is that of a registered user in the GIP.
      • The URL of the GIP is specified in the --idp option.
      • myWsClientToken.json is the filename where the access token is saved.
      • myWSClientScope is the list of scopes (permissions) defined in your Genero web service source file (4gl) with the WSScope attribute. You should have a list of such WSScope entries.

      You get an access token that is valid for 10 minutes. After ten minutes, you will need to query for a new access token.

    2. (Optional) Test to ensure you can access the web service with the scopes in the token.
      • If your GAS is not running, start it.
      • Enter the URL of the web service in your browser to get the OpenAPI documentation, adding the token obtained in the previous step in the query:

      http://myhost:6394/gas/ws/r/myGroup/myXcf/myClientService?openapi.json&access_token=token

      If you can see the OpenAPI documentation displayed, you have accessed the protected service.
    3. Generate the stub with code to handle OAuth access.
      Use the fglrestful tool.
      Line breaks have been added to improve readability.
      fglrestful -o ws_client 
          --oauth yes 
          --tokenfile myWsClientToken.json
          http://myhost:6394/gas/ws/r/myGroup/myXcf/myClientService?openapi.json
      Where:
      • ws_client specifies the filename of the stub in the output (-o) option.
      • --oauth yes specifies that the web service is secured by GIP and code to handle OAuth access is generated in the stub, taking the access token into account.
      • --tokenfile specifies myWsClientToken.json as the file where the token was saved.
      • ?openapi.json query string gets the OpenAPI document.

      The ws_client.4gl is generated from the OpenAPI document.

    4. Compile ws_client.4gl.

      The stub file must be imported into your web service (server side) application.

  3. Code in your Genero REST web service (server side) to call your client service.
    Write code in your web service 4gl module:
    1. Import the client service stub file module.
      IMPORT FGL ws_client
    2. Import the OAuthAPI library.
      IMPORT FGL OAuthAPI
    3. Code to call the client service function.
      You must retrieve the access token and initiate the client service before calling any of its functions. The code to do this is shown in the sample function.

      The access token – provided via the GAS environment at runtime – is registered in a global variable ctx when the service (server side) starts, and is retrieved for forwarding to the web service client when using OAuthAPI.InitService(5, access_token). For more information about OAuthAPI.InitService, refer to the Genero Business Development Language User Guide.

      # Web service (server side module)
      IMPORT FGL ws_client
      IMPORT FGL OAuthAPI
      
      PRIVATE DEFINE ctx DICTIONARY ATTRIBUTE(WSContext) OF STRING
      
      PUBLIC FUNCTION myClientServiceCall()
          DEFINE access_token STRING
          DEFINE wsstatus INTEGER
          DEFINE ret string
      
          # retrieve access_token for forwarding to the client service
          LET access_token=ctx["OIDC_ACCESS_TOKEN"]
      
          # Init OAuth service
          IF NOT OAuthAPI.InitService(5, access_token) THEN
              RETURN "Cannot initiate OAuth service"
          END IF
      
          # Call the client service via the stub file
          CALL ws_client.myFunc() RETURNING wsstatus
      
          CASE wsstatus
          WHEN ws_client.C_SUCCESS
            LET ret=SFMT("Successfull ws_client call:%1",wsstatus)
          OTHERWISE
            LET ret=SFMT("Unexpected ws_client error :%1",wsstatus)
          END CASE
          RETURN ret
      END FUNCTION
      # Other web service (server side) functions
  4. Configure your web service (server side) xcf file for delegation.
    In the DELEGATE element set the service attribute to the Genero GeneroAccessService, and provide the URL of the GIP in the IDP element.
    <APPLICATION Parent="ws.default" ...>
      <EXECUTION>
        </ENVIRONMENT_VARIABLE>
        </PATH>
        </MODULE>
        <DELEGATE service="services/GeneroAccessService">
           <IDP>https://host:port/gas/ws/r/services/GeneroIdentityProvider</IDP>
        </DELEGATE>
       #...
      </EXECUTION>
    </APPLICATION>
    GeneroAccessService is the REST web service in the $FGLDIR that handles delegation in the service calls, and forwards the access token if used in a call to another service.
  5. Deploy and secure your web service (server side) in a Genero Archive file using the Deployment App.

    You must package your applications and services into a Genero Archive for deployment on the GAS. You create a Genero Archive (gar) with fglgar gar. For more information about packaging a Genero Archive, refer to the Deploying apps with Genero Archive pages in the Genero Application Server User Guide.

    Your gar file must have a MANIFEST file pointing to your web service configuration file (xcf).
    For details of deploying your gar, go to the Deploy a web service using the Deployment App.
    The Deployment App secures the web service.
  6. Generate the stub for your Genero web service (server side).
    1. Get an access token from the GIP to access the secured web service.

      Use the GetToken tool. For further information on using GetToken, go to GetToken.

      Line breaks have been added to the command example to improve readability.
      fglrun GetToken password -u user -p mypw 
      --idp https://host:port/gas/ws/r/services/GeneroIdentityProvider
      --savetofile mytoken.json myWSScope
      Where:
      • The password command is used to get an access token on behalf of an authenticated user.
      • The user name and password provided (-u user -p mypw) is that of a registered user in the GIP.
      • The URL of the GIP is specified in the --idp option.
      • mytoken.json is the filename where the access token is saved.
      • myWSScope is the list of scopes (permissions) defined in your Genero web service (server side) and web service client source files (4gl) with the WSScope attribute. You should have a list of such WSScope entries.

      You get an access token that is valid for 10 minutes. After ten minutes, you will need to query for a new access token.

    2. (Optional) Test to ensure you can access the web service with the scopes in the token.
      • If your GAS is not running, start it.
      • Enter the URL of the web service in your browser to get the OpenAPI documentation, adding the token obtained in the previous step in the query:

      http://myhost:6394/gas/ws/r/myGroup/myXcf/myService?openapi.json&access_token=token

      If you can see the OpenAPI documentation displayed, you have accessed the protected service.
    3. Generate the stub with code to handle OAuth access.
      Use the fglrestful tool.
      Line breaks have been added to improve readability.
      fglrestful -o ws_stub 
          --oauth yes 
          --tokenfile mytoken.json
          http://myhost:6394/gas/ws/r/myGroup/myXcf/myService?openapi.json
      Where:
      • ws_stub specifies the filename of the stub in the output (-o) option.
      • --oauth yes specifies that the web service is secured and code to handle OAuth access is generated in the stub, taking the access token into account.
      • --tokenfile specifies mytoken.json as the file where the token was saved.
      • ?openapi.json query string gets the OpenAPI document.

      The ws_stub.4gl is generated from the OpenAPI document.

    4. Compile ws_stub.4gl.

      The stub file must be imported into your client application.

Create the client application

  1. Create your Genero REST client application.
    This is a browser-based application secured by your IdP that will provide the interface to the web services resources and operations.
    For more details on calling a web service through a client application, refer to the Calling a web service function section in the Genero Business Development Language User Guide.
    Write code in your 4gl module:
    1. Import the service stub file module.
    2. Import the OAuthAPI library.
    3. In the MAIN program block, code to retrieve the OIDC IDS – provided via the GAS environment at runtime – and initiate OAuth.
      Important: Initiate OAuth first

      Call the OAuthAPI.init() function to register the access token and access information, such as subject, scopes, endpoints, and so on, with the GWS engine before making any calls to the service functions.

      IMPORT FGL ws_stub
      IMPORT FGL OAuthAPI
      
      DEFINE c_client_id STRING
      DEFINE c_secret_id STRING
      DEFINE my_user_id  STRING
      
      MAIN
        # Init OAuthAPI
        LET c_client_id = fgl_getenv("OIDC_CLIENT_ID")
        LET c_secret_id= fgl_getenv("OIDC_SECRET_ID")
        IF NOT OAuthAPI.Init(5, c_client_id, c_secret_id) THEN
           DISPLAY "Error: unable to initialize OAuth"
           EXIT PROGRAM 1
        ELSE
          LET my_user_id = OAuthAPI.getIDSubject
        END IF
      
        # ... calls to web service functions
        
      END MAIN
  2. Register the application with the GIP using the Console App.
    For details about using the Console App's Applications > New app menu to register an application, go to Manage applications
    The Console App generates the CLIENT_PUBLIC_ID and CLIENT_SECRET_ID that you use for requesting access tokens.
  3. Configure your Genero web application xcf file for delegation.
    Add the DELEGATE element and set its service attribute to the Genero OpenIDConnectServiceProvider. Provide the URL of the GIP and the CLIENT_PUBLIC_ID and CLIENT_SECRET_ID tokens obtained when registering your application.
    <APPLICATION Parent="defaultwa" ...>
      <EXECUTION>
       #...
        </PATH>
        </MODULE>
        <DELEGATE service="services/OpenIDConnectServiceProvider">
          <IDP>https://host:port/gas/ws/r/services/GeneroIdentityProvider</IDP>
          <SCOPE>email</SCOPE>      
          <CLIENT_PUBLIC_ID>XXXXXXXX.apps.myOAUTHIdpusercontent.com</CLIENT_PUBLIC_ID>
          <CLIENT_SECRET_ID>XXXXXX-XXXXXX</CLIENT_SECRET_ID>         
        </DELEGATE>
       #...
      </EXECUTION>
    </APPLICATION>
  4. Deploy and secure your application with the GIP using the Deployment App.

    Test your application by starting the GAS and entering the application URL in the browser. If your browser is redirected to the GIP to enter your credentials, you have configured your application correctly. If your credentials are valid, your browser is redirected back to the application; otherwise, an HTML error page is returned.

Troubleshooting access
  • If errors are encountered accessing your service, check the logs: vm-services-GeneroAccessService*.log, and the AccessService.log:
    For instance, if you get error 403 "Forbidden" and you see the following message in the AccessService.log:
    ERROR : 29389 - [AccessService] "RetrieveIDPMetadata" INSERT failed :UNIQUE constraint failed: fjs_provider.issuer
    Check that the GIP's URL in the DELEGATE element of your web service xcf is correct.
  • If a call made to a Genero web service function fails, check the error status code returned to the client. For example, a value of -1 indicates a failed operation. The code -15553 with a description such as Asynchronous Connection failed or connection timeout expired indicates an infrastructure error.

    Check that you have a call to OAuthAPI.init() in your client application as shown in the step to Create your Genero REST client application.

    For more information on handling errors, refer to the Handle REST server errors page in Genero Business Development Language User Guide

What to do next:

Users also need to have the required scopes. When considering users who need to use the application and the web service, you must ensure users have the required access scopes. For ideas on how to manage access to applications and web services, go to Genero Identity Provider scenario. For more information on managing GIP users, go to Manage groups.