# Authentication Without Email / Password

### Obtaining Authentication Keys

Client Id and Client Secret are available over the merchant portal in the Settings menu.

<figure><img src="/files/YkpOFOLxOSzNWgqfr2xP" alt=""><figcaption></figcaption></figure>

### Authentication

Encryption is done using AES algorithm with PKCS7 padding, CBC mode and a key size of 256. Encryption of the body payload is as follows:-

{% tabs %}
{% tab title="Javascript" %}

```javascript
var CryptoJS = require('crypto-js');
var data = CryptoJS.AES.encrypt(JSON.stringify({
    merchantId: 'MERCHANT_ID',
    totp:''
}), 'CLIENT_SECRET').toString();

pm.variables.set("encryptedPayload", data);
```

{% endtab %}

{% tab title="Python" %}

```python
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import hashlib
import base64

def generate_salt():
    return get_random_bytes(8)

def evpkdf(passphrase, salt, key_size=32, iv_size=16):
    """
    Derives a key and IV from the passphrase and salt using OpenSSL compatible method.
    """
    d = d_i = b''  # Initialize empty bytes for key and IV derivation
    while len(d) < key_size + iv_size:
        d_i = hashlib.md5(d_i + passphrase.encode('utf-8') + salt).digest()
        d += d_i
    return d[:key_size], d[key_size:key_size + iv_size]

def pad(data):
    """Pads data to a multiple of AES block size (16 bytes)."""
    padding_len = 16 - len(data) % 16
    return data + chr(padding_len) * padding_len

def encrypt_data(data, passphrase, salt):
    key, iv = evpkdf(passphrase, salt)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted_data = cipher.encrypt(pad(data).encode())
    return encrypted_data

def create_encrypted_payload(data, passphrase):
    salt = generate_salt()
    encrypted_data = encrypt_data(data, passphrase, salt)
    encrypted_data_with_salt = b"Salted__" + salt + encrypted_data
    return base64.b64encode(encrypted_data_with_salt).decode('utf-8')

# Data to be encrypted
data = {
    'merchantId': MERCHANT_ID,
    'totp': ''
}

# Convert the data to a JSON string
data_str = json.dumps(data)

# Create the encrypted payload
encrypted_payload = create_encrypted_payload(data_str, CLIENT_SECRET)
```

{% endtab %}

{% tab title="PHP" %}

