Skip to main content

User Authentication and MFA

Rexec provides multiple layers of authentication to secure your account and terminal sessions.

Authentication Methods

Rexec uses PipeOps for secure OAuth2 authentication with PKCE (Proof Key for Code Exchange). Sign In Flow:
  1. Click Sign In with PipeOps on the landing page
  2. Authorize Rexec to access your PipeOps account
  3. You’ll be redirected back to Rexec with a session token
  4. Your session includes:
    • Access token (JWT) - valid for 1 hour
    • Refresh token - for renewing your session
    • User profile (name, email, subscription status)

Guest Mode

No sign-in required for quick access:
  • Instant access - No account needed
  • Limited resources - Same as free tier (5 containers)
  • Temporary - 50-hour session limit
  • No persistence - Sessions expire and cannot be recovered
To use guest mode, simply click Try Without Sign In on the landing page.

OAuth2 Flow Details

PKCE Challenge

Rexec implements OAuth2 with PKCE for enhanced security:
  1. Client generates code verifier - Random 32-byte string
  2. Creates code challenge - SHA256 hash of verifier
  3. Authorization request - Includes challenge
  4. Token exchange - Provides original verifier for validation
This prevents authorization code interception attacks.

Scopes

Rexec requests the following OAuth scopes:
  • user:read - Access to basic profile information

Token Storage

Tokens are stored in browser localStorage. Clear your browser data to revoke local access.
Security Recommendations:
  • Use private browsing for shared computers
  • Sign out when done on public devices
  • Enable MFA for additional protection

Account-Level MFA

Setting Up TOTP MFA

  1. Navigate to Account → Settings
  2. Find the Multi-Factor Authentication section
  3. Click Enable MFA
  4. Scan the QR code with your authenticator app:
    • Google Authenticator
    • Authy
    • 1Password
    • Any TOTP-compatible app
  5. Enter the 6-digit code to confirm setup
  6. Save your backup codes - 10 one-time codes for account recovery
Backup codes are shown only once. Store them securely (password manager, printed copy, etc.).

Logging In with MFA

Once MFA is enabled:
  1. Sign in with PipeOps OAuth as usual
  2. After OAuth completes, you’ll be prompted for a TOTP code
  3. Enter the 6-digit code from your authenticator app
  4. Click Verify to complete login
Using Backup Codes: If you don’t have access to your authenticator:
  1. Click Use a backup code instead
  2. Enter one of your 10 backup codes (format: XXXX-XXXX)
  3. The code will be consumed and removed from the list
Each backup code can only be used once. You’ll have 9 remaining after using one.

Disabling MFA

  1. Navigate to Account → Settings
  2. Scroll to Multi-Factor Authentication
  3. Click Disable MFA
  4. Enter a TOTP code or backup code to confirm
  5. MFA is removed from your account

Terminal-Level MFA Protection

Add an extra layer of security to individual containers or agents.

Enabling Terminal MFA Lock

From the Dashboard:
  1. Find the container or agent you want to protect
  2. Click the menu → Lock with MFA
  3. Enter your TOTP code to confirm
  4. The terminal is now MFA-protected (shows 🔒 icon)
Via API:
curl -X POST https://rexec.sh/api/security/terminal/<container-id>/mfa-lock \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "123456"}'

Accessing MFA-Locked Terminals

When you try to connect to an MFA-locked terminal:
  1. A modal prompts for your TOTP code
  2. Enter the 6-digit code from your authenticator
  3. Click Verify and Connect
  4. Terminal opens if code is valid
One-time Verification: Each verification is valid for the current terminal session. If you:
  • Close the terminal
  • Refresh the page
  • Open in a new tab
You’ll need to verify again.

Removing Terminal MFA Lock

  1. In the Dashboard, find the locked terminal (🔒 icon)
  2. Click Unlock Terminal
  3. Enter your TOTP code to confirm removal
  4. The terminal is no longer MFA-protected
Via API:
curl -X POST https://rexec.sh/api/security/terminal/<container-id>/mfa-unlock \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "123456"}'

API Authentication

API Tokens

For programmatic access, create long-lived API tokens:
  1. Navigate to Account → API Tokens
  2. Click Generate New Token
  3. Provide details:
    • Name: Descriptive label (e.g., “CI/CD Pipeline”)
    • Scopes: Select permissions (containers, agents, etc.)
    • Expiration: Optional expiry date
  4. Click Create
  5. Copy the token - it won’t be shown again!
Token Format: rexec_<random-string>

Using API Tokens

# Export as environment variable
export REXEC_TOKEN="rexec_your_token_here"

# Use in API requests
curl https://rexec.sh/api/containers \
  -H "Authorization: Bearer $REXEC_TOKEN"

Scopes

Available API token scopes:
  • agent - Connect and manage agents
  • containers - Create and manage containers
  • ssh - Access SSH key management
  • snippets - Manage command snippets
  • recordings - Access session recordings
ScopeReadWriteDelete
agent
containers
ssh
snippets
recordings

Token Management

List Active Tokens:
curl https://rexec.sh/api/tokens \
  -H "Authorization: Bearer $TOKEN"
Revoke a Token:
curl -X DELETE https://rexec.sh/api/tokens/<token-id> \
  -H "Authorization: Bearer $TOKEN"
Best Practices:
  • Use separate tokens for different services
  • Rotate tokens every 90 days
  • Revoke unused tokens immediately
  • Never commit tokens to version control

