Authenticate and Validate Webhook Messages

This topic describes how to authenticate and validate webhook messages during runtime execution.

The Rapid Adapter Builder platform supports the authentication and validation of both inbound and outbound webhook messages.

For business scenarios where designtime configurations require invoking an external application’s API, the adapter developer must ensure to implement the authentication scheme that is supported by the external application. These implementations may include handling the following scenarios:

  • Configure a trigger that needs to obtaining data from an external application so that the integration developer can make choices during configuration.

  • Create a schema dynamically by obtaining metadata information from an external application.

  • Register and deregister a subscription by calling an external application’s API.

To authenticate inbound messages, the adapter developer can enforce security by defining the composite security policy in the connection section of the adapter definition document.

The composite security policy in the connection section of adapter definition document consists of the following sections:

  • policyOutbound

    Implements the external application's authentication scheme for invoking their API. For example, most external applications and services use standard Oauth policies to protect their APIs and permit outbound calls.

  • policyInbound

    Implements authentication schemes that allow Oracle Integration to validate runtime messages sent by external applications and services.

Note:

External applications and services mostly influence the design of webhook security. Most services use digital signatures to validate webhook messages.

The policyInbound section supports digital signatures. The Rapid Adapter Builder platform supports the following valid values:

  • DIGITAL_SIGNATURE
  • HMAC_SIGNATURE_VALIDATION
  • RSA_SIGNATURE_VALIDATION
  • JWT_VALIDATION

Sample code that defines a security policy that supports Github triggers and utilizes the HMAC_SIGNATURE_VALIDATION managed policy:

