Using the Authentication APIs

Configuring SSL
Authenticating to the Store
Renewing Expired Login Credentials
Unauthorized Access

Oracle NoSQL Database can be installed such that your client code does not have to authenticate to the store. (For the sake of clarity, most of the examples in this book do not perform authentication.) However, if you want your store to operate in a secure manner, you can require authentication. Note that doing so will result in a performance cost due to the overhead of using SSL and authentication. While best practice is for a production store to require authentication over SSL, some sites that are performance sensitive may want to forgo that level of security.

Authentication involves sending username/password credentials to the store at the time a store handle is acquired. A store that is configured to support authentication is automatically configured to communicate with clients using SSL in order to ensure privacy of the authentication and other sensitive information. When SSL is used, SSL certificates need to be installed on the machines where your client code runs in order to validate that the store that is being accessed is trustworthy.

Configuring a store for authentication is described in the Oracle NoSQL Database Security Guide.

Configuring SSL

If you are using a secure store, then all communications between your client code and the store is transported over SSL, including your authentication credentials. You must therefore configure your client code to use SSL. To do this, you identify where the SSL certificate data is, and you also separately indicate that the SSL transport is to be used.

Identifying the Trust Store

When an Oracle NoSQL Database store is configured to use the SSL transport, a series of security files are generated using a security configuration tool. One of these files is the client.trust file, which must be copied to any machine running Oracle NoSQL Database client code.

For information on using the security configuration tool, see the Oracle NoSQL Database Security Guide.

Your code must be told where the client.trust file can be found because it contains the certificates necessary for your client to establish an SSL connection with the store. You indicate where this file is physically located on your machine using the oracle.kv.ssl.trustStore property. There are two ways to set this property:

  1. Identify the location of the trust store by using a Properties object to set the oracle.kv.ssl.trustStore property. You then use KVStoreConfig.setSecurityProperties() to pass the Properties object to your KVStore handle.

    When you use this method, you use KVSecurityConstants.SSL_TRUSTSTORE_FILE_PROPERTY as the property name.

  2. Use the oracle.kv.security property to refer to a properties file, such as the client.trust file. In that file, set the oracle.kv.ssl.trustStore property.

Setting the SSL Transport Property

In addition to identifying the location of the client.trust file, you must also tell your client code to use the SSL transport. You do this by setting the oracle.kv.transport property in one of two ways:

  1. Identify the location of the trust store by using a Properties object to set the oracle.kv.transport property. You then use KVStoreConfig.setSecurityProperties() to pass the Properties object to your KVStore handle.

    When you use this method, you use KVSecurityConstants.TRANSPORT_PROPERTY as the property name, and KVSecurityConstants.SSL_TRANSPORT_NAME as the property value.

  2. Use the oracle.kv.security property to refer to a properties file, such as the client.trust file. In that file, set the oracle.kv.transport property.

Authenticating to the Store

You authenticate to the store by specifying a LoginCredentials implementation instance to KVStoreFactory.getStore(). Oracle NoSQL Database provides the PasswordCredentials class as a LoginCredentials implementation. If your store requires SSL to be used as the transport, configure that prior to performing the authentication. (See the previous section for details.)

Your code should be prepared to handle a failed authentication attempt. KVStoreFactory.getStore() will throw AuthenticationFailure in the event of a failed authentication attempt. You can catch that exception and handle the problem there.

The following is a simple example of obtaining a store handle for a secured store. The SSL transport is used in this example.

import java.util.Properties;

import oracle.kv.AuthenticationFailure;
import oracle.kv.PasswordCredentials;
import oracle.kv.KVSecurityConstants;
import oracle.kv.KVStoreConfig;
import oracle.kv.KVStoreFactory;



KVStore store = null;
try {
    /*
     * storeName, hostName, port, username, and password are all
     * strings that would come from somewhere else in your 
     * application.
     */
    KVStoreConfig kconfig = 
        new KVStoreConfig(storeName, hostName + ":" + port);

    /* Set the required security properties */
    Properties secProps = new Properties();
    secProps.setProperty(KVSecurityConstants.TRANSPORT_PROPERTY,
                         KVSecurityConstants.SSL_TRANSPORT_NAME);
    secProps.setProperty
        (KVSecurityConstants.SSL_TRUSTSTORE_FILE_PROPERTY,
        "/home/kv/client.trust");
    kconfig.setSecurityProperties(secProps);

    store =
        KVStoreFactory.getStore(kconfig,
            new PasswordCredentials(username,
                                    password.toCharArray()));
            null /* ReauthenticateHandler */);
} catch (AuthenticationFailureException afe) {
    /* 
     * Could potentially retry the login, possibly with different
     * credentials, but in this simple example, we just fail the
     * attempt.
     */
    System.out.println("authentication failed!")
    return;
} 

Another way to handle the login is to place your authentication credentials in a flat text file that contains all the necessary properties for authentication. In order for this to work, a password store must have been configured for your Oracle NoSQL Database store. (See the Oracle NoSQL Database Security Guide for information on setting up password stores).

For example, suppose your store has been configured to use a password file password store and it is contained in a file called login.pwd. In that case, you might create a login properties file called login.txt that looks like this:

oracle.kv.auth.username=clientUID1
oracle.kv.auth.pwdfile.file=/home/nosql/login.pwd
oracle.kv.transport=ssl
oracle.kv.ssl.trustStore=/home/nosql/client.trust 

In this case, you can perform authentication in the following way:

import oracle.kv.AuthenticationFailure;
import oracle.kv.PasswordCredentials;
import oracle.kv.KVStoreConfig;
import oracle.kv.KVStoreFactory;

