Package oracle.jdbc

Interface AccessToken


public interface AccessToken

An access token generated by an authorization server. This interface is intended for integrating an OAuth 2.0 client with Oracle JDBC. It is not intended as a general-purpose solution for handling access tokens in Java applications.

Creating an AccessToken

Factory methods declared by this interface create instances of AccessToken from different inputs:

create(CharSequence, Duration)
Accepts the values of access_token and expires_in parameters issued by an OAuth 2.0 authorization server .
create(CharSequence, OffsetDateTime)
Also accepts an access_token issued by an authorization server, but it accepts the expiration time as an absolute value, instead of the relative expires_in value.
createJsonWebToken(char[])
Accepts the text of a JSON Web Token (JWT) and extracts its "exp" claim to get an expiration time.
createJsonWebToken(char[], PrivateKey)
Accepts the text of a JWT that requires a private key for proof of possession (PoP) , and extracts its "exp" claim to get an expiration time.
create(CharSequence)
Accepts text which may or may not be a JWT. This method can result in an unknown expiration time for non-JWT encoded tokens.

In most cases, one of the first two create methods should be used. These methods are designed to handle tokens of any opaque format that an authorization server may issue.

The createJsonWebToken factory methods should be used only if the issuer of the token is guaranteed the use of the JWT format, and is not going to use a different format in the future. OAuth 2.0 authorization servers do not usually make such a guarantee, so the create factory methods are usually a safer choice.

Usage of create methods that accept an expiration time should be preferred over create(CharSequence). When an expiration time is available, Oracle JDBC can detect if a token has expired and avoid sending it to the database. This conserves database resources that would otherwise be spent on rejecting an expired token.

Checking the Expiration Time

The expiration() method returns the expiration time of an AccessToken. Oracle JDBC does not send expired tokens to the database. If an attempt is made to send a token after the expiration time, Oracle JDBC will throw a SQLException with the ORA-25708 error code. Applications with long-running sessions should use the expiration() method to check and refresh an expired token before Oracle JDBC sends it to the database. Alternatively, an application might create a cache as described in the next section. The cache will automatically replace an expired token with a new one.

Caching Access Tokens

Factory methods declared by this interface can create a Supplier that caches an instance of AccessToken until it is about to expire.

In most cases, createCache should be used instead of createJsonWebTokenCache, unless an application wants to strictly enforce the use of JWT-encoded tokens.

The caching Supplier created by these factory methods can be used to configure a PoolDataSource from UCP . The Supplier can also be passed to OracleCommonDataSource.setTokenSupplier(java.util.function.Supplier), and a connection pool like Hikari can use that OracleDataSource when creating JDBC connections.

The code example below creates a cache of access tokens which are requested using the Nimbus OAuth 2.0 SDK. A PoolDataSource is then configured to use this cache.


  void example(PoolDataSource poolDataSource) throws SQLException {
    Supplier<? extends AccessToken> cachedTokenSupplier = AccessToken.createCache(() -> {
      final TokenResponse response;
      try {
        TokenRequest tokenRequest = new TokenRequest(
          new URI(System.getenv("OAUTH_TOKEN_ENDPOINT")),
          new ClientSecretBasic(
            new ClientID(System.getenv("OAUTH_CLIENT_ID")),
            new Secret(System.getenv("OAUTH_CLIENT_SECRET"))),
          new ClientCredentialsGrant(),
          new Scope(System.getenv("OAUTH_SCOPE")));
 
        HTTPResponse httpResponse = tokenRequest.toHTTPRequest().send();
        response = TokenResponse.parse(httpResponse);
      }
      catch (Exception requestFailed) {
        throw new RuntimeException(requestFailed);
      }
 
      if (!response.indicatesSuccess()) {
        throw new RuntimeException(
          response.toErrorResponse()
            .getErrorObject()
            .toJSONObject()
            .toString());
      }
 
      com.nimbusds.oauth2.sdk.token.AccessToken accessToken =
        response.toSuccessResponse()
          .getTokens()
          .getAccessToken();
 
      return AccessToken.create(
        accessToken.getValue(),
        Duration.ofSeconds(accessToken.getLifetime()));
    });
 
    poolDataSource.setTokenSupplier(cachedTokenSupplier);
  }
  

