Accepts a 3D Secure authentication request with cryptographic signatures.
This endpoint is used to confirm a 3DS authentication after the user has
reviewed the transaction details in their wallet application. The request
must include valid cryptographic signatures proving the user's consent.
Example with Ethersjs:
const wallet = ethers.Wallet.createRandom();
const types = {
Authenticate: [{ name: 'id', type: 'string' }],
};
const domain = {
name: 'Kulipa Wallet Authentication',
version: '1',
chainId: 1, // this is the chain ID of the wallet network
};
const message = {
id: '3ds-e259ee22-f857-444d-9c38-e0bf2ee669e3', // the 3DS authentication ID
};
const signature = await wallet.signTypedData(domain, types, message);
Example with ECDSA signature (when signing from the user wallet is not possible):
Make sure to use the correct private key and passphrase from the public key you shared with Kulipa.
const crypto = require('crypto');
const canonicalize = require('canonicalize');
const payload = {
primaryType: 'Authenticate',
message: {
id: '3ds-e259ee22-f857-444d-9c38-e0bf2ee669e3'
}
};
const canonicalizedPayload = canonicalize(payload);
const sign = crypto.createSign('SHA256');
sign.update(canonicalizedPayload);
sign.end();
const signature = sign.sign({
key: privateKey, // your private key
format: 'pem',
type: 'pkcs8',
passphrase: 'your-passphrase',
}, 'hex');
Example with Solana kit:
const keys = await generateKeyPair();
const address = await getAddressFromPublicKey(keys.publicKey);
const validTypedData = {
cardAuthenticationId: '3ds-e259ee22-f857-444d-9c38-e0bf2ee669e3',
purpose: 'Card 3DS Authentication',
organisation: 'Kulipa',
userId: 'usr-595f5f00-8471-4676-90b0-61a9277e2f78',
nonce: getUnixTime(new Date()), // we validate the nonce is within +/- 5 minutes of the current timestamp to allow for server drift
wallet: address,
};
const structCodec = getStructCodec([
['organisation', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['userId', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['purpose', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['nonce', getU64Codec()],
['wallet', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
['cardAuthenticationId', addCodecSizePrefix(getUtf8Codec(), getU32Codec())],
]);
const encodedData = structCodec.encode(validTypedData);
const signedBytes = await signBytes(keys.privateKey, encodedData);
const signature = getBase58Decoder().decode(signedBytes);
console.log(signature);
For more details about the 3DS process, see the 3DS documentation.
2043DS authentication successfully accepted
400The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
401Although the HTTP standard specifies "unauthorized", semantically this response means "unauthenticated". That is, the client must authenticate itself to get the requested response.
403The client does not have access rights to the content; that is, it is unauthorized, so the server is refusing to give the requested resource. Unlike 401 Unauthorized, the client's identity is known to the server.
429The user has sent too many requests in a given amount of time ("rate limiting").
500The server has encountered a situation it does not know how to handle.
502This error response means that the server, while working as a gateway to get a response needed to handle the request, got an invalid response.
503The server is not ready to handle the request. Common causes are a server that is down for maintenance or that is overloaded.
504This error response is given when the server is acting as a gateway and cannot get a response in time.
