Skip to main content

Overview

Enterprise Feature: The programmatic Link authentication methods documented on this page are part of Enterprise Whitelabel and require approval before use. Contact [email protected] to request access.
The Link module (sdk.link) provides direct authentication capabilities for building custom login experiences. This is intended for approved enterprise partners who need full control over the authentication flow.
For most applications: Use Hosted Mode or Link Embed instead. These provide excellent user experiences and are available immediately without approval.

Session Initialization

initSession(options)

Initialize a new authentication session for OnlyFans login.
options
InitSessionOptions
required
Configuration for the authentication session.
options.mode
'whitelabel'
required
The session mode. Requires Enterprise Whitelabel approval.
options.clientReferenceId
string
required
A unique identifier for this session. Use this to track sessions in your application.
options.webhook
string
Optional webhook URL to receive authentication status updates.
clientSecret
string
The session secret used for subsequent authentication operations.
sessionId
string
Internal session identifier.
// Requires Enterprise Whitelabel approval
const authSession = await sdk.link.initSession({
  mode: "whitelabel",
  clientReferenceId: "user_123_session_" + Date.now(),
  webhook: "https://your-app.com/webhooks/auth" // Optional
})

console.log("Session secret:", authSession.clientSecret)
// Store this secret for subsequent authentication calls
{
  "data": {
    "clientSecret": "cs_1234567890abcdef",
    "sessionId": "session_abcdef123456"
  },
  "error": undefined
}

Login Methods

loginWithCredentials(clientSecret, email, password, proxyOptions?)

Attempt to log in to OnlyFans using email and password credentials.
clientSecret
string
required
The client secret obtained from initSession().
email
string
required
The user’s OnlyFans email address.
password
string
required
The user’s OnlyFans password.
proxyOptions
ProxyOptions
Optional proxy configuration for the login request.
status
'completed' | 'awaiting_2fa' | 'failed'
required
The login attempt status.
data
object
Present when status is ‘completed’. Contains the session data for API calls.
error
string
Present when status is ‘failed’. Contains the error message.
const loginResult = await sdk.link.loginWithCredentials(
  authSession.clientSecret,
  "[email protected]",
  "secure_password_123",
  {
    url: "http://proxy_user:[email protected]:8080" // Optional
  }
)

if (loginResult.status === "completed") {
  const session = loginResult.data.session
  console.log("Login successful!")
  
  // Store session securely for API calls
  await storeUserSession(loginResult.data.user.id, session)
  
} else if (loginResult.status === "awaiting_2fa") {
  console.log("2FA required")
  // Prompt user for 2FA code
  
} else {
  console.error("Login failed:", loginResult.error)
}
{
  "status": "completed",
  "data": {
    "session": {
      "user-id": "123456789",
      "x-bc": "random_browser_context_string",
      "cookie": "session_cookies_here",
      "user-agent": "Mozilla/5.0..."
    },
    "user": {
      "id": "123456789",
      "username": "example_user",
      "name": "Example User",
      "email": "[email protected]"
    }
  }
}
{
  "status": "awaiting_2fa",
  "data": {
    "twoFactorState": {
      "options": ["app", "phone"],
      "phoneLast4": "1234",
      "phoneCode": {
        "checkAttemptsLeft": 2,
        "checkAttemptsLimit": 3,
        "expirationDate": "2025-02-13T07:20:11+00:00",
        "expirationSeconds": 300,
        "lastSentDate": "2025-02-13T07:15:11+00:00",
        "requestAttemptsLeft": 2,
        "requestAttemptsLimit": 3
      }
    }
  }
}

submitTwoFactorCode(clientSecret, code)

Submit a two-factor authentication code when login requires 2FA.
clientSecret
string
required
The client secret from the authentication session.
code
string
required
The 6-digit 2FA code from the user’s authenticator app.
status
'completed' | 'failed'
required
The 2FA verification status.
data
object
Present when status is ‘completed’. Contains session and user data.
error
string
Present when status is ‘failed’. Contains the error message.
// After login returns "awaiting_2fa" status
const twoFactorCode = await getTwoFactorCodeFromUser() // Your UI implementation

const otpResult = await sdk.link.submitTwoFactorCode(
  authSession.clientSecret,
  twoFactorCode
)

if (otpResult.status === "completed") {
  const session = otpResult.data.session
  console.log("2FA verification successful!")
  
  // Store session for API calls
  await storeUserSession(otpResult.data.user.id, session)
  
} else {
  console.error("2FA failed:", otpResult.error)
  // Allow user to retry or go back to password entry
}
{
  "status": "completed",
  "data": {
    "session": {
      "user-id": "123456789",
      "x-bc": "browser_context_after_2fa",
      "cookie": "updated_session_cookies",
      "user-agent": "Mozilla/5.0..."
    },
    "user": {
      "id": "123456789",
      "username": "example_user",
      "name": "Example User",
      "email": "[email protected]"
    }
  }
}

Session Status

getAuthenticationStatus(clientSecret)

Check the current status of an authentication session.
clientSecret
string
required
The client secret from the authentication session.
status
'pending' | 'completed' | 'failed' | 'expired'
required
Current authentication status.
data
object
Present when status is ‘completed’. Contains session and user data.
error
string
Present when status is ‘failed’ or ‘expired’.
const status = await sdk.link.getAuthenticationStatus(
  authSession.clientSecret
)