```php
function encrypt($toEncrypt, $secret){
    $salt = openssl_random_pseudo_bytes(8);
    $salted = $dx = '';
    while (strlen($salted) < 48) {
        $dx = md5($dx . $secret . $salt, true);
        $salted .= $dx;
    }

    $key = substr($salted, 0, 32);
    $iv = substr($salted, 32, 16);

    // encrypt with PKCS7 padding
    return base64_encode('Salted__' . $salt . openssl_encrypt($toEncrypt . '', 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv));
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.Security.Cryptography;
using System.Text;
using System.Linq;

public class EncryptionAES
{
    private static readonly Random SecureRandom = new Random();

    public static byte[] EvpKDF(byte[] password, byte[] salt, int keySize = 48, string hasher = "MD5", int iterations = 1)
    {
        byte[] keyMaterial = new byte[keySize];
        int generatedBytes = 0;
        byte[] block = null;
        using (var md = MD5.Create())
        {
            while (generatedBytes < keySize)
            {
                md.Initialize();

                if (block != null)
                    md.TransformBlock(block, 0, block.Length, null, 0);

                md.TransformBlock(password, 0, password.Length, null, 0);
                md.TransformFinalBlock(salt, 0, salt.Length);
                block = md.Hash;

                for (int i = 1; i < iterations; i++)
                {
                    block = md.ComputeHash(block);
                }

                int remaining = Math.Min(block.Length, keySize - generatedBytes);
                Array.Copy(block, 0, keyMaterial, generatedBytes, remaining);
                generatedBytes += remaining;
            }
        }

        return keyMaterial;
    }

    public static string EncryptAES(string data, string key)
    {
        using (var aes = Aes.Create())
        {
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            byte[] salt = new byte[8];
            SecureRandom.NextBytes(salt);

            byte[] derivedKey = EvpKDF(Encoding.UTF8.GetBytes(key), salt);
            byte[] keyBytes = derivedKey.Take(32).ToArray();
            byte[] ivBytes = derivedKey.Skip(32).Take(16).ToArray();

            aes.Key = keyBytes;
            aes.IV = ivBytes;

            using (var encryptor = aes.CreateEncryptor())
            using (var ms = new System.IO.MemoryStream())
            {
                ms.Write(Encoding.ASCII.GetBytes("Salted__"), 0, 8);
                ms.Write(salt, 0, salt.Length);

                using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                using (var sw = new System.IO.StreamWriter(cs))
                {
                    sw.Write(data);
                }

                return Convert.ToBase64String(ms.ToArray());
            }
        }
    }
}


//how to use
    var data = new { merchantId = "861331d4-aa85-42c2-8a4f-a8e78043809a",totp=""};
    string toEncrypt = JsonSerializer.Serialize(data);
    string secret = "SECRET";
    string encrypted = EncryptionAES.EncryptAES(toEncrypt, secret);
    Console.WriteLine("Encrypted: " + encrypted);
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
If we provide the clientSecret as string it will not use it as a key but a passphrase to derive the key value using a KDF. The default KDF for this library is similar to the open SSL EVP function. (for reference: <https://www.openssl.org/docs/manmaster/man3/EVP_BytesToKey.html>) This function generates the actual AES keys using MD5 hashing and uses 1 iteration.
{% endhint %}

```kotlin
/**
* based on: www.openssl.org/docs/crypto/EVP_BytesToKey.html
*/
fun evpKDF(password: ByteArray, salt: ByteArray, keySize: Int = 48, hasher: String = "MD5", iterations: Int = 1): ByteArray {
val keyMaterial = ByteArray(keySize)
var generatedBytes = 0
var block: ByteArray? = null
val md = MessageDigest.getInstance(hasher)

while (generatedBytes < keySize) {
        md.reset()

if (block != null)
            md.update(block)

        md.update(password)
        block = md.digest(salt)

for (i in 1 until iterations)
            block = md.digest(block)

val remaining = block!!.size.coerceAtMost(keySize - generatedBytes)
System.arraycopy(block, 0, keyMaterial, generatedBytes, remaining)
        generatedBytes += remaining
    }

return keyMaterial
}

fun encryptAES(data: String, key: String): String {
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
val salt = ByteArray(8)
secureRandom.nextBytes(salt)

val derivedKey = evpKDF(key.toByteArray(), salt)
val secretKey = SecretKeySpec(derivedKey.sliceArray(0..31), "AES")
val iv = IvParameterSpec(derivedKey.sliceArray(32..47))

    cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv)

val encrypted = cipher.doFinal(data.toByteArray())
val encryptedWithIv = "Salted__".toByteArray() + salt + encrypted

return Base64.getEncoder().encodeToString(encryptedWithIv)
}
```

### Generating access and refresh token using encrypted payload and clientId

<mark style="color:green;">`POST`</mark> `api/auth/generate-auth-token`

This endpoint is used for generating access and refresh tokens without using merchant email and password. It requires clientId and encrypted string generated from the encryption logic discussed above.&#x20;

#### Request Body

| Name                                               | Type   | Description                                                                 |
| -------------------------------------------------- | ------ | --------------------------------------------------------------------------- |
| clientId<mark style="color:red;">\*</mark>         | String | Client Id can be accessed in Settings menu                                  |
| encryptedPayload<mark style="color:red;">\*</mark> | String | AES encrypted (using client secret) payload containing merchant Id and totp |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
    "ok": true,
    "result": {
        "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImM1MzlkZWVjLTYwNDktNDZlMC1hZTJjLTUwZTJhNjQ2ZmMzMSIsImlhdCI6MTY2MjczOTczOSwiZXhwIjoxNjYyODI2MTM5fQ.AkvmP2oTAfj91w5w9arOAsiAAnfg1o-Ia-3b3PkZSaw",
        "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImM1MzlkZWVjLTYwNDktNDZlMC1hZTJjLTUwZTJhNjQ2ZmMzMSIsImlhdCI6MTY2MjczOTczOSwiZXhwIjoxNjY1MzMxNzM5fQ.CFjoO4XwZpuQNBpkVoQtpUbu1_yYVIFoMO5MqDWYGeg",
        "status": 2
    }
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.rocketfuel.inc/developer-guides/api-reference/payins/authentication/authentication-without-email-password.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
