Skip to main content

Authentication

Learn how to authenticate with AceSteps APIs.

Overview

AceSteps uses different authentication methods for backend API and smart contract interactions.

Backend API Authentication

Farcaster Auth

The primary authentication method uses Farcaster:

import { sdk } from '@farcaster/miniapp-sdk';

// 1. Get Farcaster context
const context = await sdk.context;

// 2. Request auth token
const authResponse = await fetch('/api/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fid: context.user.fid,
// Include signature for verification
})
});

const { token } = await authResponse.json();

// 3. Use token in subsequent requests
const response = await fetch('/api/music/generate', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
// ...
});

JWT Token Format

The authentication token is a JWT containing:

{
"sub": "12345", // Farcaster FID
"address": "0x...", // Connected wallet address
"iat": 1704067200, // Issued at
"exp": 1704153600 // Expiration (24 hours)
}

Token Refresh

Tokens expire after 24 hours. Refresh before expiration:

// Check if token is about to expire
const tokenData = JSON.parse(atob(token.split('.')[1]));
const expiresIn = tokenData.exp * 1000 - Date.now();

if (expiresIn < 3600000) { // Less than 1 hour
const refreshResponse = await fetch('/api/auth/refresh', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
const { token: newToken } = await refreshResponse.json();
}

Smart Contract Authentication

Wallet Connection

Smart contract interactions require wallet connection:

import { useAccount, useConnect } from 'wagmi';

function ConnectWallet() {
const { address, isConnected } = useAccount();
const { connect, connectors } = useConnect();

if (isConnected) {
return <div>Connected: {address}</div>;
}

return (
<button onClick={() => connect({ connector: connectors[0] })}>
Connect Wallet
</button>
);
}

Signature-Based Minting

NFT minting requires a platform signature:

// 1. Generate music and get signature from backend
const { metadataUri, audioHash, signature } = await fetch('/api/nft/prepare', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ generationId })
}).then(r => r.json());

// 2. Call contract with signature
const tx = await songNFTContract.mint(
metadataUri,
audioHash,
signature
);

await tx.wait();

Signature Verification

The contract verifies signatures:

// Contract-side verification
function mint(
string calldata metadataURI,
bytes32 audioHash,
bytes calldata signature
) external {
bytes32 hash = keccak256(abi.encodePacked(
msg.sender,
metadataURI,
audioHash
));

address signer = ECDSA.recover(
hash.toEthSignedMessageHash(),
signature
);

require(signer == platformSigner, "Invalid signature");
require(!usedSignatures[signature], "Signature used");

usedSignatures[signature] = true;
// ... mint NFT
}

Error Handling

Authentication Errors

Error CodeDescriptionSolution
AUTH_REQUIREDNo token providedInclude Authorization header
TOKEN_EXPIREDJWT has expiredRefresh or re-authenticate
TOKEN_INVALIDMalformed or tampered tokenRe-authenticate
SIGNATURE_INVALIDContract signature invalidRequest new signature from backend
SIGNATURE_USEDReplay attack detectedRequest new signature

Example Error Response

{
"error": "TOKEN_EXPIRED",
"message": "Authentication token has expired",
"details": {
"expiredAt": "2024-01-01T12:00:00Z"
}
}

Security Best Practices

  1. Never expose private keys - Keep platform signing key secure
  2. Use HTTPS only - All API calls over TLS
  3. Validate signatures - Always verify on-chain
  4. Short token lifetimes - 24 hour JWT expiration
  5. Prevent replay attacks - Track used signatures