# Session Token

> How an embedded Shoplazza App authenticates with session tokens: the JWT anatomy, its one-minute lifetime, and how to verify the signature on your backend.

[Embedded apps](/docs/app/getting-started/app-types#embedded-apps-method) in the Shoplazza admin authenticate using OAuth and session tokens. This guide is for developing embedded apps.

## How session tokens work

A session token, also known as a [JSON web token (JWT)](https://jwt.io), lets your app authenticate the requests that it makes between the client side and your app's backend. The session token also contains information about the merchant who's currently using your embedded app.

## Authentication flow using a session token

When your [embedded app](/docs/app/getting-started/app-types#embedded-apps-method) first loads, it's unauthenticated and serves up the frontend code for your app. The app renders a user interface skeleton or loading screen to the user.

After the frontend code has loaded, the app calls a [Shoplazza App Bridge action](/docs/app/developer-tools/app-bridge/actions/) to get the session token. Your app includes the session token in an authorization header when it makes any HTTPS requests to its backend.

![](https://cnres.appracle.com/8633e4b77dfc8a6d37968580568ef363.png "截屏2021-12-27 下午4.37.34.png")

## Request flow using a session token

The session token is signed using the shared secret between your app and Shoplazza so that your backend can verify if the request is valid.

![](https://cnres.appracle.com/0ed2a5b4fd5a64fb867a4ed3aac3caec.png "截屏2021-12-28 下午1.25.44.png")

## Lifetime of a session token

The lifetime of a session token is one minute. Session tokens must be fetched using Shoplazza App Bridge on each request to ensure that stale tokens aren't used.

## Anatomy of a session token

A session token consists of a header, payload, and signature. For an interactive example, refer to [JWT.io](https://jwt.io), where you can experiment with setting different values for each section. Shoplazza recommends that you use a test app's credentials when testing on JWT.io.

**Header**\
The values in the header are constant and never change.

```json
{
  "alg": "HS256",
  "typ": "JWT"
}
```

* alg: The algorithm used to encode the JWT.
* typ: The (type) header parameter used by session token to declare the media type. 

**Payload**

```json
{
  "iss": "<shop-name.myshoplaza.com/admin>",
  "dest": "<shop-name.myshoplaza.com>",
  "aud": "<app client id>",
  "sub": "<user ID>",
  "exp": "<time in seconds>",
  "nbf": "<time in seconds>",
  "iat": "<time in seconds>",
  "jti": "<random UUID>",
  "sid": "<session ID>",
  "locale": "zh-CN",
  "account":"test@shoplazza.com"
}
```

* iss: The shop's admin domain.
* dest: The shop's domain.
* aud: The API key of the receiving app.
* sub: The user that the session token is intended for.
* exp: The time (in seconds) when the session token expires.
* nbf: The time (in seconds) when the session token becomes active.
* iat: The time (in seconds) when the session token was issued.
* jti: A secure random UUID.
* sid: A unique session ID per user and app.
* locale: The shop’s locale (language and region), e.g., zh-CN for Chinese.
* account: The user login account.

**Example payload** 

```json
{
  "locale": "zh-CN",
  "account": "test@shoplazza.com",
  "dest": "test.myshoplaza.com",
  "sid": "MTY0MDIyMzE5MHxRaHMzanN1OF9leGdWQTNYZmdqS2tvcnQ0UXpmVlhrZVlhZlJSSG1URTBnOUY4WFNVdl9BVWVmNHozbkVnYU5yc3NwRG9MZFptSGs9fPCmLb7qbttCuZl79rEcRKho9lRqTLZsvs_OESW0um8I",
  "aud": "825a8255676252ee1053073b2b42528c763fd011972ad2803036aea89882920c",
  "exp": 1640331670,
  "jti": "1cf4b3dd-6ccc-4978-9c5a-ad9cee17d4a7",
  "iat": 1640331610,
  "iss": "https://test.myshoplaza.com/admin",
  "nbf": 1640331610,
  "sub": "dafd283d-1274-4412-b86d-21a68ab1172f"
}
```

## Verifying a session token on your backend

Your backend must verify the session token on every request before trusting it. Verification uses the same client secret (`CLIENT_SECRET`) that signs the token.

1. Read the token from the `Authorization: Bearer <token>` header.
2. Verify the signature and the `exp` expiry with `CLIENT_SECRET` using the `HS256` algorithm.
3. Read the store and user from the verified payload — never from a request parameter.

Example (Node.js, `jsonwebtoken`):

```javascript
const jwt = require("jsonwebtoken");

function verifySessionToken(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    return res.status(401).json({ error: "Missing session token" });
  }

  const token = authHeader.replace("Bearer ", "");

  try {
    // jwt.verify checks the signature and the `exp` expiry
    const decoded = jwt.verify(token, CLIENT_SECRET, { algorithms: ["HS256"] });
    req.session = {
      shop: decoded.dest, // shop domain — trusted
      userId: decoded.sub, // user ID
      locale: decoded.locale,
      account: decoded.account,
    };
    next();
  } catch (err) {
    return res.status(401).json({ error: "Invalid session token" });
  }
}
```

:::note
Resolve the store from the verified `dest` claim, not from a `shop` URL parameter. A URL parameter can be forged; a verified token cannot.
:::

For an end-to-end walkthrough that uses this middleware, see [Develop an embedded app](/docs/app/getting-started/create-public-app/embed-in-admin).
