Skip to main content

Guest & logged-in users

Web publishers have a problem mobile apps do not: most visitors are anonymous. The widget handles this with a guest state, and a defined path for converting a guest into a signed-in customer who keeps the reward they earned.

The two states

GuestLogged in
virtualId"guest"Your identifier for the customer
authTokenNot requiredRequired
RewardEarned, held pendingClaimable

The guest flow

When virtualId is "guest", the customer is treated as non-logged-in.

The documented behaviour:

  • From the trigger event, run the banner flow as normal.
  • The customer should be redirected to the publisher login page to claim.
  • authToken is not required while they are a guest.

After they successfully sign in:

  • authToken is required — obtain it from the token API.
  • Re-trigger the flow, passing the updated virtualId.
  • Include all required parameters: virtualId, authToken, eventType, deviceType, additionalParams.
The re-trigger is not optional

Without it, the customer signs in and the reward they were promised does not appear. This is the single most common failure in a web widget integration — the login redirect is implemented, the return path is not.

Implementing the return

Your login flow needs to know it was triggered by a reward claim, and re-trigger on return.

// Before redirecting to login, remember why
sessionStorage.setItem('cheggout_pending_claim', 'true');
window.location.href = '/login?return=' + encodeURIComponent(window.location.pathname);

// After login completes, on return
if (sessionStorage.getItem('cheggout_pending_claim') === 'true') {
sessionStorage.removeItem('cheggout_pending_claim');

const virtualId = getYourUserId();
const authToken = await fetchCheggoutToken(); // from your backend

window.CheggoutRewards.bannerTriggerEvent(virtualId, authToken, 'article_read', 'web', {});
}

Also call checkPendingRewardEvent on your landing and home pages immediately after login. It catches customers who signed in through a different route and still have something unclaimed.

The token API

The authToken is minted from your server, never in the browser — minting requires your private key.

POST /api/GetAuthToken

Authenticated with the signed envelope.

BDATA payload
{
"virtualId": "pub_8f14e45fceea167a",
"channelName": "YOURCHANNEL"
}
Decoded response
{
"token": "REPLACE_WITH_TOKEN",
"expireDate": "REPLACE_WITH_EXPIRY"
}

The token is used as the bearer credential for the reward APIs the widget calls on the customer's behalf.

Validity

The token is valid for one hour. After it expires, obtain a new one by calling the same token API from the server before making further requests.

That one-hour figure is documented for the widget token specifically. Other token lifetimes vary by channel — see Tokens.

Never mint tokens in the browser

Minting requires your RSA private key. Expose a small endpoint on your own backend that your page calls to obtain a token for the current session — the key stays server-side, and the browser only ever sees the short-lived result.

// Your own endpoint, not Cheggout's
async function fetchCheggoutToken() {
const res = await fetch('/api/cheggout/token', {credentials: 'include'});
const {token} = await res.json();
return token;
}

Your endpoint authenticates the customer with your own session, resolves their virtualId, and mints the Cheggout token server-side. Cache it per user for its lifetime rather than minting per page view.

Host

TBD GetAuthToken is published against more than one host family across Cheggout's documents. Confirm the host that applies to your channel during onboarding rather than copying one from another integration. See Environments and GetAuthToken.

Guest identification

The guest state uses the literal value "guest" as virtualId.

Do not construct guest-like identifiers

Send exactly "guest" for anonymous visitors. Values such as guest_12345 are not the documented guest marker, and how the platform classifies them is unspecified. If you need to distinguish anonymous sessions on your own side, track that in your analytics rather than in virtualId.

Privacy on the web

Browser storage is readable by every script on the page, and the widget keeps interaction state there. Keep your third-party script surface tight on pages where the widget runs, and never place a token anywhere longer-lived than the session needs.

Your virtualId should be pseudonymous on the web for exactly the same reason it is in an app — it travels to Cheggout, and on the web it also sits in the browser.

Next