Skip to content

Auth Guard Module

The Auth Guard module allows your Crowdin app to implement additional authentication and authorization checks during the user login flow. This module is executed after standard authentication (password, MFA, device verification) but before granting access to the organization.

  • Type: auth-guard
  • Scope: Organization
  • Available in: Crowdin Enterprise
  • Compliance Checks: Enforce organization-specific security policies
  • Multi-factor Authorization: Add custom authorization steps beyond standard MFA

The Auth Guard module supports three verification types:

Server-to-server verification without user interaction.

Use when:

  • Verification can be done automatically (country check, the user has an assigned task)
  • No user input needed
  • Fast response time (< 10 seconds)

Redirects user to an external page for verification.

Use when:

  • User interaction required (accept terms, solve captcha)
  • Need full page control
  • Validation with external authorization server

Displays verification page in an iframe within Crowdin.

Use when:

  • Want to keep user within Crowdin interface
  • Need custom UI but don’t want full redirect
  • Building interactive verification

The following examples demonstrate different ways to configure Auth Guard modules. You can customize the implementation based on your specific requirements.

import { createApp } from '@crowdin/app-project-module';
createApp({
identifier: 'my-security-app',
name: 'Security App',
clientId: process.env.CROWDIN_CLIENT_ID,
clientSecret: process.env.CROWDIN_CLIENT_SECRET,
baseUrl: process.env.BASE_URL,
authGuard: {
name: 'Country Whitelist Check',
description: 'Verifies user country',
options: {
type: 'direct',
applyToAdmin: false
},
verify: async ({ userId, organizationId, ipAddress }) => {
// Check country from IP address
const allowed = await checkCountryAllowlist(organizationId, ipAddress);
if (allowed) {
return { success: true };
}
return {
success: false,
message: 'Access denied: Authorization from Russia is forbidden'
};
}
}
});
authGuard: [
{
key: 'country-check',
name: 'Country Whitelist',
options: { type: 'direct' },
verify: async ({ ipAddress, organizationId }) => {
// Country verification logic
return { success: true };
}
},
{
key: 'device-trust',
name: 'Company Device Verification',
options: {
type: 'redirect',
url: '/device-verification'
},
verify: async ({ code, userId }) => {
if (!code) {
return { success: false }; // Trigger redirect
}
// Verify code from redirect
const valid = await verifyDeviceCode(code, userId);
return { success: valid };
}
}
]
authGuard: {
name: 'Company Device Verification',
options: {
type: 'redirect',
url: '/device-verification' // User-facing URL
},
verify: async ({ code, userId }) => {
if (!code) {
// No code yet, user will be redirected
return { success: false };
}
// Verify code and check if device was verified
const deviceVerified = await verifyDeviceCode(code, userId);
if (deviceVerified) {
return { success: true };
}
return {
success: false,
message: 'Device verification failed'
};
}
}

On the redirect page (/device-verification), your application could implement logic such as:

  1. Verifying device fingerprint or certificate
  2. Checking browser version and security settings
  3. Validating company device requirements
  4. Generating a verification code
  5. Redirecting back to Crowdin with the code and state parameter:
    https://accounts.crowdin.com/{domain}/guard/callback?state={STATE}&code={CODE}

The only requirement is to redirect back with the state parameter and either a code (success) or error (failure).

authGuard: {
name: 'Translation Agency Terms',
description: 'Accept translation agency terms to continue',
options: {
type: 'iframe',
url: '/terms-acceptance-iframe'
},
settingsUiModule: {
uiPath: path.join(__dirname, 'public'),
fileName: 'terms-settings.html'
},
verify: async ({ code, userId, context }) => {
if (!code) {
return { success: false };
}
const termsAccepted = await verifyTermsAcceptance(code, userId);
if (termsAccepted) {
return { success: true };
}
return {
success: false,
message: 'Terms must be accepted to continue'
};
}
}
PropertyTypeRequiredDescription
keystringNoUnique identifier (auto-generated if not provided)
namestringNoDisplay name for the verification step
descriptionstringNoDescription shown to users (primarily for iframe type)
optionsobjectNoModule configuration options
options.typestringNoVerification type: direct, redirect, or iframe (default: direct)
options.applyToAdminbooleanNoApply to administrators (default: false, owner always bypassed)
options.urlstringNo*User-facing URL for redirect and iframe types (required for those types)
settingsUiModuleobjectNoUI module for settings configuration
verifyfunctionYesVerification logic function

The verify function receives an object with:

{
client?: Crowdin; // Crowdin API client
context?: CrowdinContextInfo; // JWT context information
userId: number; // User ID
organizationId: number; // Organization ID
ipAddress: string; // User's IP address
moduleKey: string; // Module key identifier
code?: string; // Verification code (for redirect/iframe)
}

Required: The function must return an object with a success boolean property:

{
success: boolean; // Whether verification passed
message?: string; // Optional error/info message
}

Important: Always return HTTP 200. Use success: false to indicate failed verification, not HTTP error codes.

For iframe type, you can create an HTML page that uses the Crowdin Apps SDK. Here’s an example:

<!DOCTYPE html>
<html>
<head>
<title>Translation Agency Terms</title>
<script src="https://cdn.crowdin.com/apps/dist/iframe.js"></script>
</head>
<body>
<h1>Translation Agency Terms of Service</h1>
<div class="terms-content">
<p>Please read and accept our translation agency terms to continue...</p>
<!-- Terms content here -->
</div>
<button id="accept">Accept Terms</button>
<button id="decline">Decline</button>
<script>
const urlParams = new URLSearchParams(window.location.search);
const jwtToken = urlParams.get('jwtToken');
const state = urlParams.get('state');
document.getElementById('accept').addEventListener('click', async () => {
// Generate verification code after user accepts terms
const code = await generateCode(jwtToken, state);
// Send success to Crowdin
AP.verifyAuth({ code: code });
});
document.getElementById('decline').addEventListener('click', () => {
AP.verifyAuth({ error: 'User declined terms' });
});
async function generateCode(token, state) {
const response = await fetch('/api/generate-code', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ state })
});
const data = await response.json();
return data.code;
}
</script>
</body>
</html>

By default, Auth Guard modules are applied to:

  • Regular users: Always
  • Administrators: Only if applyToAdmin: true
  • Organization owner: Never (always bypassed)

These are mandatory security requirements for Auth Guard modules:

  1. Token Validation: JWT tokens are automatically validated by the framework
  2. State Management: The state parameter is managed by Crowdin - you must return it unchanged in callbacks
  3. HTTPS Required: All communication must use HTTPS in production
  4. Timeout Handling:
    • Direct type: Must respond within 10 seconds
    • Redirect/IFrame type: State expires in 5 minutes
    • JWT tokens expire in 15 minutes

These are recommended practices for implementing Auth Guard modules:

  1. Fail Securely: Consider defaulting to denying access if verification cannot be completed
  2. Clear Messages: Provide user-friendly error messages to help users understand what went wrong
  3. Performance: Optimize API response time, especially for direct type (< 10 seconds)
  4. Logging: Consider logging all verification attempts for security auditing
  5. Monitoring: Consider tracking verification success rates and response times

These are platform-level limitations you need to be aware of:

  • Direct type timeout: 10 seconds maximum response time
  • State expiration: 5 minutes for redirect/iframe flows
  • JWT expiration: 15 minutes
  • Code usage: Verification codes should be single-use only
  • Owner bypass: Organization owner always bypasses auth guards