Session Management

Token Refresh

Access tokens expire after 1 hour. Rexec automatically refreshes them using your refresh token. Manual Refresh (API):
curl -X POST https://rexec.sh/api/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "your-refresh-token"}'
Response:
{
  "access_token": "new-jwt-token",
  "expires_in": 3600
}

Session Duration

Free Tier:
  • OAuth sessions: Until token expires (refresh extends)
  • Guest sessions: 50 hours maximum
  • Container lifetime: 50 hours (without subscription)
Pro/Enterprise:
  • OAuth sessions: Indefinite (with active subscription)
  • No container time limits

Logging Out

From UI:
  1. Click your profile avatar
  2. Select Sign Out
  3. Session is cleared locally
Programmatic:
curl -X POST https://rexec.sh/api/auth/logout \
  -H "Authorization: Bearer $TOKEN"
This invalidates the current session token.

Security Headers

Rexec sets the following security headers:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'

WebSocket Authentication

Terminal Connections

WebSocket connections for terminal access support multiple auth methods: 1. Authorization Header:
const ws = new WebSocket('wss://rexec.sh/api/terminal/<id>');
ws.send(JSON.stringify({
  type: 'auth',
  token: 'Bearer your-jwt-token'
}));
2. Query Parameter:
const ws = new WebSocket('wss://rexec.sh/api/terminal/<id>?token=your-jwt-token');
3. Sec-WebSocket-Protocol:
const ws = new WebSocket('wss://rexec.sh/api/terminal/<id>', ['rexec.token.your-jwt-token']);
Method 3 is recommended for browser-based connections to avoid query parameter exposure in logs.

CORS and Origin Validation

Allowed Origins

Rexec validates WebSocket and API origins: Default Allowed:
  • https://rexec.pipeops.app
  • https://rexec.pipeops.io
  • https://rexec.sh
  • http://localhost:* (development)
Custom Origins: Set ALLOWED_ORIGINS environment variable:
ALLOWED_ORIGINS="https://custom.domain.com,https://another.domain.com"

Blocking Empty Origins

By default, requests without an Origin header are allowed (for CLI/agent clients). To block:
BLOCK_EMPTY_ORIGIN=true
Clients must then send:
curl https://rexec.sh/api/containers \
  -H "Origin: https://rexec.sh" \
  -H "Authorization: Bearer $TOKEN"

Best Practices

Rexec uses PipeOps OAuth, so password security is managed by PipeOps:
  • Use a strong, unique password
  • Enable MFA on your PipeOps account
  • Don’t share OAuth tokens
  • Sign out on shared devices
  • Enable account MFA for all users
  • Use terminal MFA for production containers
  • Print backup codes and store offline
  • Test backup codes before relying on them
  • Regenerate backup codes periodically
  • Rotate API tokens every 90 days
  • Use minimal scopes - only what’s needed
  • Monitor token usage in Account → API Tokens
  • Revoke compromised tokens immediately
  • Use environment variables - never hardcode
  • Always use HTTPS/WSS - no plain HTTP
  • Verify certificates - don’t ignore TLS warnings
  • Use VPN for sensitive operations
  • Avoid public WiFi for critical work

Troubleshooting

Symptoms:
  • Redirect loop
  • “Invalid state” error
  • “Code expired” message
Solutions:
  1. Clear browser cookies and localStorage
  2. Try incognito/private mode
  3. Check system clock is accurate
  4. Verify PipeOps account is active
  5. Contact support if issue persists
Common Issues:
  1. Time Sync: Ensure device clock is accurate (TOTP requires time sync)
    # Check system time
    date
    # Sync with NTP
    sudo ntpdate -s time.nist.gov
    
  2. Wrong Account: Verify you’re using the correct entry in your authenticator
  3. Backup Codes: Try a backup code if authenticator fails
  4. Rate Limiting: Wait 30 seconds between attempts
If you see “Token expired” errors:
  1. Refresh token - Rexec should auto-refresh
  2. Sign out and back in if refresh fails
  3. Check subscription status - expired Pro users revert to free
  4. Verify time sync - JWT validation is time-sensitive
# Test token validity
curl https://rexec.sh/api/auth/validate \
  -H "Authorization: Bearer $TOKEN"
Check:
  1. Token format: Should be Bearer <token> or just <token>
  2. Origin header: Must be allowed origin
  3. Token expiry: Refresh if expired
  4. Firewall: Ensure WSS (port 443) is allowed
Debug:
ws.addEventListener('error', (error) => {
  console.log('WebSocket error:', error);
});

ws.addEventListener('close', (event) => {
  console.log('Close code:', event.code);
  console.log('Close reason:', event.reason);
});
Common Close Codes:
  • 1000 - Normal closure
  • 1008 - Policy violation (auth failed)
  • 1011 - Internal error

Advanced: Self-Hosted Auth

If running a self-hosted Rexec instance, configure OAuth:
# .env
PIPEOPS_OAUTH_BASE_URL=https://your-oauth-provider.com
PIPEOPS_CLIENT_ID=your_client_id
PIPEOPS_REDIRECT_URI=https://your-rexec.com/auth/callback
JWT_SECRET=your-256-bit-secret
STRIPE_WEBHOOK_SECRET=whsec_...
See Self-Hosting Guide for complete setup.

Build docs developers (and LLMs) love