Tutorial · Cloud Storage

Connect Amazon S3 to webQsee with per-user credentials

The complete walk-through for teams: every member gets their own AWS IAM user, their own private folder and a shared space, with permission checks enforced at the storage layer, not just in the UI.

Estimated time: 25–35 min · Last reviewed: 2026-05 · Difficulty: Intermediate

Before you start
  • webQsee installed in Chrome or Edge, get it here
  • Active webQsee Professional or Team plan (Cloud Gallery requires this), see pricing
  • A working email address that you can use to sign up for AWS (a credit card is required by AWS even on the free tier, but webQsee usage typically stays well within free limits for small teams)
  • Your webQsee team set up with the team members you want to onboard, if you don't have one yet, see Create and manage multiple webQsee teams

webQsee uploads captured screenshots, screencasts, behavior reports and snapshots directly to your S3 bucket from the browser extension. Our servers only see metadata. That means you stay in control of the data, and you pay AWS, not us, for storage.

This tutorial sets up the multi-user pattern: one shared webqsee-admin policy for power users, plus a restricted webqsee-user policy that uses an IAM tag (webqsee-user-id) so each team member can only write into their own /private/<their-id>/ folder while still sharing access to /shared/ and read-only /protected/.

Looking for the lighter setup (one shared IAM user, no per-user separation)? See Simplified S3 integration instead.

1. Create an AWS account

  1. Open the AWS sign-up page

    Navigate to aws.amazon.com/free and click Create an AWS Account. Use a long-lived email address, this becomes the AWS root account, which you should rarely use afterwards.

    Sanity check: You should land on the "Sign up for AWS" form with fields for Root user email address and AWS account name.
  2. Complete the AWS sign-up flow

    Fill in contact details, choose Personal or Business, enter payment info, confirm the SMS/voice verification code and pick the Basic Support, Free plan. AWS may take a minute or two to activate the account.

    Sanity check: You see "Congratulations" / "Your AWS account is ready" and the button "Go to the AWS Management Console" is enabled. Sign in as the root user.
  3. Set the console region

    In the top-right of the AWS Console choose the region you want your data to live in, for European GDPR-friendly storage that's typically eu-central-1 (Frankfurt). Write the region code down; you'll need it later.

    Sanity check: The region selector in the top-right shows your chosen region (e.g. "Europe (Frankfurt) eu-central-1").
    AWS console region selector dropdown open, showing several regions with flag glyphs and code suffixes (e.g. eu-central-1, us-east-2, ap-northeast-1); the Europe (Frankfurt) row hovered in cyan

2. Create the webqsee-admin IAM policy

