# scramClientFirst {#scramClientFirst}

**Parent topic:**[Functions](../../Functions/category.md)

## Syntax {#syntax}

`scramClientFirst(user, cnonce)`

## Arguments {#arguments}

**user**is a string indicating a user name. It can only contain letters, underscores, or numbers. It cannot start with numbers. The length cannot exceed 30 characters.

**cnonce** is a client-generated [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce) \(a one-time random value\). It must be a 16-byte random value, encoded in base64.

## Details {#details}

`scramClientFirst` is a function used in the first stage of SCRAM \(Salted Challenge Response Authentication Mechanism\) authentication. It retrieves the server's authentication information.

**Return value:**

A tuple containing the following elements:

-   salt – The base64-encoded hash salt value.
-   iterCount – The iteration count for password hashing.
-   combined\_nonce – The base64-encoded combination of the client nonce and server nonce.

**Note:**

SCRAM Authentication Workflow

**1. Client initiates authentication request**

The client generates a **client nonce** \(a one-time random value\) and sends it along with the username to the server.

**2. Server responds to authentication request**

-   Upon receiving the request, the server uses `scramClientFirst` to generate:
    -   A **hash salt value** for encryption
    -   The **password hashing iteration count**
    -   A **server nonce**, which is combined with the client nonce
-   These details are then returned to the client.

**3. Client generates authentication proof and submits final request**

Using the received data, the client converts the plaintext password into a cryptographic key and generates a **client proof**, and then sends this proof back to the server for verification.

**4. Server verifies the authentication request**

The server uses `scramClientFinal` to validate the **combined nonce** and **client proof**. If successful, the server returns a **server signature** to the client.

**5. Client verifies the server signature**

The client checks the server’s returned signature. If verification passes, **login succeeds**. If verification fails, the session is **terminated**.

## Examples {#examples}

The following Python script provides a complete example of how to implement SCRAM authentication for a user \("user01"\) in a Python client. The server-side operations are handled through the [DolphinDB Python API](https://docs.dolphindb.cn/en/pydoc/chap1_quickstart_landingpage.html).

``` {#codeblock_zq1_tms_c2c}
import dolphindb as ddb

# Establish a session connection with DolphinDB
s = ddb.session("183.134.101.133", 8888)
s.run("print", 1, 2, 3)

##########################

import hashlib
import hmac
import os
import base64
import hashlib


# ------------------ Server Functions ------------------
def server_handle_client_first(username, client_nonce):
  """Handles the first authentication request, returns salt, iteration count, and combined nonce."""
    salt, iter_count, nonce = s.run("scramClientFirst", username, client_nonce)

    return {
        "salt": salt,
        "iteration_count": iter_count,
        "combined_nonce": nonce
    }

def server_handle_client_final(user, combined_nonce, client_proof):    
  """Handles the final authentication request, verifies client_proof, and returns server_signature."""
    return s.run("scramClientFinal", user, combined_nonce, client_proof)

# ------------------ Client Functions ------------------
def client_initiate_authentication(username):
    """Client initiates authentication by generating a client_nonce."""
    client_nonce = base64.b64encode(os.urandom(16)).decode()
    return {
        "username": username,
        "client_nonce": client_nonce
    }

def client_generate_proof(user, password, salt, iteration_count, cnonce, combined_nonce):
    """Generates the client_proof."""
    
    # Compute SaltedPassword
    salted_password = hashlib.pbkdf2_hmac(
        'sha256',
        password.encode(),
        base64.b64decode(salt),
        iteration_count
    )
    
    # Generate keys
    client_key = hmac.new(salted_password, b"Client Key", hashlib.sha256).digest()
    stored_key = hashlib.sha256(client_key).digest()
    server_key = hmac.new(salted_password, b"Server Key", hashlib.sha256).digest()
    
    # Construct AuthMessage
    auth_message = (
        f"n={user},r={cnonce},"  # client-first-bare
        f"r={combined_nonce},s={salt},i={iteration_count},"  # server-first
        f"c=biws,r={combined_nonce}"                         # client-final-without-proof
    )
    
    # Compute ClientProof
    client_signature = hmac.new(stored_key, auth_message.encode(), hashlib.sha256).digest()
    client_proof = bytes([ck ^ cs for ck, cs in zip(client_key, client_signature)])
    
    return {
        "client_proof": base64.b64encode(client_proof).decode(),
        "server_key": server_key,
        "auth_message": auth_message
    }

# ------------------ SCRAM Authentication ------------------
if __name__ == "__main__":
    
    # Client initiates authentication
    client_data = client_initiate_authentication("user01")
    
    # Server processes the first request
    server_response = server_handle_client_first(
        client_data["username"], 
        client_data["client_nonce"]
    )
    
    password = "123456"
    
    # Client generates proof
    client_proof_data = client_generate_proof(
        "user01",
        password,
        server_response["salt"],
        server_response["iteration_count"],
        client_data["client_nonce"],
        server_response["combined_nonce"]
    )
    
    # Server verifies and returns signature
    # auth_sessions[server_response["combined_nonce"]]["auth_message"] = client_proof_data["auth_message"]
    server_signature = server_handle_client_final(
        "user01",
        server_response["combined_nonce"],
        client_proof_data["client_proof"]
    )
    
    # Client verifies the server signature
    computed_server_sig = hmac.new(
        client_proof_data["server_key"],
        client_proof_data["auth_message"].encode(),
        hashlib.sha256
    ).digest()
    
    # If verification passes, authentication succeeds
    assert server_signature == base64.b64encode(computed_server_sig).decode()
    print("SCRAM authentication succeeded!")

    # Check current session and user
    print(s.run("getCurrentSessionAndUser()"))
```

