Configure service to service authentication with IdP

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:

Before you begin, you must have an account with an OpenID Connect/OAuth2 provider that will provide authentication services for you.

Determine which single sign-on protocol the IdP uses: OpenID Connect or OAuth2. If the IdP is using the OAuth2 protocol, you will need to register the IdP's OAuth2 endpoints in the GAS. To learn about how to check this, go to Identify the protocol in use by IdP.
Important:

If the IdP is using the OpenID Connect protocol, there is no need to use the ImportOAuth program as OpenID Connect has the metadata feature that allows Genero's OpenID Connect service to automatically fetch the metadata, including all the endpoint URLs, from the IdP.

Go to Import IdP metadata as OAuth2 for instructions on importing the metadata in the GAS.

About this task:

In service-to-service – also known as "machine-to-machine" – implementation, an application authenticates itself with the Identity Provider (IdP) using its Client ID and Client Secret. The IdP 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 web service or APIs providing the service on the IdP site.
    For more information on registering a web service, refer to the IdP documentation. Typically, it consists of the following tasks:
    1. Register the web service with the IdP.
    2. Register all the scopes (permissions) that might be granted to the client.
      These are the scopes you should already have defined in your web service 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.
    3. Register the GAS delegation service oauth2callback URL
      Typically, the oauth2callback redirect URL is the URL of the OpenIDConnect service on the GAS where the IdP will redirect the user-agent to get the OAuth2 ID token. For more information on the callback URL, go to SSO oauth2callback redirect URL
  2. Generate the stub for your Genero web service (client).

    You must have the web service (client) secured by the IdP 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 IdP 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://my_OAuth_provider.oauth.com
        --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 IdP 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 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 IdP in the IDP element.
    <APPLICATION Parent="ws.default" ...>
      <EXECUTION>
        </ENVIRONMENT_VARIABLE>
        </PATH>
        </MODULE>
        <DELEGATE service="services/GeneroAccessService">
           <IDP>my_OAuth_provider_url</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 enable your service on the GAS using a Genero Archive file.
    For more details of deploying and managing applications and web services using Genero Archives on the GAS, refer to the Deploying and managing applications with GAR section in the Genero Application Server User Guide
    Your gar file must have a MANIFEST file pointing to your web service configuration file (.xcf).
    1. Deploy the gar file on the GAS.
      Use the gasadmin tool; for example, gasadmin gar --deploy-archive myServiceGar.gar
    2. Enable the service on the GAS
      Use the gasadmin tool; for example, gasadmin gar --enable-archive myServiceGar.gar
  6. Generate the stub for your Genero web service.
    1. Get an access token to access the web service secured by the IdP.

      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://my_OAuth_provider.oauth.com
        --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 IdP.
      • The URL of the IdP 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. On the IdP site, register your Genero application.
    Single sign-on implementation may vary depending on the IdP; refer to the IdP documentation for more information.
    1. Register the web application.
    2. Register the GAS delegation service oauth2callback URL
      Typically, the oauth2callback redirect URL is the URL of the OpenIDConnect service on the GAS where the IdP will redirect the user-agent to get the OAuth2 ID token. For more information on the callback URL, go to SSO oauth2callback redirect URL
    The IdP provides the CLIENT_PUBLIC_ID and CLIENT_SECRET_ID that you use for requesting access tokens to the protected resources.
  3. Configure your Genero web application xcf file for delegation to the IdP.
    Add the DELEGATE element and set its service attribute to the Genero OpenIDConnectServiceProvider. Provide the URL of the IdP 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>my_OAuth_provider_url</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>
    The configuration differs slightly depending on the Single sign-on protocol used by the IdP.
  4. Deploy and enable your application on the GAS using a Genero Archive file.
    For more details of deploying and managing applications and web services using Genero Archives on the GAS, refer to the Deploying and managing applications with GAR section in the Genero Application Server User Guide
    Your gar file must have a MANIFEST file pointing to your application configuration file (.xcf).
    1. Deploy the gar file on the GAS.
      Use the gasadmin tool; for example, gasadmin gar --deploy-archive myAppGar.gar
    2. Enable the application on the GAS
      Use the gasadmin tool; for example, gasadmin gar --enable-archive myAppGar.gar

    Test your application by starting the GAS and entering the application URL in the browser. If your browser is redirected to the IdP 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 IdP'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.

    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 web services, 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 user scopes, refer to the IdP documentation.