This policy grants the operations webQsee needs to upload, read, replace and delete artifacts across all three folder areas of the bucket: /shared/, /protected/ and /private/. Admins also need ListBucket on those prefixes.

  1. Open the IAM Policies page

    In the AWS Console search bar type IAM and open the IAM service. In the left sidebar click Policies, then Create policy. Switch to the JSON tab.

    Sanity check: You're in the IAM → Policies → Create policy editor and see an empty JSON editor on the right.
  2. Paste the admin policy JSON

    Replace the placeholder content with the JSON below. Note: this policy targets a bucket literally named webqsee, if you intend to use a different bucket name, replace every occurrence of webqsee in the ARNs below first.

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "AdminListBucket",
          "Effect": "Allow",
          "Action": [
            "s3:ListBucket",
            "s3:GetBucketLocation",
            "s3:GetBucketCors"
          ],
          "Resource": "arn:aws:s3:::webqsee",
          "Condition": {
            "StringLike": {
              "s3:prefix": [
                "shared/*",
                "protected/*",
                "private/*",
                "shared/",
                "protected/",
                "private/",
                ""
              ]
            }
          }
        },
        {
          "Sid": "AdminFullObjectAccess",
          "Effect": "Allow",
          "Action": [
            "s3:GetObject",
            "s3:GetObjectTagging",
            "s3:PutObject",
            "s3:PutObjectTagging",
            "s3:DeleteObject",
            "s3:AbortMultipartUpload",
            "s3:ListMultipartUploadParts"
          ],
          "Resource": [
            "arn:aws:s3:::webqsee/shared/*",
            "arn:aws:s3:::webqsee/protected/*",
            "arn:aws:s3:::webqsee/private/*"
          ]
        }
      ]
    }
    Sanity check: The JSON editor shows no red error indicators. Click Next: AWS validates the JSON and shows a "Review and create" page summarising the permissions (S3: GetObject, PutObject, DeleteObject, ListBucket, etc.).
  3. Name and save the policy

    Set Policy name to webqsee-admin (exact spelling matters; you'll match it later). Description: "webQsee Cloud Gallery, admin/full access to /shared, /protected, /private". Click Create policy.

    Sanity check: The policies list now contains webqsee-admin with a "Customer managed" type label.

3. Create the webqsee-user IAM policy

This is the per-user policy. The clever bit is the IAM tag condition aws:PrincipalTag/webqsee-user-id: it injects the value of the tag webqsee-user-id that you attach to each IAM user into the policy at evaluation time. That means every user is granted write access only to the prefix /private/<their-own-id>/, even though all users share the same policy document.

  1. Create another policy

    IAM → Policies → Create policyJSON tab.

    Sanity check: You're back at the empty JSON editor, a fresh policy, not editing the admin one.
  2. Paste the user policy JSON

    This grants read/write on /shared/*, read/write on /private/${aws:PrincipalTag/webqsee-user-id}/*, and read-only on /protected/*.

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "UserListBucketScoped",
          "Effect": "Allow",
          "Action": [
            "s3:ListBucket",
            "s3:GetBucketLocation",
            "s3:GetBucketCors"
          ],
          "Resource": "arn:aws:s3:::webqsee",
          "Condition": {
            "StringLike": {
              "s3:prefix": [
                "shared/*",
                "shared/",
                "protected/*",
                "protected/",
                "private/${aws:PrincipalTag/webqsee-user-id}/*",
                "private/${aws:PrincipalTag/webqsee-user-id}/",
                ""
              ]
            }
          }
        },
        {
          "Sid": "UserSharedReadWrite",
          "Effect": "Allow",
          "Action": [
            "s3:GetObject",
            "s3:GetObjectTagging",
            "s3:PutObject",
            "s3:PutObjectTagging",
            "s3:DeleteObject",
            "s3:AbortMultipartUpload",
            "s3:ListMultipartUploadParts"
          ],
          "Resource": "arn:aws:s3:::webqsee/shared/*"
        },
        {
          "Sid": "UserPrivateReadWrite",
          "Effect": "Allow",
          "Action": [
            "s3:GetObject",
            "s3:GetObjectTagging",
            "s3:PutObject",
            "s3:PutObjectTagging",
            "s3:DeleteObject",
            "s3:AbortMultipartUpload",
            "s3:ListMultipartUploadParts"
          ],
          "Resource": "arn:aws:s3:::webqsee/private/${aws:PrincipalTag/webqsee-user-id}/*"
        },
        {
          "Sid": "UserProtectedReadOnly",
          "Effect": "Allow",
          "Action": [
            "s3:GetObject",
            "s3:GetObjectTagging"
          ],
          "Resource": "arn:aws:s3:::webqsee/protected/*"
        }
      ]
    }
    Sanity check: The JSON validates without errors. Click Next and the review page lists four statements (UserListBucketScoped, UserSharedReadWrite, UserPrivateReadWrite, UserProtectedReadOnly).
  3. Name and save

    Name: webqsee-user. Description: "webQsee Cloud Gallery, per-user policy, write to own /private prefix, R/W /shared, R/O /protected". Click Create policy.

    Sanity check: Both webqsee-admin and webqsee-user now appear in the Customer managed policies list.

4. Create the two IAM groups

  1. Create group webqsee-admin

    IAM → User groupsCreate group. Name: webqsee-admin. In Attach permissions policies search for webqsee-admin and tick it. Click Create group.

    Sanity check: The group webqsee-admin appears in the User groups list with 1 attached policy and 0 users.
  2. Create group webqsee-user

    Repeat: Create group, name webqsee-user, attach the webqsee-user policy, save.

    Sanity check: Both groups now show in the list, each with 1 attached policy.

5. Add one IAM user per team member

  1. Look up each member's webQsee user ID

    In webQsee click the toolbar icon and open the main window. Go to Settings → Current User and Team. The field User ID shows a UUID-like string (e.g. u_8f3c9a1e). Each member should send their own ID to you, or look at the team roster which lists them.

    Sanity check: You have a list of (display name → webQsee user ID) pairs for every team member who needs access.
    webQsee Current User and Team settings panel with three read-only fields: Email, webQsee user ID (UUID placeholder with a copy icon next to it), and Active team
  2. Create the first IAM user

    IAM → UsersCreate user. Username: pick something recognisable, e.g. alice-webqsee. Do not tick "Provide user access to the AWS Management Console", webQsee needs programmatic API access only. Click Next.

    Sanity check: You're on step 2 ("Set permissions") of the Create user wizard.
  3. Add the user to a group

    Choose Add user to group. Tick webqsee-user for a regular team member, or webqsee-admin if this person should manage the bucket. Click Next.

    Sanity check: The "Permissions summary" shows the group membership and the policy it carries.
  4. Tag the user with their webQsee ID critical

    On the Tags (optional) step add a tag with Key webqsee-user-id and Value equal to that person's webQsee user ID (e.g. u_8f3c9a1e). The case must match exactly, the webqsee-user policy uses this tag to scope /private/ access. Click NextCreate user.

    Sanity check: Open the new user, switch to the Tags tab: the row "webqsee-user-id = u_..." is present. Without this tag, the user cannot upload anything to their private folder.
  5. Generate the access key

    Open the new user → Security credentials tab → Create access key. Use case: Application running outside AWS (it's a browser extension). Click through, optionally add a description tag.

    Sanity check: AWS shows you the Access key ID (starts with AKIA) and the Secret access key exactly once.
  6. Securely deliver the credentials

    Download the CSV or copy both values into your password manager. The secret cannot be recovered, if you lose it you have to delete the key and create a new one. Send each user their own pair using an end-to-end-encrypted channel (1Password sharing, Bitwarden Send, signed PGP email, etc.).

    Sanity check: The recipient confirms they have received both the Access Key ID and the Secret. Close the AWS dialog only after confirmation.
  7. Repeat for every team member

    One IAM user per person, with the matching webqsee-user-id tag and one access key each.

    Sanity check: IAM → Users now shows every team member as an Active user, each in either webqsee-admin or webqsee-user group, each with the right tag.

6. Create the S3 bucket

  1. Open the S3 console

    Console search → S3Create bucket. Confirm in the top-right that you're still in the region you decided on (e.g. eu-central-1).

    Sanity check: You're on the "Create bucket" form and the region matches your choice.
  2. Name the bucket

    Use the name your policies expect: webqsee. If that global name is already taken, use something unique (e.g. webqsee-acme-2026) and update the ARNs in both IAM policies to match.

    Sanity check: The "Bucket name" field shows green / valid, no "already exists" or "invalid name" warning.
  3. Block all public access

    Leave Block all public access ticked. webQsee never makes objects public; it uses authenticated requests from the extension using each user's access key.

    Sanity check: All four "Block public access" sub-checkboxes are ticked.
  4. Versioning, encryption, ACLs

    Bucket versioning: Disabled (versioning makes deletions soft, not what you want for screen-recordings that may be confidential). Default encryption: SSE-S3 (AES-256), free and recommended. Object ownership: ACLs disabled.

    Sanity check: The review summary shows Versioning: Disabled, Encryption: SSE-S3, Public access: Blocked.
  5. Create the bucket

    Click Create bucket.

    Sanity check: A green toast says "Successfully created bucket 'webqsee'" and the bucket appears in the S3 bucket list with your region next to it.

7. Configure CORS on the bucket

The webQsee extension is loaded from chrome-extension:// and view.webqsee.com, so the bucket must permit cross-origin uploads and reads from these origins.

  1. Open the bucket's Permissions tab

    S3 → click your bucket → Permissions tab → scroll to Cross-origin resource sharing (CORS)Edit.

    Sanity check: A JSON editor opens, currently empty or with placeholder content.
  2. Paste the CORS config
    [
      {
        "AllowedOrigins": [
          "https://view.webqsee.com",
          "https://app.webqsee.com",
          "chrome-extension://*"
        ],
        "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
        "AllowedHeaders": ["*"],
        "ExposeHeaders": ["ETag", "x-amz-request-id", "x-amz-version-id"],
        "MaxAgeSeconds": 3000
      }
    ]

    Click Save changes.

    Sanity check: A green toast "Successfully edited CORS" appears, and the saved CORS block shows the AllowedOrigins above.

8. Configure webQsee Cloud Gallery

  1. Open Cloud Gallery settings

    In webQsee open the main window (Alt+A or via the popup), go to Gallery tab, click Cloud Gallery, then Edit Settings.

    Sanity check: A settings panel opens with two sections: Team-wide and Personal.
  2. Set the team-wide values (admin only)

    These are saved on the webQsee server for the whole team:

    • Storage Type: S3 compatible storage, KEY-Auth
    • Authentication mode: Per-user credentials (multi-user)
    • Bucket name: webqsee (or your custom name)
    • Connection settings:
      {
        "region": "eu-central-1"
      }

    Click Save team settings.

    Sanity check: A toast "Team cloud settings saved" appears; the panel now reads "Awaiting your personal credentials".
  3. Enter your personal credentials

    In the Personal section paste your own Access Key ID and Secret access key. They are stored encrypted inside the extension only, not transmitted to webQsee servers. Click Save personal credentials.

    Sanity check: A green indicator "Cloud Storage connected ✓" appears at the top of the Cloud Gallery panel.
  4. Ask every team member to do the same

    They only need to enter their personal Access Key ID and Secret, the team-wide settings come down automatically.

    Sanity check: Each member sees "Cloud Storage connected ✓" in their own webQsee installation.
Final sanity check

Open webQsee → Gallery → Cloud Gallery. You should see "Cloud Storage connected ✓". Now record a quick screenshot, right-click it in the local gallery and choose Upload to Cloud → Private. Within a few seconds it should appear under the Cloud Gallery's Private tab.

If you get a 403 in the browser console: re-check that the bucket name in your IAM policy ARNs matches the actual bucket name (step 6 vs step 2/3), and confirm the IAM user has the webqsee-user-id tag (step 5). If you get a CORS error, re-apply step 7.

Troubleshooting

  • CORS error in browser console ("No 'Access-Control-Allow-Origin' header is present"): the bucket's CORS configuration is missing or wasn't saved. Re-apply step 7 and click Save changes; AWS sometimes silently rejects malformed JSON.
  • 403 Forbidden on PUT to /private/…: the IAM user is missing the webqsee-user-id tag, or the tag value does not match the path the extension is uploading to. Re-check step 5 step 4 and ensure the tag equals the webQsee user ID exactly (case-sensitive).
  • 403 Forbidden on any PUT: the policy ARN does not match the bucket name. The policy uses arn:aws:s3:::webqsee/… but the bucket might actually be called webqsee-acme-2026. Edit the customer-managed policies and update all ARNs.
  • SignatureDoesNotMatch: the secret access key was copied incorrectly (an extra space, missing character) or expired. Generate a new key in IAM and re-enter it in webQsee.
  • NoSuchBucket: the bucket name in webQsee's team-wide settings has a typo, or the bucket lives in a different region than the region field in the Connection settings JSON.
  • RequestTimeTooSkewed: the machine clock is more than 15 minutes off UTC. Re-sync the OS clock.
  • Listing works but downloads of /protected/ 403: the user is in the webqsee-user group, which has read-only on /protected/. That's correct, uploads to /protected/ are reserved for admins.

Related tutorials

Simplified S3 integration

One shared IAM user for solo users and small teams, less granular but quicker to set up.

Open tutorial

Migrate from simplified to multi-user

Already running on the simple setup? This guide shows the safe transition path.

Open tutorial

One bucket, multiple teams

Share a single bucket across several webQsee teams using prefix-based isolation.

Open tutorial

One bucket per team

The stricter alternative, harder isolation boundaries at the cost of more buckets to manage.

Open tutorial

Stop guessing. Start webQseeing.

Add webQsee to Chrome or Edge in one click. Most features are free, forever, no signup needed. Upgrade only if your team needs cloud sharing, S3 storage and Pro-grade tooling.

Works in Chrome 103+, Edge 103+ and most Chromium-based browsers. Install instructions.