@siws/core

Core library for Sign In With Solana -- message building, parsing, and Ed25519 signature verification.

Implements the SIWS specification -- message construction, parsing, validation, and signature verification.

SiwsMessage

Immutable representation of a SIWS message. Validates all fields on construction.

import { SiwsMessage } from '@siws/core'
 
// Create from fields
const message = new SiwsMessage({
  domain: 'example.com',
  address: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU',
  uri: 'https://example.com/auth',
  version: '1',
  nonce: 'k8sj2n4b',
  issuedAt: '2024-01-15T12:00:00Z',
  cluster: 'mainnet-beta',
  statement: 'Sign in to Example',
})
 
// Parse from string
const parsed = SiwsMessage.parse(rawMessageString)
 
// Serialize to string
const str = message.toMessage()
 
// Extract fields
const fields = message.toFields()

Methods

MethodReturnsDescription
static parse(message: string)SiwsMessageParse and validate a raw message string
toMessage()stringSerialize to the canonical string format
toFields()SiwsMessageFieldsExtract fields as a plain object

SiwsMessageBuilder

Fluent builder for constructing SiwsMessage instances. Accepts Date objects for timestamp fields.

import { SiwsMessageBuilder, generateNonce } from '@siws/core'
 
const message = new SiwsMessageBuilder()
  .domain('example.com')
  .address(walletAddress)
  .uri('https://example.com/auth')
  .nonce(generateNonce())
  .issuedAt(new Date())
  .expirationTime(new Date(Date.now() + 10 * 60 * 1000))
  .cluster('mainnet-beta')
  .statement('Sign in to Example')
  .addResource('https://example.com/api/profile')
  .build()

Builder Methods

MethodParameterDescription
domain(domain)stringRequesting domain
address(address)stringSolana wallet address
uri(uri)stringAuthentication URI
nonce(nonce)stringReplay-protection nonce
issuedAt(issuedAt)string | DateMessage creation time
statement(statement)stringHuman-readable statement
expirationTime(time)string | DateMessage expiration
notBefore(time)string | DateEarliest validity time
requestId(id)stringApplication-specific request ID
cluster(cluster)ClusterSolana cluster
resources(resources)string[]Resource URIs
addResource(resource)stringAppend a resource URI
build()--Validate and return SiwsMessage

verifySignature

Verify a signature against a SIWS message.

import { verifySignature } from '@siws/core'
 
const result = await verifySignature(
  messageString,  // raw message string or SiwsMessage
  signature,      // Base58-encoded Ed25519 signature
  publicKey,      // optional -- defaults to address in message
  {
    domain: 'example.com',  // optional domain check
    nonce: expectedNonce,    // optional nonce check
    time: new Date(),        // optional time override
  }
)
 
if (result.success) {
  console.log('Authenticated:', result.data.address)
} else {
  console.error('Failed:', result.error.code, result.error.message)
}

Parameters

ParameterTypeDescription
messagestring | SiwsMessageThe message to verify
signaturestringBase58-encoded Ed25519 signature (64 bytes)
publicKeystring?Base58-encoded public key. Defaults to the address field in the message
optionsVerificationOptions?Optional domain, nonce, and time checks

VerificationResult

interface VerificationResult {
  success: boolean
  data?: SiwsMessageFields  // parsed fields on success
  error?: SiwsError          // error details on failure
}

generateNonce

Generate a cryptographically random Base58 nonce.

import { generateNonce } from '@siws/core'
 
const nonce = generateNonce()     // 16 characters (default)
const long = generateNonce(32)    // 32 characters

Uses crypto.getRandomValues with rejection sampling for uniform distribution across the Base58 alphabet.

Types

SiwsMessageFields

interface SiwsMessageFields {
  domain: string
  address: string
  uri: string
  version: string         // always '1'
  nonce: string
  issuedAt: string        // ISO 8601
  cluster: Cluster
  statement?: string
  expirationTime?: string // ISO 8601
  notBefore?: string      // ISO 8601
  requestId?: string
  resources?: string[]
}

Cluster

type Cluster = 'mainnet-beta' | 'devnet' | 'testnet'

SiwsError

class SiwsError extends Error {
  readonly code: SiwsErrorCode
  readonly expected?: string
  readonly received?: string
}

SiwsErrorCode

All error codes:

CodeWhen
INVALID_DOMAINDomain format validation fails
INVALID_ADDRESSNot a valid Base58 Solana address
INVALID_URIURI format validation fails
INVALID_VERSIONVersion is not '1'
INVALID_NONCENonce too short or not alphanumeric
INVALID_TIMESTAMPNot a valid ISO 8601 datetime
INVALID_STATEMENTContains newlines or non-printable chars
INVALID_CLUSTERNot a recognized cluster value
INVALID_RESOURCEResource URI format invalid
INVALID_SIGNATURESignature is not valid Base58 or wrong length
INVALID_REQUEST_IDContains non-printable characters
MESSAGE_EXPIREDCurrent time exceeds expirationTime
MESSAGE_NOT_YET_VALIDCurrent time precedes notBefore
DOMAIN_MISMATCHMessage domain doesn't match expected
NONCE_MISMATCHMessage nonce doesn't match expected
PARSE_ERRORMessage string cannot be parsed
MISSING_REQUIRED_FIELDA required field is missing
SIGNATURE_VERIFICATION_FAILEDEd25519 verification returned false