# 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.

## Module Type

- **Type:** `auth-guard`
- **Scope:** Organization
- **Available in:** Crowdin Enterprise

## Use Cases

- **Compliance Checks**: Enforce organization-specific security policies
- **Multi-factor Authorization**: Add custom authorization steps beyond standard MFA

## Verification Types

The Auth Guard module supports three verification types:

### 1. Direct Type (Backend-only)

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)

### 2. Redirect Type (External Page)

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

### 3. IFrame Type (Embedded UI)

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

## Configuration Examples

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

### Basic Example (Direct Type)

```ts
import { createApp } from '@crowdin/app-project-module';

// Your own helper that checks the request origin country
async function checkCountryAllowlist(organizationId: number, ipAddress: string): Promise<boolean> {
    // Country lookup logic
    return true;
}

createApp({
    identifier: 'my-security-app',
    name: 'Security App',
    description: 'Security and access control 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: your country is not in the allowlist'
                };
            }
        }
    ]
});
```

### Example: Multiple Auth Guard Modules

```ts
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 };
        }
    }
]
```

### Example: Redirect Type

```ts
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).

### Example: IFrame Type with UI Module

```ts
authGuard: [
    {
        name: 'Translation Agency Terms',
        description: 'Accept translation agency terms to continue',
        options: {
            type: 'iframe',
            url: '/terms-acceptance-iframe'
        },
        settingsUiModule: {
            uiPath: import.meta.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'
            };
        }
    }
]
```

## Module Properties

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `key` | string | No | Unique identifier (auto-generated if not provided) |
| `name` | string | No | Display name for the verification step |
| `description` | string | No | Description shown to users (primarily for iframe type) |
| `options` | object | No | Module configuration options |
| `options.type` | string | No | Verification type: `direct`, `redirect`, or `iframe` (default: `direct`) |
| `options.applyToAdmin` | boolean | No | Apply to administrators (default: `false`, owner always bypassed) |
| `options.url` | string | No* | User-facing URL for `redirect` and `iframe` types (required for those types) |
| `settingsUiModule` | object | No | UI module for settings configuration |
| `verify` | function | Yes | Verification logic function |

## Verify Function Parameters

The `verify` function receives an object with:

```ts
{
    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)
}
```

## Verify Function Response

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

```ts
{
    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.

## IFrame Type Implementation Example

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

```html
<!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>
```

## Role-Based Application

By default, Auth Guard modules are applied to:

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

## Security Requirements

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

## Recommended Best Practices

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

## Platform Limitations

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