9.2 Update Application Configuration with IAM Details

To enforce data access control using Oracle Deep Data Security (Deep Sec), the application must pass an end-user security context payload to the database with every SQL operation through an Oracle client driver.

This section describes how to configure an application to build and propagate this payload for all deployment scenarios where end users connect to the database through the application, regardless of whether those users are IAM-managed or maintained in the application's own user store.

Note:

You must configure the database server for TLS before configuring the application. The client-side trust store requires the server certificate to establish trust. For TLS configuration, see Configuring Transport Layer Security Encryption in Oracle AI Database Security Guide.

9.2.1 Understand Authentication Flow and Prerequisites

Review the authentication concepts and gather the necessary credentials before modifying your application configuration.

Authentication flow

For the application to successfully construct and propagate the EndUserSecurityContext payload to the database, you must register your application as a confidential client in an IAM system. During runtime, the application must typically acquire and manage two distinct tokens to build the security context payload:
  • End-user token: An OAuth 2.0 access token generated when the end user logs in through the browser. This token establishes the user's identity and role claims (app roles in Microsoft Entra ID or group memberships in OCI IAM).
  • Database-access token: A token the application obtains to authorize itself to access the database resource. This is acquired through the on-behalf-of (OBO) flow or the client credentials flow.

The application passes these tokens as part of the EndUserSecurityContext payload on the database connection; the database-access token authorizes the connection, and the end-user token supplies the user identity and role claims used to enforce data security.

Required credentials

Before proceeding, ensure you have gathered the following values from your application's IAM registration:

  • Client ID: The unique identifier assigned to your application.
  • Client secret: The secret key generated for the application.
  • Tenant ID (Entra ID) / Domain URL (OCI IAM): The identifier for your IAM instance.
  • Database Scope: The scope required to access the database resource (for example, [DB_APP_ID_URI]/sessions:scope:connect in Entra ID or [primary_audience]DB_ACCESS_SCOPE in OCI IAM).

9.2.2 Configure Java Applications

The Oracle JDBC driver supports two approaches for propagating the end-user security context payload from your Java application to the database: API extension methods and a configuration-driven Service Provider Interface (SPI). API calls take precedence over provider-based configurations.

9.2.2.1 Use the API Extension Methods

If you want to supply the end-user security context payload directly in your application code, use the API extension methods.

This approach involves interacting directly with the oracle.jdbc.OracleConnection interface within your Java code to manually set and clear the security context payload.
  1. Build the security context payload.
    Use the createWithToken or createWithUsername method of oracle.jdbc.EndUserSecurityContext to create the security context object, with the end-user identity and optional data roles. For example, you can create a security context payload named myEndUserSecurityContext.
  2. Set the security context payload.
    Before executing SQL statements, attach the security context payload to the database connection. If your connection object is a java.sql.Connection (as is typical when obtained from a DataSource or connection pool), you must first cast or unwrap it to OracleConnection and then attach the payload.
    OracleConnection oracleConnection =
        connection instanceof OracleConnection
            ? (OracleConnection) connection
            : connection.unwrap(OracleConnection.class);
    
    oracleConnection.setEndUserSecurityContext(myEndUserSecurityContext);
  3. Perform database operations.
  4. Clear the security context payload.
    Always clear the security context payload before returning the connection to a pool to prevent data leakage. Use a finally block to ensure the security context payload is cleared even if an exception occurs during database operations.
    OracleConnection oracleConnection =
        connection instanceof OracleConnection
            ? (OracleConnection) connection
            : connection.unwrap(OracleConnection.class);
    
    oracleConnection.setEndUserSecurityContext(myEndUserSecurityContext);
    try {
        executeSqlAsEndUser(connection);
    }
    finally {
        oracleConnection.clearEndUserSecurityContext();
    }

9.2.2.2 Use the Service Provider Interface

The Service Provider Interface (SPI) approach enables your Java application to supply the end-user security context payload without requiring modifications to your core application code.

Oracle JDBC defines a Java interface named oracle.jdbc.spi.EndUserSecurityContextProvider. This interface is designed to work as a standard service provider interface that is dynamically loaded by Java's java.util.ServiceLoader class. This design allows you to implement the interface by integrating with different Java security technologies to provide a security context payload to Oracle JDBC.

