A React TypeScript project demonstrating the Legalesign Document Viewer component with secure token management.
Additional details and description available here: https://docs.legalesign.com/components/document-viewer#installation
This demo provides two authentication methods to obtain access tokens for the Document Viewer. Choose the method that best fits your use case:
Best for: Simple integrations, server-to-server communication, API-first applications
- Direct authentication using your Legalesign API key
- Minimal setup and configuration
- Single credential to manage
- Ideal for backend services and automation
Best for: Enterprise applications, user-based authentication, AWS-integrated systems
- Secure Remote Password (SRP) protocol
- User-based authentication with username/password
- JWT token-based authorization
- Integrates with AWS Cognito User Pool
- Perfect for users that already have a session open
- Supports MFA and advanced user management
Both methods are production-ready and secure. The browser never sees your credentials—only short-lived access tokens.
This demo uses a client-server architecture to securely handle authentication:
┌─────────────┐ ┌─────────────┐ ┌──────────────────┐
│ Browser │────────▶│ Server │────────▶│ Legalesign API │
│ (React) │ │ (Express) │ │ (GraphQL) │
└─────────────┘ └─────────────┘ └──────────────────┘
│ │ │
│ 1. Request token │ │
│───────────────────────▶│ │
│ │ 2. GraphQL mutation │
│ │ with API key / SRP │
│ │─────────────────────────▶│
│ │ │
│ │ 3. Access token │
│ │◀─────────────────────────│
│ 4. Return token │ │
│◀───────────────────────│ │
│ │ │
│ 5. Use token with Document Viewer │
│──────────────────────────────────────────────────▶│
Security: Your credentials (API key or SRP password) never leave the server. The browser only receives short-lived access tokens.
Option A: API Key (server.js)
- Stores API key in environment variables
- Makes GraphQL mutation with API key in Authorization header
- Returns only the access token to browser
Option B: Cognito SRP (server-cognito.js)
- Stores Cognito credentials in environment variables
- Authenticates with AWS Cognito using SRP protocol
- Obtains JWT token from Cognito User Pool
- Uses JWT token in Authorization header for GraphQL requests
- Returns only the access token to browser
- Fetches access token from the backend server on mount
- Passes the token to the LsDocumentViewer component
- No sensitive credentials in the browser
npm installYou must choose either API Key authentication or Cognito authentication.
-
Copy the API key environment template:
cp .env.example .env
-
Edit
.envand add your Legalesign API key:LEGALESIGN_API_KEY=your_api_key_here
-
Where to get API key: Log into your Legalesign account → Settings → API → Generate API key
-
Copy the Cognito environment template:
cp .env.example .env
-
Edit
.envand add your AWS Cognito credentials (the first two fields are LEGALESIGN_CLIENT_ID & LEGALESIGN_USER_POOL_ID):LEGALESIGN_USERNAME=your_username_here LEGALESIGN_PASSWORD=your_password_here LEGALESIGN_CLIENT_ID=CONTACTUS LEGALESIGN_USER_POOL_ID=CONTACTUS
-
Where to get Cognito credentials: AWS Console
Start the server that matches your chosen authentication method:
For API Key authentication:
npm run serverFor Cognito SRP authentication:
npm run server:cognitoThe server will start on http://localhost:3001
In another terminal:
npm run devThe app will start on http://localhost:5173
-
Browser requests token: When the React app loads, it calls
POST /api/get-tokenon the Express server -
Server makes GraphQL mutation: The Express server uses the API key to request an access token from Legalesign's GraphQL endpoint:
mutation { generateComponentToken(input: { component: LS_DOCUMENT_VIEWER }) {
token expiresIn } }
With headers:
POST https://graphql.uk.legalesign.com/graphql Content-Type: application/json Authorization: <your_api_key>
3. **Legalesign returns token**: The GraphQL API validates the API key and returns:
```json
{
"data": {
"generateComponentToken": {
"token": "eyJraWQiOiJBTkJIeT...",
"expiresIn": 3600
}
}
}
-
Server forwards token: The Express server sends only the access token to the browser
-
Browser uses token: The React app passes the token to
LsDocumentViewer, which uses it to authenticate GraphQL requests
-
Browser requests token: When the React app loads, it calls
POST /api/get-tokenon the Express server -
Server authenticates with Cognito: The Express server uses AWS Legalesign SRP (Secure Remote Password) protocol:
- Initiates authentication with username/password
- Performs cryptographic challenge-response (SRP)
- Receives a token from the Legalesign platform.
-
Legalesign returns token: The GraphQL API validates the JWT and returns the access token
-
Server forwards token: The Express server sends only the access token to the browser
-
Browser uses token: The React app passes the token to
LsDocumentViewer
The viewer is configured with:
- Template ID:
yourTemplateId - Mode:
editor(full-featured template editing) - Endpoint:
https://graphql.uk.legalesign.com/graphql
Change the mode prop in src/App.tsx:
editor- Full template creation and editingcompose- Streamlined signature box placement (requires you to pass in JSON of the recipients you want to send to. See: https://docs.legalesign.com/components/document-viewer#recipients)preview- Read-only document preview
✅ DO:
- Store credentials in environment variables (never in code)
- Use server-side token generation (never expose credentials to browser)
- Add
.envto.gitignore - Implement token refresh logic for production
- Use HTTPS in production
- Choose the authentication method that fits your security requirements
❌ DON'T:
- Hardcode API keys or passwords in source code
- Expose credentials to the browser
- Commit
.envfiles to version control - Use this exact setup in production without additional security measures
Use API Key if:
- You're building a server-to-server integration
- You want the simplest setup
- You don't need user-level authentication
- You're building internal tools or automation
- You've generated an API key for your account
- Want to use restricted keys for specific Legalesign components
Use Legalesign SRP / Username & Password if:
- You want to quickly demo a component, or you're a developer wanting the quickest first project
- You're building a user-facing application
- You need user-level authentication and authorization
- You've integrated your ID/user accounts with the Legalesign platform
- You need MFA or advanced security features
- You want to track which user performed which action
Important: The Express/Node.js server in this demo is just one example. Any server-side technology can call the Legalesign GraphQL API as long as it can:
- Make HTTPS requests
- Set HTTP headers (Authorization, Content-Type)
- Parse JSON responses
Common alternatives:
- Python (Flask, FastAPI, Django) - Most popular alternative to Node.js
- Java (Spring Boot) - Enterprise standard
- C# (.NET Core) - Microsoft ecosystem
- Go - High-performance services
- Ruby (Rails, Sinatra) - Rapid development
- PHP (Laravel) - Web hosting standard
The authentication logic is identical regardless of language—make a GraphQL mutation with your credentials in the Authorization header.
Important: This Express server setup is designed for local development only. AWS Amplify Hosting serves static files and cannot run the Express server.
For production deployment on AWS Amplify, choose one of these options:
Create a Lambda function to handle the token exchange:
// amplify/functions/get-token/handler.ts
export const handler = async (event) => {
const response = await fetch('https://graphql.uk.legalesign.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': process.env.LEGALESIGN_API_KEY,
},
body: JSON.stringify({
query: `
mutation {
generateComponentToken(input: { component: LS_DOCUMENT_VIEWER }) {
token
expiresIn
}
}
`,
}),
});
const data = await response.json();
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
body: JSON.stringify({
accessToken: data.data.generateComponentToken.token,
expiresIn: data.data.generateComponentToken.expiresIn
}),
};
};Deploy the Lambda function and API Gateway endpoint, then update your React app to call the Lambda endpoint instead of http://localhost:3001.
Deploy the Express server to a separate AWS service:
- AWS Elastic Beanstalk - Managed platform for Node.js applications
- AWS ECS/Fargate - Containerized deployment
- AWS App Runner - Simplified container deployment
Update the React app's fetch URL to point to your deployed backend.
If using Next.js or a similar SSR framework, use Amplify Gen 2 which supports server-side rendering:
// app/api/get-token/route.ts (Next.js)
export async function POST() {
const response = await fetch('https://graphql.uk.legalesign.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': process.env.LEGALESIGN_API_KEY!,
},
body: JSON.stringify({
query: `
mutation {
generateComponentToken(input: { component: LS_DOCUMENT_VIEWER }) {
token
expiresIn
}
}
`,
}),
});
const data = await response.json();
return Response.json({
accessToken: data.data.generateComponentToken.token,
expiresIn: data.data.generateComponentToken.expiresIn
});
}For production deployments:
- Implement token caching and refresh logic
- Add rate limiting to the token endpoint
- Use environment-specific configuration
- Implement proper error handling and logging
- Add authentication/authorization for your backend API
- Use HTTPS for all communications