beginner quickstartintegrationsdk

Integrator Quickstart

Get from zero to first successful authentication in 5 minutes

Integrator Quickstart

You want to add hardware-backed authentication to your app. This guide gets you to your first successful auth in about 5 minutes.

We’ll use the Node SDK — the Go SDK works the same way, just swap the imports.

[!NOTE] Pre-release build: Sigil Auth is not yet published to GitHub or npm. This guide assumes you’re working from the local monorepo at /Volumes/Expansion/src/sigilauth/. Replace paths as needed for your environment.

What You’ll Need

  • Docker installed
  • Node 18+ or Go 1.21+
  • Access to the Sigil Auth source code
  • 10 minutes

Step 1: Start the Sigil service

Run the full stack with docker-compose. This gives you the auth server, push relay, and PostgreSQL database.

If you’re working from the published repo (once available):

git clone https://github.com/sigilauth/server.git
cd server
make up

If you’re working from the local development tree:

cd /Volumes/Expansion/src/sigilauth/server
make up

The service boots in init mode. You’ll see:

INFO: Sigil Auth starting in init mode
INFO: Visit https://localhost:8443/init to complete setup

Open https://localhost:8443/init in your browser. Your browser will warn about the self-signed certificate — that’s expected in development. Click through.

The wizard shows you a 24-word mnemonic. Write it down. You’ll need it if you restart the container.

Click “Continue” and the service generates its keypair. You’ll see the server’s pictogram — five emoji that identify this server. That’s it. The server is operational.

Step 2: Get an API key

The init wizard gives you a test API key that looks like:

sgk_test_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2

Copy it. Never commit this to git.

Set it in your environment:

export SIGIL_API_KEY='sgk_test_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2'

Step 3: Install the SDK

[!NOTE] The SDK is not yet published to npm or as a standalone Go module. Use the local path from your monorepo.

Node:

# Once published:
npm install @sigilauth/sdk

# For now, use local path:
npm install file:../server/sdk-node

Go:

# Once published:
go get github.com/sigilauth/server/sdk-go

# For now, use local replace directive in go.mod:
replace github.com/sigilauth/server/sdk-go => ../server/sdk-go

Step 4: Create your first challenge

Here’s the complete flow. A challenge is a request for the user to prove they control their device by signing with their private key.

Node:

import { SigilAuth } from '@sigilauth/sdk';

const sigil = new SigilAuth({
  serviceUrl: 'https://localhost:8443'
});

// Create challenge for a device
const challenge = await sigil.auth.createChallenge({
  fingerprint: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2',
  device_public_key: 'Ag8xYzI3ZWRkNDUzYmNlYzVmMTJjNmI5MzA4OGY0...',
  action: {
    type: 'step_up',
    description: 'Add security key',
    params: { key_name: "Sarah's YubiKey" }
  }
});

console.log('Challenge created:', challenge.challenge_id);
console.log('Pictogram:', challenge.pictogram);
console.log('Speakable:', challenge.pictogram_speakable);

// Push notification sent to device automatically
// Poll for result
const result = await sigil.auth.awaitResult(challenge.challenge_id, {
  timeout: 60000,  // 60 seconds
  pollInterval: 2000  // check every 2 seconds
});

if (result.status === 'verified') {
  console.log('User approved! Signature verified.');
} else if (result.status === 'rejected') {
  console.log('User denied the request.');
} else {
  console.log('Challenge expired.');
}

Go:

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/sigilauth/server/sdk-go"
)

func main() {
	client, err := sigilauth.New(sigilauth.Config{
		ServiceURL:  "https://localhost:8443",
		APIKey:      os.Getenv("SIGIL_API_KEY"),
		HTTPTimeout: 30 * time.Second,
	})
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	challenge, err := client.Auth.CreateChallenge(ctx, &sigilauth.ChallengeRequest{
		Fingerprint:     "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
		DevicePublicKey: "Ag8xYzI3ZWRkNDUzYmNlYzVmMTJjNmI5MzA4OGY0...",
		Action: sigilauth.Action{
			Type:        "step_up",
			Description: "Add security key",
			Params:      map[string]string{"key_name": "Sarah's YubiKey"},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Challenge created: %s\n", challenge.ChallengeID)
	fmt.Printf("Pictogram: %v\n", challenge.Pictogram)

	// Poll for result
	result, err := client.Auth.AwaitResult(ctx, challenge.ChallengeID, &sigilauth.AwaitOptions{
		PollInterval: 1000, // ms
		MaxAttempts:  30,
	})
	if err != nil {
		log.Fatal(err)
	}

	switch result.Status {
	case "verified":
		fmt.Println("User approved! Signature verified.")
	case "rejected":
		fmt.Println("User denied the request.")
	case "expired":
		fmt.Println("Challenge expired.")
	}
}

Where’s the device fingerprint coming from?

In the example above, fingerprint and device_public_key are placeholders. In your real app, these come from device registration.

When a user pairs their phone with your app:

  1. The mobile app generates a keypair in the device’s secure hardware (Secure Enclave on iOS, StrongBox on Android)
  2. The app sends the public key to your backend
  3. Your backend stores user_id → device_public_key in your database
  4. The device fingerprint is SHA256(device_public_key) — Sigil’s SDK calculates this for you, or you can compute it yourself

Check the App Onboarding Guide for the full pairing flow, or see the self-hosting guide for how to wire up your app’s backend.

What happens on the device?

When you call createChallenge, Sigil:

  1. Generates a cryptographic challenge (random bytes)
  2. Signs it with the server’s private key
  3. Sends a push notification to the device via APNs (iOS) or FCM (Android)

The device:

  1. Receives the push
  2. Verifies the server’s signature
  3. Shows the action to the user (“Approve: Add security key”)
  4. Prompts for biometric (Face ID, fingerprint, etc.)
  5. Signs the challenge with the device’s private key
  6. Sends the signed response back to Sigil

Sigil verifies the device’s signature against the public key you provided and marks the challenge as verified or rejected.

Next Steps

That’s it. You’ve created your first challenge and seen how the SDK works. From here, you can wire this into your app’s authentication flow, configure webhooks for async notification, or set up multi-party approval for destructive actions.