switch (status.status) {
  case "pending":
    console.log("Authentication still in progress...")
    break
    
  case "completed":
    console.log("Authentication successful!")
    const session = status.data.session
    // Use session for API calls
    break
    
  case "failed":
    console.error("Authentication failed:", status.error)
    break
    
  case "expired":
    console.log("Session expired, need to start over")
    break
}

Complete Authentication Flow

Here’s a complete example showing the full authentication flow with error handling:
Complete Authentication Example
async function authenticateUser(
  email: string,
  password: string,
  getTwoFactorCode: () => Promise<string>
): Promise<{ session: any; user: any } | null> {
  
  try {
    // Step 1: Initialize session
    const authSession = await sdk.link.initSession({
      mode: "whitelabel",
      clientReferenceId: `auth_${Date.now()}_${Math.random()}`
    })
    
    console.log("Session initialized:", authSession.clientSecret)
    
    // Step 2: Attempt login
    const loginResult = await sdk.link.loginWithCredentials(
      authSession.clientSecret,
      email,
      password
    )
    
    if (loginResult.status === "completed") {
      // Login successful without 2FA
      console.log("Login successful!")
      return {
        session: loginResult.data.session,
        user: loginResult.data.user
      }
    }
    
    if (loginResult.status === "awaiting_2fa") {
      // Step 3: Handle 2FA if required
      console.log("2FA required, requesting code...")
      
      const twoFactorCode = await getTwoFactorCode()
      
      const otpResult = await sdk.link.submitTwoFactorCode(
        authSession.clientSecret,
        twoFactorCode
      )
      
      if (otpResult.status === "completed") {
        console.log("2FA verification successful!")
        return {
          session: otpResult.data.session,
          user: otpResult.data.user
        }
      } else {
        console.error("2FA verification failed:", otpResult.error)
        return null
      }
    }
    
    // Login failed
    console.error("Login failed:", loginResult.error)
    return null
    
  } catch (error) {
    console.error("Authentication error:", error)
    return null
  }
}

// Usage
const authResult = await authenticateUser(
  "[email protected]",
  "password123",
  async () => {
    // Your implementation to get 2FA code from user
    return prompt("Enter 2FA code:")
  }
)

if (authResult) {
  console.log("Authenticated as:", authResult.user.username)
  
  // Now you can use the session for API calls
  const { data: profile } = await sdk.self.getMe({
    authentication: { session: authResult.session }
  })
  
  console.log("Profile loaded:", profile)
} else {
  console.log("Authentication failed")
}

Error Handling

Authentication methods can fail for various reasons. Here are common error scenarios:

Invalid Credentials

const loginResult = await sdk.link.loginWithCredentials(
  clientSecret,
  "[email protected]",
  "wrong_password"
)

if (loginResult.status === "failed") {
  console.error("Invalid credentials:", loginResult.error)
  // Show error message to user
}

Account Restrictions

// Some accounts may be restricted or suspended
if (loginResult.status === "failed" && 
    loginResult.error.includes("restricted")) {
  console.log("Account has restrictions")
  // Handle account restriction scenario
}

Network Issues

try {
  const loginResult = await sdk.link.loginWithCredentials(
    clientSecret,
    email,
    password
  )
} catch (error) {
  if (error.message.includes("network") || 
      error.message.includes("timeout")) {
    console.log("Network error, please try again")
    // Implement retry logic
  }
}

Best Practices

Security: Never log or store passwords in plain text. Always use secure storage for session data and implement proper session expiration.
Session Management: Sessions can expire. Implement session validation and refresh logic in your application.
Rate Limiting: Authentication endpoints are rate limited. Don’t attempt too many login requests in quick succession.

Secure Session Storage

// Example secure session storage
import crypto from "crypto"

const ENCRYPTION_KEY = process.env.SESSION_ENCRYPTION_KEY // 32 bytes key

function encryptSession(sessionData: any): string {
  const iv = crypto.randomBytes(16)
  const cipher = crypto.createCipher('aes-256-cbc', ENCRYPTION_KEY)
  let encrypted = cipher.update(JSON.stringify(sessionData), 'utf8', 'hex')
  encrypted += cipher.final('hex')
  return iv.toString('hex') + ':' + encrypted
}

function decryptSession(encryptedData: string): any {
  const [ivHex, encrypted] = encryptedData.split(':')
  const iv = Buffer.from(ivHex, 'hex')
  const decipher = crypto.createDecipher('aes-256-cbc', ENCRYPTION_KEY)
  let decrypted = decipher.update(encrypted, 'hex', 'utf8')
  decrypted += decipher.final('utf8')
  return JSON.parse(decrypted)
}

// Store session securely
async function storeSession(userId: string, session: any) {
  const encrypted = encryptSession(session)
  
  await database.sessions.upsert({
    where: { userId },
    update: { 
      sessionData: encrypted,
      updatedAt: new Date()
    },
    create: {
      userId,
      sessionData: encrypted,
      createdAt: new Date(),
      updatedAt: new Date()
    }
  })
}

Next Steps