/* the client gets login credentials from the login.txt file */
/* can be set on command line as well */
System.setProperty("oracle.kv.security", "/home/nosql/login.txt");

KVStore store = null;
try {
    /*
     * storeName, hostName, port are all strings that would come 
     * from somewhere else in your application.
     *
     * Notice that we do not pass in any login credentials.
     * All of that information comes from login.txt
     */
    myStoreHandle =
        KVStoreFactory.getStore(
            new KVStoreConfig(storeName, hostName + ":" + port)) 
} catch (AuthenticationFailureException afe) {
    /* 
     * Could potentially retry the login, possibly with different
     * credentials, but in this simple example, we just fail the
     * attempt.
     */
    System.out.println("authentication failed!")
    return;
} 

Renewing Expired Login Credentials

It is possible for an authentication session to expire. This can happen for several reasons. One is that the store's administrator has configured the store to not allow session extension and the session has timed out. These properties are configured using sessionExtendAllow and sessionTimeout. See the Oracle NoSQL Database Security Guide for information on these properties.

Reauthentication might also be required if some kind of a major disruption has occurred to the store which caused the authentication session to become invalidated. This is a pathological condition which you should not see with any kind of frequency in a production store. Stores which are installed in labs might exhibit this condition more, especially if the stores are frequently restarted.

An application can encounter an expired authentication session at any point in its lifetime, so robust code that must remain running should always be written to respond to authentication session expirations.

When an authentication session expires, by default the method which is attempting store access will throw AuthenticationRequiredException. Upon seeing this, your code needs to reauthenticate to the store, and then retry the failed operation.

You can manually reauthenticate to the store by using the KVStore.login() method. This method requires you to provide the login credentials via a LoginCredentials class instance (such as PasswordCredentials):

try {
    ...
    /* Store access code happens here */
    ...
} catch (AuthenticationRequiredException are) {
    /*
     * myStoreHandle is a KVStore class instance.
     * 
     * pwCreds is a PasswordCredentials class instance, obtained
     * from somewhere else in your code.
     */
    myStoreHandle.login(pwCreds);
} 

Note that this is not required if you use the oracle.kv.auth.username and oracle.kv.auth.pwdfile.file properties, as shown in the previous section. In that case, your Oracle NoSQL Database client code will automatically and silently reauthenticate your client using the values specified by those properties.

A third option is to create a ReauthenticationHandler class implementation that performs your reauthentication for you. This option is only necessary if you provided a LoginCredentials implementation instance (that is, PasswordCredentials) in a call to KVStoreFactory.getStore(), and you want to avoid a subsequent need to retry operations by catching AuthenticationRequiredException.

A truly robust example of a ReauthenticationHandler implementation is beyond the scope of this manual (it would be driven by highly unique requirements that are unlikely to be appropriate for your site). Still, in the interest of completeness, the following shows a very simple and not very elegant implementation of ReauthenticationHandler:

package kvstore.basicExample

import oracle.kv.ReauthenticationHandler;
import oracle.kv.PasswordCredentials;

public class MyReauthHandler implements ReauthenticationHandler {
    public void reauthenticate(KVStore reauthStore) {
        /*
         * The code to obtain the username and password strings would
         * go here. This should be consistent with the code to perform
         * simple authentication for your client.
         */
        PasswordCredentials cred = new PasswordCredentials(username,
            password.toCharArray());

        reauthStore.login(cred);
    }
} 

You would then supply a MyReauthHandler instance when you obtain your store handle:

import java.util.Properties;

import oracle.kv.AuthenticationFailure;
import oracle.kv.PasswordCredentials;
import oracle.kv.KVSecurityConstants;
import oracle.kv.KVStoreConfig;
import oracle.kv.KVStoreFactory;

import kvstore.basicExample.MyReauthHandler;

KVStore store = null;
try {
    /*
     * storeName, hostName, port, username, and password are all
     * strings that would come from somewhere else in your 
     * application. The code you use to obtain your username
     * and password should be consistent with the code used to
     * obtain that information in MyReauthHandler.
     */
    KVStoreConfig kconfig = 
        new KVStoreConfig(storeName, hostName + ":" + port);

    /* Set the required security properties */
    Properties secProps = new Properties();
    secProps.setProperty(KVSecurityConstants.TRANSPORT_PROPERTY,
                         KVSecurityConstants.SSL_TRANSPORT_NAME);
    secProps.setProperty
        (KVSecurityConstants.SSL_TRUSTSTORE_FILE_PROPERTY,
        "/home/kv/client.trust");
    kconfig.setSecurityProperties(secProps);

    store =
        KVStoreFactory.getStore(kconfig,
            new PasswordCredentials(username,
                                    password.toCharArray()));
            new MyReauthHandler());
} catch (AuthenticationFailureException afe) {
    /* 
     * Could potentially retry the login, possibly with different
     * credentials, but in this simple example, we just fail the
     * attempt.
     */
    System.out.println("authentication failed!")
    return;
} 

Unauthorized Access

Currently, clients which must authenticate to a store either have no access or complete access, depending on the current state of their authorization session. However, future releases of Oracle NoSQL Database may introduce authorization levels such that a call to the Oracle NoSQL Database API could fail due to not having the authority to perform the operation. When this happens, UnauthorizedException will be thrown.

In this release, your code should never see UnauthorizedException. However, you might want to plan your code so that it is possible to respond gracefully to this exception, especially if you think your organization would be interested in using multi-level access controls.

When UnauthorizedException is seen, the operation should not be retried. Instead, the operation should either be abandoned entirely, or your code could attempt to reauthenticate using different credentials that would have the required permissions necessary to perform the operation. Note that a client can log out of a store using KVStore.logout(). How your code logs back in is determined by how your store is configured for access, as described in the previous sections.