Backend Authentication
Guide for setting up a secure backend proxy with Turnkey authentication, focusing on JWT implementation and user data management.
Introduction
When integrating Turnkey into an application with an existing authentication system, you’ll need to establish a secure communication pattern between your frontend, backend, and the Turnkey API. This guide explains how to implement a backend proxy pattern that leverages your existing user authentication while enabling Turnkey operations.
Why Use a Backend Proxy?
There are several benefits to proxying Turnkey requests through your own backend:
- User data: Store and retrieve user data associated with Turnkey sub-organizations
- Metrics and monitoring: Add custom validations, rate limiting, and logging
- Co-signing capabilities: Enable 2/2 signing patterns where your application is a co-signer
JWT Authentication Flow
JSON Web Tokens (JWTs) provide a secure, stateless way to authenticate requests between your frontend and backend. Here’s how to implement a JWT-based flow with Turnkey:
Architecture Overview
Login and Sign Up Flows
The first step in integrating Turnkey with JWT authentication is handling user login and signup. Both processes follow similar patterns but differ in how they establish the user’s identity with Turnkey.
Getting the User’s Sub-organization ID
Before you can authenticate a user with Turnkey, you need their sub-organization ID. There are multiple ways to obtain this:
-
Database Lookup: Query your database using the user’s email or ID
-
Turnkey API Lookup (Alternative) If you don’t have the sub-organization ID stored alongside your user record, you can query Turnkey using the user’s email to find associated sub-organization IDs.
-
JWT Cookie: Extract the user ID from an existing JWT in cookies (for returning users)
If a user has previously logged in, their JWT might be stored in a cookie. You can verify this token and extract the user ID to confirm their identity.
Login Flow
Client-side
Generate a signed Turnkey request
Send to Backend
Forward the signed request
Backend Verification
Process the request and issue JWT
Sign Up Flow
For new users, the flow is similar but involves creating a new sub-organization:
Client-side
Collect user information
Backend Processing
Create sub-organization and issue JWT
Email OTP Authentication Flow
Turnkey allows users to authenticate via email using a secure One-Time Passcode (OTP). Your backend initiates this by calling sendOtp
. Turnkey emails the code to the user. The user enters the code in the frontend, which sends it (along with identifiers) to your backend. Your backend then calls verifyOtp
. Upon successful Turnkey verification, your backend issues its own application JWT to manage the proxied session.
Note: This flow generally assumes the user and their associated Turnkey Sub-Organization already exist, as it’s primarily for authentication rather than initial signup.
Architecture Overview
Implementation Steps
Request OTP
The user provides their email. The frontend gets the Turnkey iframe’s public key (referred to as targetPublicKey
) and sends both to the backend. The backend finds the user’s sub-organization and asks Turnkey to send the OTP email by calling sendOtp
, passing the iframe’s public key as the targetPublicKey
parameter (used by Turnkey for credential encryption upon successful verification).
Verify OTP & Issue Backend JWT
The user submits the received OTP code. The frontend gets the iframe’s public key again (as targetPublicKey
for encryption) and sends the code, email, original otpId
, and targetPublicKey
to the backend. The backend asks Turnkey to verify the code using verifyOtp
. If successful, the backend generates and sets its own application session JWT cookie and returns the authSession
object from Turnkey to the frontend. The frontend then uses this authSession
to establish the Turnkey session within the iframe via loginWithSession
.
This provides a secure authentication flow using Turnkey’s Email OTP service, integrated with your backend’s JWT-based session management and Turnkey’s frontend iframe session management.
Subsequent Authenticated Requests
Once the user is logged in (via passkey, OTP, or other methods) and has a valid JWT, subsequent requests from the frontend to your backend should include this JWT.
Crucial Security Note on Proxied Requests (Read and Write):
When proxying requests to Turnkey through your backend, it’s critical to ensure that the authenticated user (identified by userId
from the JWT) is actually authorized to perform the requested action on the target Turnkey sub-organization (subOrgId
). This applies to both write operations (like signing transactions) and read operations (like fetching balances or activities).
Simply verifying the JWT authenticates the user, but it doesn’t authorize them for a specific sub-organization. Your backend must:
- Verify the JWT to get the authenticated
userId
. - Look up the
subOrgId
(s) associated with thatuserId
in your application’s database. - Compare the
subOrgId
from the incoming request against the one associated with the authenticated user in your database. - Only proceed if the requested
subOrgId
matches the one(s) associated with the authenticated user.
Failure to perform this check, especially on read operations, could allow a user to potentially access information from sub-organizations they do not belong to.
-
Client-side: Include JWT in requests to your backend
-
Backend Verification: Your backend verifies the JWT and processes the request
JWT Implementation
When implementing JWT authentication:
JWT Best Practices
- Keep token expiration times short (15-60 minutes)
- Include only necessary claims in the payload
- Use HTTPS for all communications
- Store tokens securely (use HTTP-only cookies rather than localStorage)
- Implement token refresh mechanisms for long-lived sessions
- Consider using JWTs with signatures (JWS) for enhanced security
User Data Storage and Retrieval
Many applications need to store additional user data and associate it with their Turnkey activities. Here’s how to implement this:
User-Turnkey Association Model
Store a mapping between your application’s user IDs and their corresponding Turnkey organization IDs:
Storing User Data
When a user first authenticates with Turnkey (either by creating a new sub-organization or linking to an existing one):
- Save the Turnkey organization ID in your user database
- Optionally store additional metadata (creation time, recovery options, etc.)
- Set up any necessary hooks or listeners for Turnkey activities
Retrieving User Data
When processing a proxied Turnkey request:
- Extract the user ID from the authenticated JWT
- Look up the associated Turnkey organization ID
- Use this organization ID when forwarding requests to Turnkey
Implementation Examples
Next.js Server Actions
Next.js Server Actions provide a convenient way to implement secure backend operations:
Express.js Example
For standard Node.js applications using Express:
Security Considerations
When implementing this pattern, keep these security considerations in mind:
- API Key Security: Never expose your backend Turnkey API keys to the frontend
- JWT Validation: Thoroughly validate JWTs (signature, expiration, audience, etc.)
- Input Validation: Sanitize and validate all input received from the frontend
- Rate Limiting: Implement rate limiting to prevent abuse
- Monitoring: Set up logging and monitoring for suspicious activities
- Error Handling: Return appropriate error messages without exposing sensitive details
Advanced Topics
Co-signing Transactions
Learn about advanced patterns like multi-signature setups requiring approvals from both the user and the backend.
Was this page helpful?