By simply installing and configuring a provider implementation, you can enable your application to automatically propagate the security context payload to the database before each SQL operation. The SPI approach also allows you to integrate with different IAM-specific providers without changing how your application interacts with the JDBC driver.

To supply the security context payload using the SPI approach, perform the following tasks.

  1. Install the driver.
    Ensure the core Oracle JDBC driver is included in your project dependencies by adding the following block to your pom.xml file.
    <!-- Oracle JDBC driver -->
    		<dependency>
    			<groupId>com.oracle.database.jdbc</groupId>
    			<artifactId>ojdbc11</artifactId>
    			<version>23.26.2.0.0</version>
    		</dependency>
    
  2. Install a provider implementation.

    Add an implementation of oracle.jdbc.spi.EndUserSecurityContextProvider to your application's class path. You can use a pre-built provider published to a repository such as Maven Central, or implement the interface yourself to integrate with your security framework. For a pre-built provider that integrates with Spring Security, see Example: Use the JDBC Spring Boot Provider (The SPI Approach).

  3. Configure the data source to use the provider.
    Configure the application's data-source connection properties to use the installed provider. The specific properties depend on the provider implementation. See your provider's documentation for the required configuration.

9.2.2.3 Example: Use the JDBC Spring Boot Provider (The SPI Approach)

The JDBC Spring Boot provider is an out-of-the-box (OOTB) security context payload provider for the Oracle JDBC driver. It automates the propagation of the end-user security context payload from Spring Security (OAuth 2.0) to the database through the JDBC connection. In most cases, no application code changes are necessary.

For Spring Boot applications, both the IAM registration and the data source driver configuration are handled in the application.properties file. You must link the data source to the OAuth registration using the registrationId.
  1. Install the provider.

    Add the Oracle JDBC Spring provider dependency to your pom.xml. This artifact is part of the Oracle JDBC Extensions project hosted on GitHub: https://github.com/oracle/ojdbc-extensions.

    <dependency>
        <groupId>com.oracle.database.jdbc</groupId>
        <artifactId>ojdbc-provider-spring</artifactId>
        <version>${ojdbc.provider.version}</version>
    </dependency>
  2. Add IAM credentials and endpoints.
    In your application.properties file, add the following properties to configure the application with your application's IAM registration details.

    Note:

    The registration name used below (for example, hrapp) acts as the identifier for the next step.
    
    # Spring OAuth2 Configuration
    spring.security.oauth2.client.registration.hrapp.client-name=hrapp
    spring.security.oauth2.client.registration.hrapp.client-id=<HRAPP_CLIENT_ID>
    spring.security.oauth2.client.registration.hrapp.client-secret=<HRAPP_CLIENT_SECRET>
    
    # Endpoint Configuration
    spring.security.oauth2.client.provider.hrapp.token-uri=<HRAPP_TOKEN_URI>
    spring.security.oauth2.client.provider.hrapp.authorization-uri=<HRAPP_AUTH_URI>
    spring.security.oauth2.client.registration.hrapp.redirect-uri=<REDIRECT_URI>
    
    # Scope Configuration
    spring.security.oauth2.client.registration.hrapp.scope=<DB_APP_ID_URI>/.default
    
    # --- SELECT ONE GRANT TYPE BELOW ---
    
    # OPTION A: OBO Flow (User assertion)
    spring.security.oauth2.client.registration.hrapp.authorization-grant-type=urn:ietf:params:oauth:grant-type:jwt-bearer
    
    # OPTION B: Client Credentials Flow (Service assertion)
    # spring.security.oauth2.client.registration.hrapp.authorization-grant-type=client_credentials
    
  3. Enable the provider.

    Next, enable the provider in your connection pool's data-source configuration. The connection pool setup (data source URL, SSL wallet, pool sizing, and so on) is standard and is not covered here. Ensure the registrationId matches the name used in step 2 (hrapp). For the complete connection pool configuration details, see Configure Oracle Deep Data Security for a Sample Application.

    
    # Enable the Spring Security Context Provider
    spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.endUserSecurityContext=ojdbc-provider-spring-end-user-security-context
    
    # Link to the OAuth2 Registration ID configured in Step 2
    spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.endUserSecurityContext.registrationId=hrapp
  4. Set additional data roles and context attributes.

    Optionally, you can set additional data roles and end-user context attributes in the Spring security context. The provider attaches these to the end-user security context payload and propagates them with every database operation.

    # (Optional) Configure default Data Roles or Attributes to pass with every connection
    spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.endUserSecurityContext.dataRoles=COMPENSATION_ANALYST
    spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.endUserSecurityContext.endUserContextAttribute={"HR_APP_NAMESPACE":{"APP":"HR"}}
    
  5. Handle privilege elevation (Java code).

    For scenarios requiring temporary elevated permissions (for example, generating a compensation summary), use the @RunWithDataRoles annotation in your Java code. This adds specific data roles to the end-user security context payload for the duration of a method’s execution. After the method finishes, the user’s original security context is restored.

    1. Define a unique prefix for data roles through your application code (default is ORACLE_DATA_ROLE_).
    2. Annotate your service method.
      @RunWithDataRoles(
                  dataRoles = {"COMPENSATION_ANALYST"}
          )
          @Transactional(readOnly = true)
          public SalarySummaryDto getSalarySummary()

