Skip to main content

Custom Backend

Introduction

This guide provides comprehensive instructions for integrating FastAuth into your application using a custom authentication backend. A custom backend integration gives you complete control over the JWT signing and validation process while still benefiting from FastAuth's transaction signing capabilities on the NEAR blockchain.

Custom backend integration is ideal for organizations that need to implement custom authentication logic, integrate with existing authentication systems, or require specific JWT claim structures. Through this integration, your application maintains full ownership of the authentication process while leveraging FastAuth's robust transaction signing infrastructure.

The custom backend approach requires deploying and managing your own JWT verification contract (guard contract) and implementing a custom authentication provider that generates properly formatted JWT tokens for transaction signing.

Requirements

Successfully integrating a custom backend with FastAuth requires three essential components: properly structured JWT tokens with required claims, a deployed guard contract for JWT verification, and registration of your guard in the FastAuth router system.

Specify custom claims

Your custom backend must generate JWT tokens that include specific claims required by the FastAuth system. These claims ensure proper transaction verification and user identification.

Required JWT Claims Structure

interface FastAuthJWTClaims {
// Standard JWT claims
iss: string; // Issuer - your backend domain
sub: string; // Subject - unique user identifier
aud?: string; // Audience (optional)
exp: number; // Expiration timestamp
iat: number; // Issued at timestamp

// FastAuth specific claims
fatxn: number[]; // Transaction payload (binary-encoded)
}

Mandatory Claims

sub (Subject): A unique identifier for the user. This claim is used by FastAuth to derive the user's account path and must remain consistent for the same user across authentication sessions.

fatxn (FastAuth Transaction): The transaction payload that will be signed, encoded as a binary array. This claim contains the actual transaction data that needs to be verified against the signing request.

Additional Custom Claims

You can extend the JWT with additional custom claims for your specific use case:

interface ExtendedFastAuthJWTClaims extends FastAuthJWTClaims {
// Your custom claims
role?: string;
permissions?: string[];
organizationId?: string;
}

Customize your guard contract

The guard contract is responsible for verifying JWT tokens generated by your custom backend. FastAuth provides a template contract that you can customize for your specific verification requirements, such as JWT algorithm, claims, and more.

Using the JWT RS256 Guard Template

info

The JWT RS256 Guard Template is a good starting point for implementing your custom JWT verification logic. It supports only RS256 algorithm, but you can easily extend it or replace it to support other algorithms.

The jwt-rs256-guard example contract provides a foundation for implementing your custom JWT verification logic.

Step 1: Customize the Guard Contract

To add custom claims to the guard contract, you need to extend the CustomClaims struct.

warning

sub and fatxn claims are mandatory and must be present in the JWT token. Removing them will break the guard contract.

#[derive(Serialize, Deserialize)]
pub struct CustomClaims {
/// The subject identifier claim that uniquely identifies the user
pub sub: String,
/// The FastAuth claim that specifies the signed payload
pub fatxn: Vec<u8>,

// Add your custom claims here
pub role: Option<String>,
pub permissions: Option<Vec<String>>,
}

Step 2: Customize Verification Logic

Once you have extended the CustomClaims struct, you can implement your custom verification logic (if needed) in the verify_custom_claims function.

fn verify_custom_claims(&self, jwt_payload: Vec<u8>, sign_payload: Vec<u8>) -> (bool, String) {
let claims: CustomClaims = match serde_json::from_slice(&jwt_payload) {
Ok(claims) => claims,
Err(error) => return (false, error.to_string()),
};

// Verify fatxn claim (mandatory)
if claims.fatxn != sign_payload {
return (false, "Transaction payload mismatch".to_string());
}

// Add your custom verification logic here
if let Some(role) = &claims.role {
if !self.is_valid_role(role) {
return (false, "Invalid user role".to_string());
}
}

// Return success with subject claim
(true, claims.sub)
}

Step 3: Deploy the Guard Contract

Once the contract is customized and tested, you can deploy it to the NEAR blockchain.

warning

We encourage you to deploy a testnet guard contract first to test your custom logic. Once you are satisfied with the results, you can deploy the contract to the mainnet.

Register your guard

After deploying your custom guard contract, you must register it with the FastAuth JWT Guard Router to make it available for transaction verification.

Registration Process

Step 1: Prepare Registration Information

  • Guard Name: A unique identifier for your guard (without the "jwt#" prefix). Must match your issuer JWT claim.
  • Guard Account: The NEAR account ID where your guard contract is deployed.

Step 2: Register Your Guard

near call jwt-guard-router.testnet add_guard '{
"guard_name": "your-custom-backend",
"guard_account": "your-guard.testnet"
}' --accountId your-account.testnet --deposit 1.03

Verification of Registration

Confirm your guard is properly registered:

near view jwt-guard-router.testnet get_guard '{"guard_name": "your-custom-backend"}'

Choose an SDK

FastAuth provides SDKs for integrating custom backend authentication into your applications. Currently, the Browser SDK is the primary option for web-based integrations.

Browser SDK

The FastAuth Browser SDK (@fast-auth/browser) supports custom backend integration through the implementation of a custom provider that communicates with your authentication backend.

Implement your custom backend provider

To be able to use your custom backend solution with any SDK, you must implement the IFastAuthProvider interface that handles communication between the SDK and your authentication system.

import { IFastAuthProvider } from "@fast-auth/browser";
import { SignatureRequest } from "@fast-auth/browser";

interface IFastAuthProvider {
// Authentication state management
isLoggedIn(): Promise<boolean>;
login(...args: any[]): void;
logout(): void;

// Transaction signing requests
requestTransactionSignature(...args: any[]): Promise<void>;
requestDelegateActionSignature(...args: any[]): Promise<void>;

// Signature data retrieval
getSignatureRequest(): Promise<SignatureRequest>;
getPath(): Promise<string>;
}