Skip to main content

Sign In With QoreChain: server verification

SIWQ is an EIP-4361-style sign-in message signed by the account's secp256k1 key (ADR-36) and by its ML-DSA-87 key. Format: Sign In With QoreChain v1.

Flow​

  1. The server issues a single-use nonce for the session.
  2. The client builds the message with createSiwqMessage and the wallet signs it with signSiwq (or produces the classical part with signArbitrary and the ML-DSA-87 part with pqSign over siwqPqSignBytes(message)).
  3. The server verifies with verifySiwqWithChain, which checks the message, the nonce, the domain, the times, that the classical key controls the address, and the ML-DSA-87 signature against the key registered on chain for the address (see Verification).

TypeScript​

import { verifySiwqWithChain } from "@qorechain/qoresign/network";

const fields = await verifySiwqWithChain({
rest: "https://api.qore.host",
address: body.address,
message: body.message,
signature: body.signature,
domain: "app.example.com", // the host the request came to
nonce: session.nonce, // single use; delete it now
chainId: "qorechain-vladi",
});
// fields.address is signed in; fields.possessionProven tells whether the key was registered with a proof of possession

Go​

import (
qoresign "github.com/qorechain/qoresign/go"
"github.com/qorechain/qoresign/go/network"
)

// The network helpers are methods on a client.
client := network.NewClient("https://api.qore.host")

var sig qoresign.SiwqSignature
if err := json.Unmarshal(signatureJSON, &sig); err != nil {
return err
}
result, err := client.VerifySiwqWithChain(ctx, address, qoresign.VerifySiwqOptions{
Message: message,
Signature: sig,
Domain: "app.example.com", // the host the request came to
Nonce: issuedNonce, // the nonce your server issued
ChainID: "qorechain-vladi",
})
if qoresign.IsCode(err, qoresign.CodeSiwqExpired) {
// ask the user to sign in again
}
// result.Address signed in; result.PossessionProven reports how the key was registered.

Classical-only accounts​

verifySiwq refuses a sign-in without the ML-DSA-87 part unless requirePq: false is passed. Accept classical-only sign-ins only if your service does not need the post-quantum guarantee.

A runnable server is in examples/siwq-server.