Note:

The OOTB JDBC Spring Boot provider may not support custom or non-standard authentication and authorization workflows. If the provider doesn’t meet your requirements, you can build a custom provider. See Advanced: Implement a Security Context Provider.

9.2.3 Configure Python Applications

The python-oracledb driver supports two approaches for propagating the end-user security context payload from your Python application to the database: API extension methods and a configuration-driven Service Provider Interface (SPI). API calls take precedence over provider-based configurations.

9.2.3.1 Use the API Extension Methods

If you want to supply the end-user security context payload directly in your application code, use the API extension methods.

The python-oracledb driver extends the standard Python Database API with the Connection methods to set and clear the end-user security context payload. Currently, you can use this feature only in python-oracledb's Thin mode.

To supply the security context payload using the python-oracledb driver, perform the following tasks.

  1. Build the security context payload.
    In your application logic, use the create_end_user_security_context method to construct the security context object. You must provide both the end-user identity and the database-access token using the following supported values. Other parameters are optional, such as additional data roles to activate and end-user context attributes.
    • End-user identity (end_user_identity): Provide one of the following:
      • end_user_token: For users managed in IAM.
      • end_user_name and key: For users managed in the application's own user store. You can create the user name using the create_end_user_pk command, and key is the server-side string (also referred to as the security context lookup key).
    • Database-access token (database_access_token): Provide one of the following:
      • On-behalf-of (OBO) token: Obtained using the end-user token as an assertion.
      • Client credentials token: Obtained using the application's credentials.
    • Data roles (data_roles): Optional. Additional data roles that the application can enable for the end-user security context, beyond those mapped to application roles in IAM and those enabled by default for the application identity.
    • End-user context attributes (attributes): Optional. A dictionary of application-defined key-value pairs to include in the security context.
    import oracledb
    # ... authentication logic to retrieve tokens ...
    data_roles = ["hr_role"]
    attrs = {
        "emp_id": 3,
        "org_id": 5
    }
    ctx_attrs = {
            "hr.hcm_context": attrs
    }  
    # Build the end-user security context
    user_context = oracledb.create_end_user_security_context(
        end_user_identity = <user_token_or_local_end_user_name_and_key>,  
        database_access_token = <db_token_issued_by_an_IAM>,
        data_roles = data_roles,   #optional
        attributes = ctx_attrs     #optional
    )
    
  2. Obtain a connection.

    You can obtain a connection using oracledb.connect() (standalone connection) or pool.acquire() (connection pool).

    connection = oracledb.connect(user="db_user", password=userpwd,
        dsn="orclpdb", config_dir="/opt/oracle/config",
        wallet_location="location_of_pem_file", wallet_password=walletpw)
  3. Set and clear the security context payload on the connection.

    Add the security context payload to the connection before executing queries, and clear it afterward to return the connection to its original state. The security context payload is piggybacked on the next database operation.

    1. Set the security context payload.
      connection.set_end_user_security_context(user_context)
    2. Execute your SQL queries.
      with connection.cursor() as cursor:
          cursor.execute("select 1 from dual")
          row = cursor.fetchone()
          print(row)
      
    3. Clear the security context payload.

      As a best practice, clear the end-user security context payload explicitly before closing or releasing the connection. This ensures the next user does not inherit the previous user's privileges. If you do not call this method, the driver clears the security context payload only when the connection is closed with connection.close() or released back to the pool with pool.release().

      connection.clear_end_user_security_context()
      connection.close()
      

