Oracle Cloud Infrastructure Documentation

Adding Authentication and Authorization to API Deployments

You can control access to APIs you deploy to API gateways based on the caller (a person or system) sending a request, and define what it is that they are allowed to do. For the APIs you deploy, you'll typically provide:

  • Authentication functionality to determine caller identity. Is the caller really who they claim to be?
  • Authorization functionality to determine appropriate access for a caller, and grant the necessary permissions. What is the caller allowed to do?

You can add authentication and authorization functionality to API gateways to support:

  • HTTP Basic Authentication
  • API Key Authentication
  • OAuth Authentication and Authorization
  • MTLS
  • OCI Identity and Access Management
  • Oracle Identity Cloud Service (IDCS) Authentication

To provide authentication and authorization functionality, you write an 'authorizer function' that:

  • processes request attributes to verify the identity of a caller with an authentication provider
  • determines the operations that the caller is allowed to perform
  • returns the operations the caller is allowed to perform as a list of 'access scopes' (an 'access scope' is an arbitrary string used to determine access)

You then deploy the authorizer function to Oracle Functions. See Creating an Authorizer Function.

Having deployed the authorizer function, you enable authentication and authorization for an API deployment by including two different kinds of request policy in the API deployment specification:

  • an authentication request policy for the entire API deployment that specifies:
    • the OCID of the authorizer function that you deployed to Oracle Functions that will perform authentication and authorization
    • the request attributes to pass to the authorizer function
    • whether unauthenticated callers can access routes in the API deployment
  • an authorization request policy for each route that specifies the operations a caller is allowed to perform, based on the caller's access scopes as returned by the authorizer function

You can add authentication and authorization request policies to an API deployment specification by:

  • using the Console
  • editing a JSON file
Tip

To help troubleshoot issues with the authorizer function, consider adding an execution log logging policy to the API deployment specification, with its log level set to Info (see Adding Logging Policies to API Deployment Specifications).

To see details in the log files related to authentication and authorization, search for customAuth.

Prerequisites

Before you can enable authentication and authorization for API deployments:

  • An auth provider (for example, Auth0, Oracle Identity Cloud Service (IDCS)) must have already been set up, containing access scopes for users allowed to access the API deployment. See the auth provider documentation for more information (for example, the Auth0 documentation, the Oracle Identity Cloud Service (IDCS) documentation).
  • An authorizer function must have been deployed to Oracle Functions already, and an appropriate policy must give a dynamic group of API gateways access to Oracle Functions. For more information, see Creating an Authorizer Function.

If you use the Console to include an authentication request policy (rather than by editing a JSON file), you select the authorizer function and the application that contains it from a list.

Note that to use the Console (rather than a JSON file) to define an authentication request policy and specify an authorizer function, your user account must belong to a group that has been given access to the authorizer function by an IAM policy (see Create a Policy to Give API Gateway Users Access to Functions).

Creating an Authorizer Function