"securityPolicies": [
   {
     "type": "composite",
     "description": "This policy is used by OIC for validating incoming requests as well as for invoking GitHub APIs",
     "displayName": "GitHub security policy",
     "scope": "TRIGGER",
     "policyOutbound": {
       "type": "managed",
       "policy": "OAUTH2.0_AUTHORIZATION_CODE_CREDENTIALS",
       "securityProperties": [
         {
           "name": "oauth.client.id",
           "displayName": "Client Id",
           "description": "Client Id",
           "shortDescription": "Client Id",
           "required": true,
           "hidden": false
         },
         {
           "name": "oauth.client.secret",
           "displayName": "Client Secret",
           "description": "Client Secret",
           "shortDescription": "Client Secret",
           "required": true,
           "hidden": false
         },
         {
           "name": "oauth.access.token.uri",
           "default": "https://github.com/login/oauth/access_token",
           "required": false,
           "hidden": true
         },
         {
           "name": "oauth.scope",
           "displayName": "Scope",
           "description": "The scope of the access request",
           "shortDescription": "scope",
           "required": false,
           "hidden": false
         },
         {
           "name": "oauth.auth.code.uri",
           "default": "https://github.com/login/oauth/authorize",
           "required": false,
           "hidden": true
         },
         {
           "name": "authRequest",
           "default": "${(.securityProperties.\"oauth.auth.code.uri\") + \"?response_type=code&client_id=\" + (.securityProperties.\"oauth.client.id\") + \"&redirect_uri=${redirect_uri}&scope=\" + (.securityProperties.\"oauth.scope\")}",
           "required": true,
           "hidden": true
         },
         {
           "name": "accessTokenRequest",
           "description": "Access Token Request that should be used to fetch the access token",
           "shortDescription": "Example: -X <Method> -H <headers> -d <string-data> <access-token-uri>?<query params>",
           "default": "${\"-X POST -H \" + \"\\\"Accept: application/json\\\" -H \\\"Content-Type: application/x-www-form-urlencoded\\\" -d \\\"false\\\" \\\"\" +  (.securityProperties.\"oauth.access.token.uri\") + \"?code=${auth_code}&client_id=\" + (.securityProperties.\"oauth.client.id\") + \"&redirect_uri=${redirect_uri}&client_secret=\" + (.securityProperties.\"oauth.client.secret\") + \"&grant_type=authorization_code\\\"\"}",
           "required": true,
           "hidden": true
         },
         {
           "name": "refreshTokenRequest",
           "description": "Refresh Token Request that should be used to fetch the access token",
           "shortDescription": "Example: -X <Method> -H <headers> -d <string-data> <refresh-token-uri>?<query params>",
           "required": true,
           "default": "${\"-X POST -H \" + \"\\\"Accept: application/json\\\" -H \\\"Content-Type: application/x-www-form-urlencoded\\\" -d \\\"false\\\" \\\"\" +  (.securityProperties.\"oauth.access.token.uri\") + \"?refresh_token=${refresh_token}&client_id=\" + (.securityProperties.\"oauth.client.id\") + \"&redirect_uri=${redirect_uri}&client_secret=\" + (.securityProperties.\"oauth.client.secret\") + \"&grant_type=refresh_token\\\"\"}",
           "hidden": true
         },
         {
           "name": "$auth_code",
           "description": "Regex that identifies the auth code",
           "shortDescription": "Auth Code Regex",
           "required": false,
           "default": "${auth_code}",
           "hidden": true
         },
         {
           "name": "$access_token",
           "description": "Regex that identifies the access token",
           "shortDescription": "Access Token Regex",
           "required": false,
           "default": "access.[tT]oken",
           "hidden": true
         },
         {
           "name": "$refresh_token",
           "description": "Regex that identifies the refresh token",
           "shortDescription": "Default: refresh.[tT]oken",
           "required": false,
           "default": "refresh.[tT]oken",
           "hidden": true
         },
         {
           "name": "$expiry",
           "description": "Numeric value (in seconds)that specifies the expiry interval or a regex that identifies when the access token expires.",
           "shortDescription": "Default: expires_in",
           "required": false,
           "default": "expires.*",
           "hidden": true
         },
         {
           "name": "$token_type",
           "description": "Regex that identifies the access token type.",
           "shortDescription": "Default: token.?[tT]ype",
           "required": false,
           "default": "token.?[tT]ype",
           "hidden": true
         },
         {
           "name": "accessTokenUsage",
           "description": "Access token usage. A curl type syntax to illustrate how access token should be passed to access a protected resource.",
           "shortDescription": "Default: -H Authorization ${token_type} ${access_token}",
           "required": false,
           "default": "-H Authorization: Bearer ${access_token}",
           "hidden": true
         }
       ]
     },
     "policyInbound": {
       "type": "managed",
       "policy": "HMAC_SIGNATURE_VALIDATION",
       "securityProperties": [
         {
           "name": "signature",
           "hidden": true,
           "required": true,
           "default": "${connectivity::hexDecode(.request.headers.\"x-hub-signature-256\" | split(\"sha256=\")[1])}"
         },
         {
           "name": "signatureString",
           "displayName": "Request Signature Location",
           "hidden": true,
           "required": true,
           "default": "${.request.body}"
         },
         {
           "name": "signatureAlgorithm",
           "displayName": "Request Signature Algorithm",
           "hidden": true,
           "required": true,
           "default": "HMACSHA256"
         },
         {
           "name": "secret",
           "displayName": "Shared Secret",
           "hidden": false,
           "required": true
         },
         {
           "name": "timestampValidator",
           "displayName": "Timestamp Validation",
           "hidden": true,
           "required": false,
           "default": ""
         }
       ]
     }
   }

Sample code that defines a security policy that the Google Cloud Publication Subscription uses to validate messages. This security policy uses JWT_VALIDATION for policyInbound, and OAUTH_AUTHORIZATION_CODE_CREDENTIALS for policyOutbound.

"securityPolicies": [
  {
    "type": "managed",
    "policy": "OAUTH_AUTHORIZATION_CODE_CREDENTIALS",
    "description": "This policy is used by OIC for invoking GCP Pub/Sub APIs",
    "displayName": "Google Authentication/Authorization Policy",
    "scope": "ACTION",
    "securityProperties": [
      {
        "name": "oauth.client.id",
        "displayName": "Google Client ID",
        "description": "Google Client ID",
        "shortDescription": "Example: 6-jdek24mqqhdleori19r.apps.googleusercontent.com",
        "required": true,
        "hidden": false
      },
      {
        "name": "oauth.client.secret",
        "displayName": "Google Client Secret",
        "description": "Google Client Secret",
        "shortDescription": "Example: GOCDPX-gBQdjksUPXWer4",
        "required": true,
        "hidden": false
      },
      {
        "name": "oauth.access.token.uri",
        "default": "https://oauth2.googleapis.com/token",
        "required": false,
        "hidden": true
      },
      {
        "name": "oauth.scope",
        "default": "https://www.googleapis.com/auth/pubsub",
        "required": false,
        "hidden": true
      },
      {
        "name": "oauth.auth.code.uri",
        "default": "https://accounts.google.com/o/oauth2/auth",
        "required": false,
        "hidden": true
      },
      {
        "name": "clientAuthentication",
        "default": "client_credentials_in_body",
        "required": false,
        "hidden": true
      }
    ],
    "authflow": "flow:extended-oauth"
  },
  {
    "type": "composite",
    "description": "This policy is used by OIC for validating incoming requests as well as for invoking GCP Pub/Sub APIs",
    "displayName": "GCP Pub/Sub security policy",
    "scope": "TRIGGER",
    "policyOutbound": {
      "type": "managed",
      "policy": "OAUTH_AUTHORIZATION_CODE_CREDENTIALS",
      "securityProperties": [
        {
          "name": "oauth.client.id",
          "displayName": "Google Client ID",
          "description": "Google Client ID",
          "shortDescription": "Example: 35532456156-jdek24mdmlqutog3gnc3rfqqhdleori19r.apps.googleusercontent.com",
          "required": true,
          "hidden": false
        },
        {
          "name": "oauth.client.secret",
          "displayName": "Google Client Secret",
          "description": "Google Client Secret",
          "shortDescription": "Example: GOCDPX-gBQdjnPG4Hdi940zJCuksUPXWer4",
          "required": true,
          "hidden": false
        },
        {
          "name": "oauth.access.token.uri",
          "default": "https://oauth2.googleapis.com/token",
          "required": false,
          "hidden": true
        },
        {
          "name": "oauth.scope",
          "default": "https://www.googleapis.com/auth/pubsub",
          "required": false,
          "hidden": true
        },
        {
          "name": "oauth.auth.code.uri",
          "default": "https://accounts.google.com/o/oauth2/auth",
          "required": false,
          "hidden": true
        },
        {
          "name": "clientAuthentication",
          "default": "client_credentials_in_body",
          "required": false,
          "hidden": true
        }
      ]
    },
    "policyInbound": {
        "type": "managed",
        "policy": "JWT_VALIDATION",
        "securityProperties": [
            {
                "name": "subjectClaim",
                "displayName": "Subject claim Override",
                "hidden": true,
                "required": false,
                "default": ""
            },
            {
                "name": "jwtToken",
                "displayName": "JWT Token",
                "hidden": true,
                "required": true,
                "default": "${.request.headers.authorization|split(\" \")|.[1]}"
            },
            {
                "name": "signatureKey",
                "displayName": "JWK URL",
                "hidden": true,
                "required": true,
                "default": "https://www.googleapis.com/oauth2/v3/certs"
            },
            {
                "name": "customClaimsValidation",
                "displayName": "Custom Claims Validation",
                "hidden": true,
                "required": false,
                "default": ""
            }
        ]
    }

Sample code that defines a security policy where the policyInbound uses Oauth managed policy OAUTH_INBOUND. The webhook message contains a bearer token in the header that allows Oracle Integration to authenticate the inbound message. The webhook message authenticates with Oracle Integration similar to how applications invoke APIs of Oracle Integration. However, this policy requires a manual setup to initialize the external application with proper credentials, and for the external application to obtain the authentication artifacts.

{
    "type": "composite",
    "description": "This policy is used by Oracle Integration to validate incoming requests for Zuora APIs",
    "displayName": "Zuora Composite Security Policy",
    "scope": "TRIGGER",
    "policyOutbound": {
        "type": "managed",
        "policy": "OAUTH2.0_CLIENT_CREDENTIALS",
        "securityProperties": [
            {
                "name": "oauth.client.id",
                "displayName": "Client Id",
                "description": "Used to identify the client(the software requesting an access token) that is making the request. The value passed in this parameter must exactly match the value shown in your API console project.",
                "shortDescription": "Used to identify the client.",
                "required": true,
                "hidden": false
            },
            {
                "name": "oauth.client.secret",
                "displayName": "Client Secret",
                "description": "Used to authorize the client(the software requesting an access token) that is making the request.  The value passed in this parameter must exactly match the value shown in your API console project.",
                "shortDescription": "<unique random string matches your API console project>",
                "required": true,
                "hidden": false
            },
            {
                "name": "oauth.access.token.uri",
                "description": "A request should be sent to this URI for obtaining an access token.",
                "default": "${\"https:/\"+\"/\"+.connectionProperties.invokeHostName + \"/oauth/token\"}",
                "required": true,
                "hidden": true
            },
            {
                "name": "oauth.scope",
                "description": "Permissions your application is requesting on behalf of the user.",
                "shortDescription": "For example: read,write.",
                "default": "",
                "required": false,
                "hidden": true
            },
            {
                "name": "accessTokenRequest",
                "description": "Access Token Request that should be used to fetch the access token",
                "shortDescription": "Example: -X <Method> -H <headers> -d <string-data> <access-token-uri>?<query params>",
                "default": "${\"-X POST -H \" + \"\\\"Content-Type: application/x-www-form-urlencoded\\\" -d \\\"client_id=\" + (.securityProperties.\"oauth.client.id\") + \"&client_secret=\" + ((.securityProperties.\"oauth.client.secret\"|=@uri).securityProperties.\"oauth.client.secret\") + \"&grant_type=client_credentials\\\" \" + \"\\\"https://\" + .connectionProperties.invokeHostName + \"/oauth/token\\\"\"}",
                "required": true,
                "hidden": true
            },
            {
                "name": "refreshTokenRequest",
                "description": "Refresh Token Request that should be used to fetch the access token",
                "shortDescription": "Example: -X <Method> -H <headers> -d <string-data> <refresh-token-uri>?<query params>",
                "required": false,
                "default": "",
                "hidden": true
            },
            {
                "name": "$access_token",
                "description": "Regex that identifies the access token",
                "shortDescription": "Access Token Regex",
                "required": false,
                "default": "access.[tT]oken",
                "hidden": true
            },
            {
                "name": "$refresh_token",
                "description": "Regex that identifies the refresh token",
                "shortDescription": "Default: refresh.[tT]oken",
                "required": false,
                "default": "refresh.[tT]oken",
                "hidden": true
            },
            {
                "name": "$expiry",
                "description": "Numeric value (in seconds)that specifies the expiry interval or a regex that identifies when the access token expires.",
                "shortDescription": "Default: expires_in",
                "required": false,
                "default": "expires.*",
                "hidden": true
            },
            {
                "name": "$token_type",
                "description": "Regex that identifies the access token type.",
                "shortDescription": "Default: token.?[tT]ype",
                "required": false,
                "default": "token.?[tT]ype",
                "hidden": true
            },
            {
                "name": "accessTokenUsage",
                "description": "Access token usage. A curl type syntax to illustrate how access token should be passed to access a protected resource.",
                "shortDescription": "Default: -H Authorization ${token_type} ${access_token}",
                "required": false,
                "default": "-H Authorization: Bearer ${access_token}",
                "hidden": true
            }
        ]
    },
    "policyInbound": {
        "type": "managed",
        "policy": "OAUTH_INBOUND"
    }
}