Documentation Index Fetch the complete documentation index at: https://mintlify.com/iamnasirudeen/key-management/llms.txt
Use this file to discover all available pages before exploring further.
This guide demonstrates how to implement secure authentication using client-side encryption and server-side decryption for passcodes and transaction PINs.
Overview
The KMS provides encrypted authentication for:
User passcodes (login authentication)
Transaction PINs (payment authorization)
All sensitive data is encrypted on the client-side using RSA public key encryption before transmission to the server.
Authentication Flow
Generate encryption key
First, obtain a public key for encrypting sensitive data: mutation GenerateKey {
generateClientEncryptionKey ( input : {
deviceId : "device-12345"
appVersion : "1.0.0"
}) {
publicKey
keyId
}
}
Store both the publicKey (for encryption) and keyId (for server-side decryption) on the client.
Encrypt sensitive data client-side
Use the public key to encrypt the passcode or PIN before sending to the server: import * as crypto from 'crypto' ;
function encryptData ( data : string , publicKey : string ) : string {
const buffer = Buffer . from ( data , 'utf-8' );
const encrypted = crypto . publicEncrypt (
{
key: publicKey ,
padding: crypto . constants . RSA_PKCS1_OAEP_PADDING ,
},
buffer ,
);
return encrypted . toString ( 'base64' );
}
// Example: Encrypt a passcode
const passcode = "123456" ;
const encryptedPasscode = encryptData ( passcode , publicKey );
Send encrypted data to server
Submit the encrypted passcode or PIN along with the keyId: mutation SetPasscode {
setUserPasscode ( input : {
encryptedPasscode : "base64-encrypted-string"
keyId : "key_1234567890_abc123def456"
})
}
Setting User Passcode
GraphQL Mutation
mutation SetUserPasscode ( $input : EncryptedPasscodeInput ! ) {
setUserPasscode ( input : $input )
}
interface EncryptedPasscodeInput {
encryptedPasscode : string ; // Base64-encoded encrypted passcode
keyId : string ; // Key ID from generateClientEncryptionKey
}
Server-Side Implementation
The server decrypts and securely stores the passcode using bcrypt hashing:
// From: src/auth/auth.service.ts:15-46
async setUserPasscode (
userId : string ,
encryptedPasscode : string ,
keyId : string ,
): Promise < boolean > {
try {
// Decrypt the passcode
const decryptedPasscode =
await this . encryptionService . decryptWithPrivateKey (
encryptedPasscode ,
keyId ,
);
// Hash the passcode for storage
const hashedPasscode = await bcrypt . hash ( decryptedPasscode , 10 );
// Store the hashed passcode
await this . prismaService . user . update ({
where: { id: userId },
data: { passcode: hashedPasscode },
});
this . logger . log ( `Successfully set passcode for user: ${ userId } ` );
return true ;
} catch (error) {
this.logger. error (
`Failed to set user passcode: ${ error . message } ` ,
error.stack,
);
throw new Error ( 'Failed to set passcode' );
}
}
Verifying User Passcode
GraphQL Mutation
mutation VerifyUserPasscode ( $input : EncryptedVerifyPasscodeInput ! ) {
verifyUserPasscode ( input : $input )
}
interface EncryptedVerifyPasscodeInput {
encryptedPasscode : string ; // Base64-encoded encrypted passcode
keyId : string ; // Key ID from generateClientEncryptionKey
}
Server-Side Implementation
// From: src/auth/auth.service.ts:51-93
async verifyUserPasscode (
userId : string ,
encryptedPasscode : string ,
keyId : string ,
): Promise < boolean > {
try {
// Get the user
const user = await this . prismaService . user . findUnique ({
where: { id: userId },
});
if ( ! user || ! user . passcode ) {
this . logger . warn (
`User not found or passcode not set for user: ${ userId } ` ,
);
return false ;
}
// Decrypt the passcode
const decryptedPasscode =
await this . encryptionService . decryptWithPrivateKey (
encryptedPasscode ,
keyId ,
);
// Verify the passcode
const isMatch = await bcrypt . compare ( decryptedPasscode , user . passcode );
if ( isMatch ) {
this . logger . log ( `Passcode verified successfully for user: ${ userId } ` );
} else {
this . logger . warn ( `Passcode verification failed for user: ${ userId } ` );
}
return isMatch ;
} catch (error) {
this.logger. error (
`Passcode verification failed: ${ error . message } ` ,
error.stack,
);
return false;
}
}
Setting Transaction PIN
GraphQL Mutation
mutation SetTransactionPin ( $input : EncryptedTransactionPinInput ! ) {
setTransactionPin ( input : $input )
}
interface EncryptedTransactionPinInput {
encryptedPin : string ; // Base64-encoded encrypted PIN
keyId : string ; // Key ID from generateClientEncryptionKey
}
Server-Side Implementation
// From: src/auth/auth.service.ts:98-128
async setTransactionPin (
userId : string ,
encryptedPin : string ,
keyId : string ,
): Promise < boolean > {
try {
// Decrypt the PIN
const decryptedPin = await this . encryptionService . decryptWithPrivateKey (
encryptedPin ,
keyId ,
);
// Hash the PIN for storage
const hashedPin = await bcrypt . hash ( decryptedPin , 10 );
// Store the hashed PIN
await this . prismaService . user . update ({
where: { id: userId },
data: { transactionPin: hashedPin },
});
this . logger . log ( `Successfully set transaction PIN for user: ${ userId } ` );
return true ;
} catch (error) {
this.logger. error (
`Failed to set transaction PIN: ${ error . message } ` ,
error.stack,
);
throw new Error ( 'Failed to set transaction PIN' );
}
}
Verifying Transaction PIN
GraphQL Mutation
mutation VerifyTransactionPin ( $input : EncryptedVerifyTransactionPinInput ! ) {
verifyTransactionPin ( input : $input )
}
interface EncryptedVerifyTransactionPinInput {
encryptedPin : string ; // Base64-encoded encrypted PIN
keyId : string ; // Key ID from generateClientEncryptionKey
}
Server-Side Implementation
// From: src/auth/auth.service.ts:133-178
async verifyTransactionPin (
userId : string ,
encryptedPin : string ,
keyId : string ,
): Promise < boolean > {
try {
// Get the user
const user = await this . prismaService . user . findUnique ({
where: { id: userId },
});
if ( ! user || ! user . transactionPin ) {
this . logger . warn (
`User not found or transaction PIN not set for user: ${ userId } ` ,
);
return false ;
}
// Decrypt the PIN
const decryptedPin = await this . encryptionService . decryptWithPrivateKey (
encryptedPin ,
keyId ,
);
// Verify the PIN
const isMatch = await bcrypt . compare ( decryptedPin , user . transactionPin );
if ( isMatch ) {
this . logger . log (
`Transaction PIN verified successfully for user: ${ userId } ` ,
);
} else {
this . logger . warn (
`Transaction PIN verification failed for user: ${ userId } ` ,
);
}
return isMatch ;
} catch (error) {
this.logger. error (
`Transaction PIN verification failed: ${ error . message } ` ,
error.stack,
);
return false;
}
}
Complete Example
Client-Side (TypeScript)
GraphQL Operations
import * as crypto from 'crypto' ;
// 1. Generate encryption key
const { publicKey , keyId } = await generateKey ({
deviceId: 'device-12345' ,
appVersion: '1.0.0'
});
// 2. Encrypt passcode
function encryptData ( data : string , publicKey : string ) : string {
const buffer = Buffer . from ( data , 'utf-8' );
const encrypted = crypto . publicEncrypt (
{
key: publicKey ,
padding: crypto . constants . RSA_PKCS1_OAEP_PADDING ,
},
buffer ,
);
return encrypted . toString ( 'base64' );
}
const passcode = '123456' ;
const encryptedPasscode = encryptData ( passcode , publicKey );
// 3. Set passcode
await setUserPasscode ({
encryptedPasscode ,
keyId
});
// 4. Verify passcode
const userInput = '123456' ;
const encryptedInput = encryptData ( userInput , publicKey );
const isValid = await verifyUserPasscode ({
encryptedPasscode: encryptedInput ,
keyId
});
Security Best Practices
Client-Side Encryption Always encrypt sensitive data on the client before transmission. Never send plaintext passcodes or PINs.
Key Management Store the keyId securely on the client. Use the same key for verification that was used during setup.
Server-Side Hashing The server decrypts and immediately hashes passwords using bcrypt with 10 salt rounds.
No Plaintext Storage Only hashed values are stored in the database. Plaintext is never persisted.
Error Handling
The authentication mutations return false on failure without throwing errors for verification operations:
setUserPasscode / setTransactionPin: Throws error on failure
verifyUserPasscode / verifyTransactionPin: Returns false on failure
// Set operations throw on error
try {
await setUserPasscode ( input );
console . log ( 'Passcode set successfully' );
} catch ( error ) {
console . error ( 'Failed to set passcode:' , error . message );
}
// Verify operations return boolean
const isValid = await verifyUserPasscode ( input );
if ( isValid ) {
console . log ( 'Authentication successful' );
} else {
console . log ( 'Authentication failed' );
}
Next Steps
Key Generation Learn about key generation and rotation strategies
Encryption & Decryption Understand the encryption/decryption workflow