To create an authorizer function:

  1. Write code to implement authentication and authorization:

    1. Write code in the authorizer function that accepts the following JSON input from API Gateway:

      {
        "type": "TOKEN",
        "token": "<token-value>"
      }
    2. where:

      • "type": "TOKEN" indicates that the value being passed to the authorizer function is an auth token.
      • "token": "<token-value>" is the auth token being passed to the authorizer function.

      For example:

      {
        "type": "TOKEN",
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1nHyDtTwR3SEJ3z489..."
      }
    3. Write code in the authorizer function that returns the following JSON to API Gateway as an HTTP 200 response when the access token has been successfully verified:

      {
        "active": true,
        "principal": "<user-principal>",
        "scope": ["<scopes>"],
        "clientId": "<client-id>",
        "expiresAt": "<date-time>",
        "context": {
          "<key>": "<value>", ... 
        }
      }

      where:

      • "active": true indicates the access token originally passed to the authorizer function has been successfully verified.
      • "principal": "<user-principal>" is the user or application obtained by the authorizer function from the authentication provider.
      • "scope": ["<scopes>"] is a comma-delimited list of strings that are the access scopes obtained by the authorizer function from the authentication provider.

      • "clientId": "<client-id>" is optionally the requestor's host (for example, the hostname or the client IP). Returning a clientId is not required.
      • "expiresAt": "<date-time>" is a date-time string in ISO-8601 format indicating when the access token originally passed to the authorizer function will expire. This value is used when determining how long to cache results after calling the authorizer function.

      • "context": {"<key>": "<value>", ... } is an optional comma-delimited list of key-value pairs in JSON format to return to API Gateway. The authorizer function can return any key-value pair for use by the API deployment (for example, the username or email address of the caller).

      For example:

      {
        "active": true,
        "principal": "https://example.com/users/jdoe",
        "scope": ["list:hello", "read:hello", "create:hello", "update:hello", "delete:hello", "someScope"],
        "clientId": "host123",
        "expiresAt": "2019-05-30T10:15:30+01:00",
        "context": {
          "email": "john.doe@example.com"
        }
      }
    4. Write code that returns the following JSON to API Gateway as an HTTP 5xx response if token verification is unsuccessful, or in the event of an error in the authorizer function or in Oracle Functions:

      {
        "active": false,
        "expiresAt": "<date-time>",
        "context": {
          "<key>": "<value>", ... "
        },
        "wwwAuthenticate": "<directive>"
      }

      where:

      • "active": false indicates the access token originally passed to the authorizer function has not been successfully verified.
      • "expiresAt": "<date-time>" is a date-time string in ISO-8601 format indicating when the access token originally passed to the authorizer function will expire.

      • "context": {"<key>": "<value>", ... } is an optional comma-delimited list of key-value pairs in JSON format to return to API Gateway. The authorizer function can return any key-value pair (for example, the username or email address of the caller). Returning context key-value pairs is not required.

      • "wwwAuthenticate": "<directive>" is the value of the WWW-Authenticate header to be returned by the authorizer function if verification fails, indicating the type of authentication that is required (such as Basic or Bearer). API Gateway returns the WWW-Authenticate header in the response to the caller, along with a 401 status code. For example, "wwwAuthenticate": "Bearer realm=\"example.com\"". For more information, see RFC 2617 HTTP Authentication: Basic and Digest Access Authentication.

      For example:

      {
        "active": false,
        "expiresAt": "2019-05-30T10:15:30+01:00",
        "context": {
          "email": "james.doe@example.com"
          },
        "wwwAuthenticate": "Bearer realm=\"example.com\""
      }
  2. Build a Docker image from the code, push the Docker image to a Docker registry, and create a new function in Oracle Functions based on the image. You can do this in different ways:

    • You can use the Fn Project CLI command fn deploy to build a new Docker image, push the image to the Docker registry, and create a new function in Oracle Functions based on the image. See Creating and Deploying Functions.
    • You can use Docker commands to build the image and push it to the Docker registry, and then use the Fn Project CLI command fn create function (or the CreateFunction API operation) to create a new function in Oracle Functions based on the image. See Creating Functions from Existing Docker Images.
  3. Make a note of the OCID of the function you create in Oracle Functions. For example, ocid1.fnfunc.oc1.phx.aaaaaaaaac2______kg6fq
  4. If one doesn't exist already, create an Oracle Cloud Infrastructure policy and specify a policy statement to give a dynamic group of API gateways access to function-related resources. The policy enables API deployments on those API gateways to invoke the authorizer function. For more information, see Create a Policy to Give a Dynamic Group of API Gateways Access to Functions

Using the Console to Add Authentication and Authorization Request Policies