The cache is designed to execute the token request only when necessary. After requesting a token one time, it will reuse that same token until it expires. To support connection pools that open JDBC connections in parallel, the cache issues only one token request at a time and makes other threads wait for that request to complete. Once the request finishes, every waiting thread will use the same token to establish its JDBC connection.

Shortly before a cached token will expire, the cache eagerly requests a replacement on a background thread. This usually results in a new token becoming available before the current one expires, which means application threads are unlikely to block on token retrieval.

If the cache notices that a previously requested token was never consumed, then it will stop executing these eager requests. This avoids unnecessary token requests during periods in which new JDBC connections are not being created.

Access Token Service Provider

Access tokens that authorize logins to Oracle Database may be provided to Oracle JDBC by an AccessTokenProvider. This is a service provider interface (SPI) which allows an existing application to begin using access tokens with Oracle JDBC without having to add new code as shown in the previous section. Instead, an AccessTokenProvider may be installed on the class-path or module-path of a Java application, and Oracle JDBC can be configured to use it through a connection properties file or any other means of configuring a JDBC datasource that an application supports.

Programmers may create their own AccessTokenProvider, or use one of the open source implementations available from the ojdbc-extensions project.

Since:
23
  • Method Details

    • toCharArray

      char[] toCharArray()
      Returns a char arrary containing the string representation of this access token. Mutating the contents of the char array will have no effect on this AccessToken.
      Returns:
      The text representation of this access token. Not null. Not retained.
    • expiration

      OffsetDateTime expiration()

      Returns the expiration time of this access token, or OffsetDateTime.MAX if the expiration time is unknown. This method can be used to check if an access token needs to be refreshed before Oracle JDBC sends it to the database.

      Returns:
      The expiration time of this access token. Not null.
    • privateKey

      Optional<PrivateKey> privateKey()
      Returns an Optional containing a private key required for proof of possession (PoP), or an empty Optional if this access token does not require PoP.
      Returns:
      Optional containing a private key for PoP, or an empty Optional if PoP is not required. Not null.
    • createJsonWebToken

      static AccessToken createJsonWebToken(char[] token, PrivateKey privateKey)

      Creates an AccessToken representing a JSON Web Token (JWT) that requires proof of possession (PoP) of a privateKey. Proof of possession is specified by RFC 7800.

      The token argument to this method must be provided as a string of characters representing a JSON Web Token (JWT) as specified by RFC 7519. This method uses the "exp" claim of the JWT as the expiration time for the token.

      The returned AccessToken retains copies of the token and privateKey provided to this method. After this method returns, the original token and privateKey may be mutated or destroyed by the caller.

      Parameters:
      token - JWT encoded token. Not null.
      privateKey - Private key for which the token requires proof of possession. Not null.
      Returns:
      An AccessToken representing a JWT that requires proof of possession of a private key. Not null.
      Throws:
      NullPointerException - If the token or privateKey are null
      IllegalArgumentException - If the token text is malformed or the privateKey uses an encoding that cannot be decoded.
      IllegalStateException - If security providers required to decode the privateKey are not installed.
    • createJsonWebToken

      static AccessToken createJsonWebToken(char[] token)

      Creates an AccessToken representing a JSON Web Token (JWT).

      The token argument to this method must be provided as a string of characters representing a JSON Web Token (JWT) as specified by RFC 7519. This method uses the "exp" claim of the JWT as the expiration time for the token.

      The returned AccessToken retains a copy of the token provided to this method. After this method returns, the original token may be mutated or destroyed by the caller.

      Parameters:
      token - JWT encoded token. Not null.
      Returns:
      An AccessToken representing a JWT. Not null.
      Throws:
      NullPointerException - If the token is null
      IllegalArgumentException - If the token has an unrecognized encoding.
    • create

      static AccessToken create(CharSequence accessToken, Duration expiresIn)

      Creates an AccessToken from text that Oracle JDBC treats as opaque. Unlike the createJsonWebToken methods, this method will not attempt to parse the text as a JSON Web Token (JWT).

      This method computes the expiration time for the token as OffsetDateTime.now(UTC).plus(expiresIn). In order for this computation to be correct, this method must be called immediately after receiving a token from an authorization server.

      The returned AccessToken retains a copy of the accessToken provided to this method. After this method returns, the original accessToken may be mutated or destroyed by the caller. For example, the accessToken could be passed to this method as a CharBuffer which is then cleared after this method returns.

      Parameters:
      accessToken - Token text. Not null.
      expiresIn - Duration after current time when the token expires. Not null. Not negative or zero.
      Returns:
      An opaque AccessToken. Not null.
      Throws:
      NullPointerException - If accessToken or expiresIn is null.
    • create

      static AccessToken create(CharSequence accessToken, OffsetDateTime expiresAt)

      Creates an AccessToken from text that Oracle JDBC treats as opaque. Unlike the createJsonWebToken methods, this method will not attempt to parse the text as a JSON Web Token (JWT).

      The expiration time for the token is given by the expiresAt parameter.

      The returned AccessToken retains a copy of the accessToken provided to this method. After this method returns, the original accessToken may be mutated or destroyed by the caller. For example, the accessToken could be passed to this method as a CharBuffer which is then cleared after this method returns.

      Parameters:
      accessToken - Token text. Not null.
      expiresAt - The time when the token expires. Not null.
      Returns:
      An opaque AccessToken. Not null.
      Throws:
      NullPointerException - If accessToken or expiresAt is null.
    • create

      static AccessToken create(CharSequence accessToken)

      Creates an AccessToken from text that Oracle JDBC attempts to parse as a JSON Web Token (JWT), but treats as opaque if the format is not recognized.

      If a JWT format is recognized, this method uses the "exp" claim as the expiration time for the token. Otherwise, if the text is not recognized as a JWT, or it is a JWT without an exp claim, the expiration() method will return OffsetDateTime.MAX to signify an unknown expiration time.

      The returned AccessToken retains a copy of the accessToken provided to this method. After this method returns, the original accessToken may be mutated or destroyed by the caller. For example, the accessToken could be passed to this method as a CharBuffer which is then cleared after this method returns.

      Parameters:
      accessToken - Token text. Not null.
      Returns:
      An AccessToken representation of the provided text. Not null.
      Throws:
      NullPointerException - If accessToken is null.
    • createJsonWebTokenCache

      static Supplier<? extends AccessToken> createJsonWebTokenCache(Supplier<? extends AccessToken> tokenSupplier)

      Returns a Supplier that caches access tokens generated by a tokenSupplier.

      The caching Supplier returned by this method throws IllegalArgumentException if the tokenSupplier returns an AccessToken not recognized as one created by createJsonWebToken(char[]) or createJsonWebToken(char[], PrivateKey)

      The caching Supplier returned by this method is otherwise identical in behavior to one returned by createCache(Supplier).

      Parameters:
      tokenSupplier - Supplier of AccessTokens. Not null. Retained.
      Returns:
      A caching token supplier. Not null.
      Throws:
      NullPointerException - If tokenSupplier is null.
    • createCache

      static Supplier<? extends AccessToken> createCache(Supplier<? extends AccessToken> tokenSupplier)

      Returns a Supplier that functions as a cache for access tokens generated by a given tokenSupplier function.

      The cache retains a single AccessToken, and it will continue to output that token until it is about to expire. When the cached token is about to expire, a new token is requested by invoking Supplier.get() on the tokenSupplier.

      The cache is initially empty. The get method of the tokenSupplier is not invoked until a token is requested from the cache by calling the get method of the caching Supplier returned by this method.

      It is not required for the tokenSupplier to have a thread-safe implementation. The cache will not perform concurrent invocations of tokenSupplier.get().

      If the tokenSupplier throws an unchecked exception, the cache will throw a CompletionException with that unchecked exception as the initial cause.

      If the tokenSupplier outputs a null value, the cache will throw a CompletionException with a NullPointerException as the initCause().

      If the tokenSupplier outputs an AccessToken that was not created by a static factory method of the AccessToken interface, the cache will throw a CompletionException with an IllegalArgumentException as the initCause().

      Parameters:
      tokenSupplier - Supplier of AccessTokens. Not null. Retained.
      Returns:
      A caching token supplier. Not null.
      Throws:
      NullPointerException - If tokenSupplier is null.