Authenticating with User Name and Password and MFA and Return an OTP

This use case provides a step-by-step example of using the Oracle Identity Cloud Service Authentication API to authenticate with a user's credentials and Multi-Factor Authentication (MFA) and to return an encrypted OTP in the response.

Note:

Use this Authenticate API only if you're building your own end-to-end login experience by developing a custom sign-in application to be used by Oracle Identity Cloud Service.

Note:

This Authenticate API can't be used to integrate your applications with Oracle Identity Cloud Service for single sign-on purposes.

Note:

See the Oracle Identity Cloud Service Authentication API Postman collection for extensive authentication use case examples. Download the AUTHN-API Return Passcode.postman_collection.json collection and the global variables file from the idcs-authn-api-rest-clients folder within GitHub and then import them into Postman.

Oracle Identity Cloud Service can be configured to send a time-based one-time passcode (OTP) to directly to a user for authentication or have the passcode encrypted and sent to the consuming client who can then send it to the user for authentication.

For example, administrators can configure Oracle Identity Cloud Service to send time-based one-time passcodes (OTP) to the Oracle Mobile Authenticator (OMA) app or email the OTPs to the user's primary email address. In both cases, Oracle Identity Cloud Service generates the OTP, sends it directly to the user and the user enters the code for authentication. To understand how to set these options using REST, see Authentication Factor Enrollment With Factor Verification - SMS and Authentication Factor Enrollment With Factor Verification - Email.

Alternatively, Administrators can configure Oracle Identity Cloud Service to return an encrypted OTP in the API response to the consuming client so that the consuming client can initiate or send the OTP to the user. Two advantages to this approach are that it allows the consuming client to customize the authentication message and to also change the sender details to suit their business needs. To configure Oracle Identity Cloud Service to return the encrypted OTP in the response, the consuming client must complete the following steps.
  1. Step 1: Create a CustomUI Application

  2. Step 2: Generate a Key Pair a Self-Signed Certificate

  3. Step 3: Configure the Application to Return the OTP in the Response

  4. Step 4: Request the OTP

Note:

These steps assume that MFA is enabled and a sign-on policy is created for MFA. See Configuring Multi-Factor Authentication Settings.

Encryption and Decryption

This implementation uses the following specification to encrypt and decrypt the OTP code received. See PKCS #1: RSA Cryptography Specifications, Version 2.0, section 7.1 RSAES-OAEP.

OTP Decryption Code

Use the following Java code to decrypt the OTP.
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package decryption;

import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;

/**
 *
 * @author <author>
 */
public class DecryptOtpCode {
    
    private static Key getPrivateKey(String privateKeyPEM) throws Exception {
        byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        return kf.generatePrivate(keySpec);
    }
    
    public static void main(String args[]) {
        String value = "<encrypted_value>";
        String privatekey = 
                            "<pem_privatekey_data>";
        try {
                Cipher cipherInstance =
                        Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding");
                CertificateFactory factory = CertificateFactory.getInstance("X.509");
                byte [] decoded = Base64.getDecoder().decode(value);
                PrivateKey pKey = (PrivateKey)getPrivateKey(privatekey);
                cipherInstance.init(Cipher.DECRYPT_MODE, pKey);
                byte[] decrypted = cipherInstance.doFinal(decoded);
                System.out.println("Decrypted text is " + new String(decrypted));
            } catch (Exception e) {
                //Unable to encrypt the content. Default to send the otp to user
                //no error or exception thrown.
                e.printStackTrace();
            }
    }
    
}

Step 1: Create a CustomUI Application

See Add Applications for more information about custom applications.

Step 2: Generate a Key Pair a Self-Signed Certificate

In order to receive the OTP in the response, the consuming client must generate a private/public key pair, then generate a self-signed certificate, and import that certificate into the CustomUI application.
  • Use the following command to generate a private key.
    [ req ]
    encrypt_key = no
    default_bits = 2048
    default_md = sha256
    utf8 = yes
    string_mask = utf8only
    prompt = no
    distinguished_name = user_dn
    [ user_dn ]
    0.organizationName = "Oracle"
    organizationalUnitName = "OCI"
    commonName = "OtpClient"
    
  • Use the following command to generate a self-signed certificate.
    #generate self signed client certificate
    
    openssl genrsa -out OtpClient.key 2048
    openssl req -new -x509 -days 10000 -key OtpClient.key -out OtpClient.crt  -subj "/CN=Root CA/C=IN/ST=KarnatakaCalifornia/L=Bangalore/O=Oracle"  -config otp-client.conf
    openssl pkcs8 -topk8 -inform PEM -in OtpClient.key -out OtpClientX509Format.key -nocrypt
    

Step 3: Configure the Application to Return the OTP in the Response

Once the self-signed certificate is generated, you need to import it into the CustomUI application.
  1. In the Identity Cloud Service console, expand the Navigation Drawer, click Applications, CustomUI application, Configuration, and then Client Configuration.
  2. Import the self-signed certificate in the Trusted Client Certificate and Save the configuration.

Step 4: Request the OTP

Request Payload
Attribute Supported Values / Sample Values Multi-Valued Usage Details
userFlowControlledByExternalClient true / false false
Set this option to
true
and the OTP will be returned in the response in the encrypted format specified.

Note: The certificate used for encryption is uploaded to the application in advance and is referred using the x5t attribute in the request example as mentioned below.

x5t String / X509 SHA-1 Certificate Thumbprint  

When specified, the service uses this uploaded certificate to encrypt the OTP data.

Note: The "x5t" attribute should match the uploaded certificate.

Request Example
{
   "op": "credSubmit",
   "credentials": {
      "username": "test.user",
      "password": "Welcome@1"
   },
   "userFlowControlledByExternalClient": true,
   "x5t": "<certificate thumbprint>",
   "requestState": "{{requestState}}"
}
Response Payload
Attribute Supported Values / Sample Values Multi-Valued Usage Details
otp

Map

"otp": {
        "value": "IMCw==",
        "alg": "RSAES-OAEP",
        "x5t": "<certificate thumbprint>"
 }
false

When present in the response, the attribute contains the encrypted OTP with following details.

  • value: Encrypted value.
  • alg: Algorithm used for encryption.
  • x5t: SHA-1 X509 Thumbprint of the certificate used for encryption.

Response Example

{
    "otp": {
        "value": "IMsNO+rqNCw==",
        "alg": "RSAES-OAEP",
        "x5t": "<certificate thumbprint>"
    },
    "status": "success",
    "ecId": "Ft^OD161000000000",
    "displayName": "+91XXXXXXXX013",
    "nextAuthFactors": [
        "SMS"
    ],
    "SMS": {
        "credentials": [
            "otpCode"
        ]
    },
    "nextOp": [
        "credSubmit",
        "getBackupFactors",
        "resendCode"
    ],
    "scenario": "AUTHENTICATION",
    "requestState": "FrrACc",
    "trustedDeviceSettings": {
        "trustDurationInDays": 15
    }
}