Note:

If no end-user identity is present, the plug-in does not attach the end-user security context payload, and the application receives a least-privileged connection or session (standard behavior) and no error is thrown.
For more information about the python-oracledb driver's support for Oracle Deep Data Security, see the python-oracledb documentation.

9.2.3.2 Use the Service Provider Interface

The Service Provider Interface (SPI) approach enables your Python application to supply the end-user security context payload with no code changes to your SQL or Object Relational Mapping (ORM) layer.

The python-oracledb driver's end_user_sec_provider plug-in acts as the SPI provider. By importing the plug-in and adding a configuration block to your application's database settings, you can enable your application to automatically propagate the security context payload on every database operation. The plug-in supports both the client_credentials and on_behalf_of (OBO) authentication flows for Microsoft Entra ID, and the client_credentials flow for OCI IAM.

To supply the security context payload using the SPI approach, perform the following tasks.

  1. Install the driver.
    Ensure the python-oracledb driver is installed. The plug-in ships with the driver and uses the driver’s Thin mode for security context payload propagation.
    python -m pip install oracledb
  2. Import the Oracle Deep Data Security plug-in.

    Import the plug-in to register its parameter hook with the driver.

    import oracledb.plugins.end_user_sec_provider
  3. Configure the Oracle Deep Data Security plug-in.
    Provide the identity provider settings in the end_user_sec_params configuration block. This block specifies the token provider type, authentication flow, IAM credentials, and optional data roles or context attributes. The location of this configuration block depends on your integration mode, which is detailed in the subsequent step.
    Example configuration:
    
    "end_user_sec_params": {
        "spi_type": "azure_tokens",
        "auth_flow": "client_credentials",
        "authority": "https://login.microsoftonline.com/<tenant_id>",
        "client_id": <client_id>,
        "client_credential": <secret>,
        "scopes": <scopes>,
        "data_roles": ["hr_role"],  #optional
        "attributes": {
            "hr.hcm_context": {"emp_id": 3, "org_id": 5}
        }
    }  #optional
    
    Parameter Description
    spi_type The name of the pre-supplied python-oracledb plug-in to use for database-access token acquisition. Valid values: azure_tokens (Microsoft Entra ID) or oci_tokens (OCI IAM).
    auth_flow The OAuth 2.0 grant type for acquiring the database-access token. Valid values: client_credentials or on_behalf_of. OCI IAM supports client_credentials only.
    authority The token endpoint authority URL. For example, for Entra ID, this is: https://login.microsoftonline.com/<tenant_id>.
    client_id The Application (Client) ID of the application registered in your IAM.
    client_credential The client secret for the application. Retrieve this value from an approved secret storage mechanism.
    scopes The scope required to access the database resource.
    data_roles (Optional) A list of additional data roles that the application can enable for the end-user security context.
    attributes (Optional) A dictionary of application-defined key-value pairs to include in the security context.
  4. Configure the integration mode.

    For every request or execution context that interacts with the database, your application must supply an end-user identity to the plug-in. The identity payload is either an end-user token (for IAM-managed users) or a user name and key (for users managed in the application's own user store). How you supply this identity and where you place the end_user_sec_params configuration block depends on your application framework.

    If both the end-user identity and end_user_sec_params are present, database calls execute under a delegated end-user security context. If either is missing, database calls proceed without a delegated end-user security context, and the application receives a standard connection with default privileges.

    The following subsections describe three common integration patterns: Django, Flask, and other Python web frameworks. Pick the one that matches your application and adapt the example accordingly.

    1. Django applications

      Register the plug-in middleware in your MIDDLEWARE setting, and place the end_user_sec_params block inside your database configuration under DATABASES['default']['OPTIONS']['extra_auth_params'] in the settings.py file. Your application is responsible for authenticating the end user and setting the end-user identity as a cookie named identity. The identity should be available on each request or execution. Its value is either the end-user token issued by your IAM system (for IAM-managed users) or a user name and key (for users managed in the application's own user store).

      The middleware reads this cookie from the request context (for example, request.COOKIES["identity"]) and makes it available to the plug-in for each database operation.

      Alternatively, after the end user authenticates with your application, your application can include the end user's bearer token in the Authorization header of the REST API call. The middleware then retrieves this token from the header (for example, request.headers.get("Authorization")).

      # settings.py
      import oracledb.plugins.end_user_sec_provider
      
      MIDDLEWARE = [
          'django.middleware.security.SecurityMiddleware',
          ...
          'oracledb.plugins.end_user_sec_provider.EndUserSecMiddleware',
      ]
      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.oracle',
              'NAME': 'db_name',
              'USER': 'hr_db',
              'PASSWORD': '<hr_db connection pool user password>',
              'OPTIONS': {
                  "extra_auth_params": {
                      "end_user_sec_params": {
                          "spi_type": "azure_tokens",
                          "auth_flow": "client_credentials",
                          "authority": "https://login.microsoftonline.com/<tenant_id>",
                          "client_id": <client_id>,
                          "client_credential": <secret>,
                          "scopes": <scopes>,
                          "data_roles": ["hr_role"],             # optional
                          "attributes": { "hr.hcm_context": {"emp_id": 3, "org_id": 5}} # optional
                      }
                  }
              }
          }
      }
      
    2. Flask applications

      Unlike Django, Flask does not use a middleware chain. So, python-oracledb's Oracle Deep Data Security plug-in is wired up inside the view function for each request. In each handler that talks to the database, extract the end-user identity from the incoming request, pass it to set_end_user_identity(), and then call oracledb.connect() with the end_user_sec_params block supplied as extra_auth_params.

      The end-user identity is typically an OAuth 2.0 access token carried in the Authorization request header when the application receives the token from an upstream caller, or a token the Flask application acquires directly using a library such as MSAL when the application authenticates the end user itself. Either way, the token becomes the value passed to set_end_user_identity().

      from flask import Flask, request, jsonify
      import oracledb
      import oracledb.plugins.end_user_sec_provider as deep_data_sec_provider
      
      app = Flask(__name__)
      
      @app.route("/myapi", methods=["GET"])
      def myfun():
          token = #get token from request headers if frontend passes token in authorization headers or
                  #get token from msal if frontend is not there
      
          deep_data_sec_provider.set_end_user_identity(token)
      
          conn = oracledb.connect(
                                    user="hr_db",
                                    password="<database password>",
                                    dsn="<your connect string>",
                                    wallet_location="path to wallet file",
                                    wallet_password="<wallet password>",
                                    extra_auth_params={
                                        "end_user_sec_params": {
                                            "spi_type": 'azure_tokens',
                                            "auth_flow": 'client_credentials',
                                            "client_id":"<client_id>",
                                            "client_credential":"<secret>",
                                            "authority":"https://login.microsoftonline.com/<tenant_id>",
                                            "scopes":"<scopes>",
                                            "data_roles": ["hr_role"],        # optional
                                            "attributes": { "hr.hcm_context": {"emp_id": 3, "org_id": 5}}   # optional
                                        }
                                    }
                                  )
          
          data = mygetdata(conn)
          
          return jsonify(data), 200
      The plug-in reads the identity set by set_end_user_identity(), acquires the database-access token using the configured end_user_sec_params, and attaches the end-user security context payload to every database call made on that connection.
    3. Other web frameworks

      For other Python web frameworks, such as FastAPI, apply the same pattern as the Flask example above: set the end-user identity for the current execution context with set_end_user_identity() before acquiring a connection, and pass the end_user_sec_params block in the extra_auth_params parameter of your connection or pool-creation call. The hook point (middleware, dependency, before-request handler) depends on your framework; the identity-and-configuration handoff to the plug-in is the same.

      The following example shows this pattern applied to a FastAPI application.

      import oracledb
      import oracledb.plugins.end_user_sec_provider as deepsec_provider
      from fastapi import FastAPI, Header
      
      app = FastAPI()
      
      @app.get("/myapi")
      def myfun(authorization: Optional[str] = Header(default="")):
          token = #get token from request headers if frontend passes token in authorization headers
      
          deepsec_provider.set_end_user_identity(token)
          conn = oracledb.connect(
                                    user="hr_db",
                                    password="<database password>",
                                    dsn="<your connect string>",
                                    wallet_location="path to wallet file",
                                    wallet_password="<wallet password>",
                                    extra_auth_params={
                                        "end_user_sec_params": {
                                            "spi_type": 'azure_tokens',
                                            "auth_flow": 'client_credentials',
                                            "client_id":"<client_id>",
                                            "client_credential":"<secret>",
                                            "authority":"https://login.microsoftonline.com/<tenant_id>",
                                            "scopes":"<scopes>"
                                        }
                                    }
                                  )
          
          data = mygetdata(conn)
          return data
      The plug-in acquires the database-access token and attaches the security context payload automatically.