To add authentication and authorization request policies to an API deployment specification using the Console:

  1. Create or update an API deployment using the Console, select the From Scratch option, and enter details on the Basic Information page.

    For more information, see Deploying an API on an API Gateway by Creating an API Deployment and Updating API Gateways and API Deployments.

  2. In the API Request Policies section of the Basic Information page, click the Add button beside Authentication and specify:

    • Application in <compartment-name>: The name of the application in Oracle Functions that contains the authorizer function. You can select an application from a different compartment.
    • Function Name: The name of the authorizer function in Oracle Functions.
    • Authentication Token: Whether the access token is contained in a request header or a query parameter.

    • Authentication Token Value: Depending on whether the access token is contained in a request header or a query parameter, specify:

      • Header Name: If the access token is contained in a request header, enter the name of the header.
      • Parameter Name: If the access token is contained in a query parameter, enter the name of the query parameter.
    • Enable Anonymous Access: Whether unauthenticated (that is, anonymous) callers can access routes in the API deployment. By default, this option is not selected. If you never want anonymous callers to be able to access routes, don't select this option. Note that if you do select this option, you also have to explicitly specify every route to which anonymous access is allowed by selecting Anonymous as the Authorization Type in each route's authorization policy.

  3. Click Save Changes, and then click Next to enter details for individual routes in the API deployment on the Routes page. To specify an authorization policy that applies to an individual route, click Show Route Request Policies, click the Add button beside Authorization, and specify:

    • Authorization Type: How to grant access to the route. Specify:

      • Any: Only grant access to callers that have been successfully authenticated, provided the authorizer function has also returned one of the access scopes you specify in the Allowed Scope field. In this case, the authentication policy's Enable Anonymous Access option has no effect.
      • Anonymous: Grant access to all callers, even if they have not been successfully authenticated by the authorizer function. In this case, you must have selected the authentication policy's Enable Anonymous Access option.
      • Authentication only: Only grant access to callers that have been successfully authenticated by the authorizer function. In this case, the authentication policy's Enable Anonymous Access option has no effect.
    • Allowed Scope: If you selected Any as the Authorization Type, enter a comma-delimited list of one or more strings that correspond to access scopes returned by the authorizer function. Access will only be granted to callers that have been successfully authenticated if the authorizer function returns one of the access scopes you specify. For example, read:hello
    Note

    If you don't include an authorization policy for a particular route, access is granted as if such a policy does exist and Authorization Type is set to Authentication only. In other words, regardless of the setting of the authentication policy's Enable Anonymous Access option:

    • only authenticated callers can access the route
    • all authenticated callers can access the route regardless of access scopes returned by the authorizer function
    • anonymous callers cannot access the route
  4. Click Save Changes, and then click Next to review the details you entered for the API deployment.
  5. Click Create or Save Changes to create or update the API deployment.
  6. (Optional) Confirm the API has been deployed successfully by calling it (see Calling an API Deployed on an API Gateway).

Editing a JSON File to Add Authentication and Authorization Request Policies

