workspaces-issues icon indicating copy to clipboard operation
workspaces-issues copied to clipboard

[Bug] - Unauthorized Session Access Error When Embedding Kasm Workspaces

Open saad-obenan opened this issue 4 months ago • 1 comments

Environment

Kasm Workspaces Version: 1.14.0 Browser: Chrome/Firefox/Safari (latest versions) Deployment: Self-hosted Kasm server with Nginx reverse proxy Authentication: Using API key/secret for session creation Connection Method: iframe embedding with ngrok for HTTPS Issue Description When attempting to embed a Kasm Workspaces session inside an iframe, I consistently receive the error message:

"Unauthorized Session Access. If problem persists try logging back in to the application"

This occurs despite following the official documentation for iframe embedding and implementing all recommended security configurations.

Steps to Reproduce

Created a FastAPI backend that communicates with Kasm API to create sessions Set up an Nginx reverse proxy with SSL termination Created a simple HTML page with an iframe to load the Kasm session Used ngrok to provide a secure HTTPS domain for both the application and Kasm Set the Kasm Auth Domain to match the ngrok domain in Kasm settings Constructed the iframe URL with session ID and token parameters

Code Implementation Backend (Python/FastAPI)

    @app.post("/v2/omnilink/session")
    async def create_omnilink_session():
    try:
    api_endpoint = f"{KASM_SERVER_URL}/api/public/get_images"
    payload = {
        "api_key": KASM_API_KEY,
        "api_key_secret": KASM_API_SECRET
    }

    http_session = requests.Session()
    response = http_session.post(api_endpoint, json=payload, verify=False)
    response.raise_for_status()
    
    # Find Chrome image
    images_data = response.json()
    target_image = next((img for img in images_data.get("images", []) 
                        if "chrome" in img.get("friendly_name", "").lower()), None)
    
    if not target_image:
        raise HTTPException(status_code=404, detail="Chrome image not found")
        
    image_id = target_image.get("image_id")
    
    # Create a new Kasm session
    api_endpoint = f"{KASM_SERVER_URL}/api/public/create_session"
    payload = {
        "api_key": KASM_API_KEY,
        "api_key_secret": KASM_API_SECRET,
        "user_id": "f9d8616e-ea32-4ea2-9a65-0f7670f1e915",
        "image_id": image_id,
        "enable_sharing": True,
        "environment": { "ENV_VAR": "value" }
    }
    
    response = http_session.post(api_endpoint, json=payload, verify=False)
    response.raise_for_status()
    
    kasm_data = response.json()
    session_url = kasm_data.get("kasm_url")

    return {
        "kasm_url": session_url,
        "kasm_id": kasm_data.get("kasm_id"),
        "session_token": kasm_data.get("session_token"),
        "share_id": kasm_data.get("share_id")
    }
except Exception as e:
    raise HTTPException(status_code=500, detail=str(e))

Frontend (HTML/JavaScript)

`

   <iframe id="kasm_iframe" width="1024" height="768"></iframe>
  <script>
    connectBtn.onclick = async () => {
      try {
        const response = await fetch("/v2/omnilink/session", { method: "POST" });
        const sessionData = await response.json();
        
        const kasmId = sessionData.kasm_id;
        const sessionToken = sessionData.session_token;
        
        if (!kasmId || !sessionToken) {
          throw new Error("Missing session data from API response");
        }
        
        // Using the ngrok domain with session ID and token
        const fullUrl = `https://e0af58a3d138.ngrok-free.app/#/session/${kasmId}?token=${sessionToken}`;
        kasm_iframe.src = fullUrl;
      } catch (error) {
        console.error(error);
      }
    };
  </script>

`

Nginx Configuration

  `server {
      listen 8088;
      server_name e0af58a3d138.ngrok-free.app;
  
      # Proxy API requests to backend
      location /v2/omnilink/session {
          proxy_pass http://host.docker.internal:8000/v2/omnilink/session;
      }
  
      # Proxy all other requests to Kasm
      location / {
          proxy_pass https://108.129.120.54:8080;
          proxy_ssl_verify off;
          
          # WebSocket support
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";
          
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          
          # Cookie handling
          proxy_cookie_path / "/; SameSite=None; Secure";
      }
  }`

Troubleshooting Steps Already Tried

✅ Set Kasm Auth Domain to match the ngrok domain (e0af58a3d138.ngrok-free.app) ✅ Cleared browser cache and cookies ✅ Verified the session is created successfully (API returns valid session data) ✅ Confirmed the iframe URL includes both session ID and token parameters ✅ Ensured all requests go through the same domain (ngrok) ✅ Verified SSL is working correctly (no certificate errors) ✅ Added proper cookie handling in Nginx configuration ✅ Tried different browsers (Chrome, Firefox, Safari)

Console Errors

Constants.js:37 Cannot parse user info. No user info stored a_ @ Constants.js:37

Additional Information

The session is created successfully on the Kasm server (verified in logs) The iframe loads initially but shows the "Unauthorized Session Access" error The error occurs consistently across different browsers and sessions The Auth Domain setting in Kasm is set exactly to e0af58a3d138.ngrok-free.app (no protocol, no trailing slash)

Questions

  1. Is there a specific format required for the Auth Domain setting?
  2. Is there a way to debug the authentication process to see exactly why it's failing?
  3. Is there a known issue with the "Cannot parse user info" error in the console?

saad-obenan avatar Aug 05 '25 10:08 saad-obenan