For more information about the python-oracledb driver's support for Oracle Deep Data Security, see the python-oracledb documentation.

9.2.4 Configure .NET Applications

The ODP.NET driver supports Oracle Deep Data Security (Deep Sec) by propagating the end-user security context payload from your .NET application to the database.

For details, see the Oracle Data Provider for .NET documentation.

9.2.5 Advanced: Implement a Security Context Provider

If your authentication and authorization workflow requires custom logic, or if there's no out-of-the-box (OOTB) security context provider available for your application framework, you can build your own provider.

A custom provider bridges your application's security context and the Oracle client driver, enabling automatic propagation of the end-user security context payload to the database without modifying your application code.

The instructions in this section are primarily for the Oracle JDBC driver and Java-based application frameworks. If you are using a different client driver (such as python-oracledb or ODP.NET), apply the same principles using the equivalent provider interface and registration mechanism in your driver. See the respective driver documentation for API details.

9.2.5.1 Understand the Security Context Payload

Before building a provider, understand the components of the end-user security context payload that the database expects. Every payload must include the following mandatory components and may include optional components.

  • End-user identity (mandatory): The end user's name as asserted in the IAM access token. For end users managed locally, this is the name of the end user created in the database.
  • Database access token (mandatory): Authorizes the application’s connection to the database. Obtain this token from your IAM system using one of the following flows:
    • Client credentials flow: The application authenticates as itself.
    • On-behalf-of (OBO) flow: The application exchanges the end-user’s token for a database-access token.
  • Data roles (optional): A list of additional data roles that the application can enable for the end-user security context, beyond those mapped to application roles in IAM and those enabled by default for the application identity.
  • Context attributes (optional): A dictionary of application-defined key-value pairs to include in the security context. Used when application logic or data grants rely on custom end-user context attributes.

