NanChat Developer Docs

Integrate NanChat into your web app with universal links. No server-side dependency on NanChat. Everything works through public Nano cryptography.

Contents

Send Request

Use a universal link to open NanChat with the recipient address and amount already filled in. The user only needs to confirm the transaction. This works on iOS and Android.

URL format

https://nanchat.com/?uri=<PREFIX>:<ADDRESS>?amount=<RAW_AMOUNT>

Example: send NanUSD

https://nanchat.com/?uri=usd:usd_3i5ychrw4ajk5tj5y9ocwr5t9j3tej7t1bwrkcm6p3p1wsu5ny5q371d3bxh?amount=1000

Send Nano

https://nanchat.com/?uri=nano:nano_1banexkcfuieufzxksfrxqf6xy8e57ry1zdtq9yn7jntzhpwu4pg4hajojmq?amount=1000000000000000000000000000

HTML button

<a href="https://nanchat.com/?uri=nano:nano_3xxx...xxx?amount=1000000000000000000000000000000" style="text-decoration: none;">
  <button style="display: inline-flex; align-items: center; gap: 8px; background: #000000; color: #fff; border: none; padding: 10px 18px; border-radius: 6px; font-size: 15px; font-weight: 600; cursor: pointer;">
    <img src="https://bucket.nanwallet.com/logo/nanchat-pay-icon-01.svg"
         alt="" width="20" height="20">
    Pay with NanChat
  </button>
</a>

Preview:

On mobile, the link opens the NanChat app if installed; otherwise it falls back to the web page where the user can download the app and complete the request.

Login with NanChat

“Login with NanChat” lets a user prove ownership of a Nano address by signing a server-generated nonce. The signature is verified on your backend with the user’s public key. No password, no third-party identity provider.

Flow

  1. Your server generates a random nonce and stores it in the user’s pending session.
  2. You render a Login with NanChat button pointing to a nanauth://sign universal link wrapped by https://nanchat.com/?uri=.
  3. The user opens the link. NanChat prompts them to sign the message.
  4. On success, NanChat calls your url callback with the signature, account, and message.
  5. Your /callback endpoint verifies the signature with nanocurrency-web, checks that the message contains the expected nonce, and issues a session cookie.

1. Build the login link

const nonce = crypto.randomBytes(16).toString('hex'); // store on the server

const message  = `Login to xxxxx.com nonce: ${nonce}`;
const callback = 'https://xxxxx.com/callback';

const nanauth = `nanauth://sign?message=${encodeURIComponent(message)}` +
                `&url=${encodeURIComponent(callback)}`;

const loginUrl = `https://nanchat.com/?uri=${encodeURIComponent(nanauth)}`;

Render it as a button:

<a href="https://nanchat.com/?uri=nanauth%3A%2F%2Fsign%3Fmessage%3DLogin%2520to%2520xxxxx.com%2520nonce%253A%2520NONCE%26url%3Dhttps%253A%252F%252Fxxxxx.com%252Fcallback" style="text-decoration: none;">
  <button style="display: inline-flex; align-items: center; gap: 8px; background: #000000; color: #fff; border: none; padding: 10px 18px; border-radius: 6px; font-size: 15px; font-weight: 600; cursor: pointer;">
    <img src="https://nanchat.com/images/nanchat.svg"
         alt="" width="20" height="20">
    Login with NanChat
  </button>
</a>

Preview:

2. Verify the callback on your server

NanChat POSTs the signature payload to your url as JSON, with account, signature, and message. Derive the public key from the account with tools.addressToPublicKey, then verify the signature with tools.verify from nanocurrency-web:

import { tools } from 'nanocurrency-web';

app.post('/callback', express.json(), (req, res) => {
  const { account, signature, message } = req.body;

  // 1. The message must contain the nonce we issued for this session.
  const expectedNonce = req.session.pendingNonce;
  if (!message || !message.includes(`nonce: ${expectedNonce}`)) {
    return res.status(400).json({ error: 'Invalid or expired nonce' });
  }

  // 2. Derive the public key from the claimed account.
  const publicKey = tools.addressToPublicKey(account);

  // 3. Verify the signature. NanChat signs the message prefixed with
  //    "Signed Message: " (standard Nano signed-message format).
  const valid = tools.verify(publicKey, signature, 'Signed Message: ' + message);
  if (!valid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // 4. Consume the nonce and issue a session cookie.
  req.session.pendingNonce = null;
  req.session.user = { account };
  res.json({ ok: true });
});

Questions or feedback?

Reach out at [email protected] or via NanChat Team on NanChat.