To add authentication and authorization request policies to an API deployment specification in a JSON file:

  1. Using your preferred JSON editor, edit the existing API deployment specification to which you want to add authentication and authorization functionality, or create a new API deployment specification (see Creating an API Deployment Specification).

    At a minimum, the API deployment specification will include a routes section containing:

    • A path. For example, /hello
    • One or more methods. For example, GET
    • A definition of a back end. For example, a URL, or the OCID of a function in Oracle Functions.

    For example, the following basic API deployment specification defines a simple Hello World serverless function in Oracle Functions as a single back end:

    {
      "routes": [
        {
          "path": "/hello",
          "methods": ["GET"],
          "backend": {
            "type": "ORACLE_FUNCTIONS_BACKEND",
            "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaab______xmq"
          }
        }
      ]
    }
  2. Add an authentication request policy that applies to all routes in the API deployment specification:

    1. Insert a requestPolicies section before the routes section, if one doesn't exist already. For example:

      {
        "requestPolicies": {},
        "routes": [
          {
            "path": "/hello",
            "methods": ["GET"],
            "backend": {
               "type": "ORACLE_FUNCTIONS_BACKEND",
               "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaab______xmq"
            }
          }
        ]
      }
    2. Add the following authentication policy to the new requestPolicies section.

      {
        "requestPolicies": {
          "authentication": {
            "type": "<type-value>",
            "isAnonymousAccessAllowed": <true|false>,
            "functionId": "<function-ocid>",
            <"tokenHeader"|"tokenQueryParam">: <"<token-header-name>"|"<token-query-param-name>">
      } }, "routes": [ { "path": "/hello", "methods": ["GET"], "backend": { "type": "ORACLE_FUNCTIONS_BACKEND", "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaab______xmq" } } ] }

      where:

      • <type-value> is the authentication type. The only valid value is CUSTOM_AUTHENTICATION.

      • "isAnonymousAccessAllowed": <true|false> optionally indicates whether unauthenticated (that is, anonymous) callers can access routes in the API deployment specification. If you never want anonymous callers to be able to access routes, set this property to false. If you don't include this property in the authentication policy, the default of false is used. Note that if you do include this property and set it to true, you also have to explicitly specify every route to which anonymous access is allowed by setting the type property to "ANONYMOUS" in each route's authorization policy.
      • <function-ocid> is the OCID of the authorizer function deployed to Oracle Functions.

      • <"tokenHeader"|"tokenQueryParam">: <"<token-header-name>"|"<token-query-param-name>"> indicates whether it is a request header that contains the access token (and if so, the name of the header), or a query parameter that contains the access token (and if so, the name of the query parameter). Note that you can specify either "tokenHeader": "<token-header-name>" or "tokenQueryParam": "<token-query-param-name>">, but not both.

      For example, the following authentication policy specifies an OCI function that will validate the access token in the Authorization request header:

      {
        "requestPolicies": {
          "authentication": {
            "type": "CUSTOM_AUTHENTICATION",
            "isAnonymousAccessAllowed": false,
            "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaac2______kg6fq",
            "tokenHeader": "Authorization"
      } }, "routes": [ { "path": "/hello", "methods": ["GET"], "backend": { "type": "ORACLE_FUNCTIONS_BACKEND", "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaab______xmq" } } ] }
  3. Add an authorization request policy for each route in the API deployment specification:

    1. Insert a requestPolicies section after the first route's backend section, if one doesn't exist already. For example:

      {
        "requestPolicies": {
          "authentication": {
            "type": "CUSTOM_AUTHENTICATION",
            "isAnonymousAccessAllowed": false,
            "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaac2______kg6fq",
            "tokenHeader": "Authorization"
      } }, "routes": [ { "path": "/hello", "methods": ["GET"], "backend": { "type": "ORACLE_FUNCTIONS_BACKEND", "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaab______xmq" }, "requestPolicies": {} } ] }
    2. Add the following authorization policy to the requestPolicies section:

      {
        "requestPolicies": {
          "authentication": {
            "type": "CUSTOM_AUTHENTICATION",
            "isAnonymousAccessAllowed": false,
            "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaac2______kg6fq",
            "tokenHeader": "Authorization"
      } }, "routes": [ { "path": "/hello", "methods": ["GET"], "backend": { "type": "ORACLE_FUNCTIONS_BACKEND", "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaab______xmq" }, "requestPolicies": { "authorization": { "type": <"AUTHENTICATION_ONLY"|"ANY_OF"|"ANONYMOUS">, "allowedScope": [ "<scope>" ] } } } ] }

      where:

      • "type": <"AUTHENTICATION_ONLY"|"ANY_OF"|"ANONYMOUS"> indicates how to grant access to the route:

        • "AUTHENTICATION_ONLY": Only grant access to callers that have been successfully authenticated by the authorizer function. In this case, the "isAnonymousAccessAllowed" property in the API deployment specification's authentication policy has no effect.
        • "ANY_OF": Only grant access to callers that have been successfully authenticated, provided the authorizer function has also returned one of the access scopes you specify in the allowedScope property. In this case, the "isAnonymousAccessAllowed" property in the API deployment specification's authentication policy has no effect.
        • "ANONYMOUS": Grant access to all callers, even if they have not been successfully authenticated by the authorizer function. In this case, you must explicitly set the "isAnonymousAccessAllowed" property to true in the API deployment specification's authentication policy.
      • "allowedScope": [ "<scope>" ] is a comma-delimited list of one or more strings that correspond to access scopes returned by the authorizer function. In this case, you must set the type property to "ANY_OF" (the "allowedScope" property is ignored if the type property is set to "AUTHENTICATION_ONLY" or "ANONYMOUS"). Also note that if you specify more than one scope, access to the route is granted if any of the scopes you specify is returned by the authorizer function.

      For example, the following request policy defines a /hello route that only allows authenticated callers with the read:hello scope to access it:

      {
        "requestPolicies": {
          "authentication": {
            "type": "CUSTOM_AUTHENTICATION",
            "isAnonymousAccessAllowed": false,
            "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaac2______kg6fq",
            "tokenHeader": "Authorization"
      } }, "routes": [ { "path": "/hello", "methods": ["GET"], "backend": { "type": "ORACLE_FUNCTIONS_BACKEND", "functionId": "ocid1.fnfunc.oc1.phx.aaaaaaaaab______xmq" }, "requestPolicies": { "authorization": { "type": "ANY_OF", "allowedScope": [ "read:hello" ] } } } ] }
    3. Add an authorization request policy for all remaining routes in the API deployment specification.
    Note

    If you don't include an authorization policy for a particular route, access is granted as if such a policy does exist and the type property is set to "AUTHENTICATION_ONLY". In other words, regardless of the setting of the isAnonymousAccessAllowed property in the API deployment specification's authentication policy:

    • only authenticated callers can access the route
    • all authenticated callers can access the route regardless of access scopes returned by the authorizer function
    • anonymous callers cannot access the route
  4. Save the JSON file containing the API deployment specification.
  5. Use the API deployment specification when you create or update an API deployment in the following ways:

    • by specifying the JSON file in the Console when you select the Upload an existing API option
    • by specifying the JSON file in a request to the API Gateway REST API

    For more information, see Deploying an API on an API Gateway by Creating an API Deployment and Updating API Gateways and API Deployments.

  6. (Optional) Confirm the API has been deployed successfully by calling it (see Calling an API Deployed on an API Gateway).