9.2.5.2 Understand the Driver’s Provider Interface

To propagate the end-user security context payload, the Oracle JDBC driver defines a Service Provider Interface (SPI) named EndUserSecurityContextProvider. Your custom provider must implement this interface and its getEndUserSecurityContext() method.

The JDBC driver invokes getEndUserSecurityContext() before every database operation on the connection. Your implementation must return an EndUserSecurityContext object containing the end-user identity, database-access token, and any optional data roles or attributes. The driver piggybacks this payload to the database on the next round-trip. If the method returns null, the driver proceeds without attaching a security context payload.

Note:

For other client drivers, use the equivalent provider interface. For example, the python-oracledb driver's end_user_sec_provider plug-in uses a configuration-driven mechanism with middleware hooks. See the respective driver documentation for the specific interface contract.

9.2.5.3 Build the Security Context Provider

Your provider implementation must perform three tasks each time the driver calls getEndUserSecurityContext(). The following steps describe these tasks for a JDBC provider. Adapt the approach to your client driver as needed.

  1. Extract the end-user identity from your framework’s security context.
    Every application framework maintains a request-scoped or thread-local security context that holds the authenticated user’s identity after the framework’s authentication filter has processed the request. Your provider must read the end-user identity from this context.
    Examples of framework security context mechanisms:
    • Jakarta EE / Servlet containers: HttpServletRequest.getUserPrincipal() or the JAX-RS SecurityContext
    • Helidon: io.helidon.security.SecurityContext
    Other frameworks provide equivalent mechanisms. Consult your framework's security documentation.
    Extract either the raw OAuth 2.0 access token (for IAM-managed users) or the user name and lookup key (for users managed in the application's own user store). If your framework stores the token as a parsed object (such as a JWT), you may need to retrieve the original serialized token string as the database expects the raw token.
  2. Acquire the database-access token from your IAM.
    Your provider must obtain a token that authorizes the application to access the database resource. This token is separate from the end user’s token.
    Use an OAuth 2.0 client library available in your framework or ecosystem to request the token from your IAM’s token endpoint. The specific library depends on your environment:
    • Microsoft Entra ID: Use MSAL for Java (com.microsoft.azure:msal4j) for both client credentials and OBO flows.
    • OCI IAM: Use the OCI SDK for Java or a standard HTTP client to call the OCI IAM token endpoint with client credentials.
    When requesting the token, use the client ID, client secret, and scopes from your application’s IAM registration.
    Database-access tokens are typically valid for a limited duration (for example, one hour). Cache the token and reuse it until it nears expiry to avoid requesting a new token on every database call. For OBO tokens, use the end-user token as the cache key, because each user produces a distinct OBO token.
  3. Construct and return the EndUserSecurityContext.
    Use the JDBC driver’s builder methods to assemble the security context object from the components gathered in previous steps.
    For IAM-managed users (token-based identity):
    EndUserSecurityContext securityContext =
        EndUserSecurityContext.createWithToken(
            databaseAccessToken, endUserToken)
          .withDataRoles(Set.of(dataRoles))                // optional
          .withAttributes(contextAttributes);              // optional
    return securityContext;
    
    For users managed in the application's own user store (user name and lookup key):
    EndUserSecurityContext securityContext =
        EndUserSecurityContext.createWithUsername(
            databaseAccessToken, endUserName, key)
          .withAttributes(contextAttributes);              // optional
    return securityContext;
    
    If no end-user identity is available for the current request (for example, a health-check endpoint that does not authenticate users), return null. The driver proceeds without attaching a security context payload, and the connection operates with its default privileges.

9.2.5.4 Register the Provider with the Driver

The JDBC driver discovers your provider through the standard Java ServiceLoader mechanism.

To register your provider:
  1. Create a file named oracle.jdbc.spi.EndUserSecurityContextProvider in your provider JAR’s META-INF/services/ directory.
  2. Add a single line containing the fully qualified class name of your provider implementation. For example:
    # META-INF/services/oracle.jdbc.spi.EndUserSecurityContextProvider
    com.example.myapp.MySecurityContextProvider
    
    When a new JDBC connection is created (that is, when java.sql.Driver.connect(String, Properties) is called), the driver scans the class path for implementations of EndUserSecurityContextProvider and loads the one matching the provider name configured in the connection properties.

9.2.5.5 Configure the Driver to Use Your Provider

Set the oracle.jdbc.provider.endUserSecurityContext connection property to the name of your provider.

This name is the value returned by your provider’s getName() method. Set this property in your connection pool’s data source configuration.

For example, in a HikariCP-based configuration:
# Activate the custom provider
spring.datasource.hikari.data-source-properties.\
  oracle.jdbc.provider.endUserSecurityContext = my-custom-provider
Or programmatically when configuring the data source:
Properties props = new Properties();
props.setProperty(
    "oracle.jdbc.provider.endUserSecurityContext",
    "my-custom-provider"
);
dataSource.setDataSourceProperties(props);

9.2.5.6 Support Privilege Elevation (Optional)

Some application operations require temporary elevated privileges. For example, reading all employees’ salary data to generate a summary report. To support this, your provider can accept additional data roles injected at runtime for the duration of a specific code block.

The recommended approach is to pass elevated data roles through your framework’s existing security context mechanism, keeping the provider decoupled from application code. The OOTB Spring Boot provider uses this pattern:
  • Application code temporarily adds data roles (with a distinguishing prefix) to the framework’s security context before the database call.
  • The provider reads the prefixed roles from the security context and includes them in the EndUserSecurityContext payload.
  • After the method returns, the application restores the original security context.

To implement this pattern in your framework, use the equivalent of a method interceptor or decorator that wraps the target method, augments the security context with additional data roles, invokes the method, and restores the original context in a finally block.

For the Spring Boot reference implementation of this pattern (using GrantedAuthority and the @RunWithDataRoles annotation), see the Oracle JDBC Extensions source code on GitHub.

9.2.5.7 Note for Other Client Drivers

If your application uses python-oracledb or ODP.NET instead of JDBC, the same architectural pattern applies: implement a component that extracts the end-user identity, acquires the database-access token, constructs the security context payload, and hooks into the driver’s connection life cycle. The specific interfaces, registration mechanisms, and configuration properties differ by driver.

For the python-oracledb driver, see the python-oracledb documentation.

For the ODP.NET driver, see the Oracle Data Provider for .NET documentation.