Authentication in Webviews
The host app authenticates the user with the same JWT used for the Lynes Loyalty API. Lynes hosts the Webview; your backend signs tokens, your app sends them on the first load.
JWT signing: JWT Authentication.
What your app must do
- Sign a JWT for the logged-in user (
sub= your user ID). - Open the Webview URL with
Authorization: Bearer <JWT>on the initial HTTP request. - Refresh the JWT before reopening or reloading the Webview if it expired.
That is the complete native auth integration.
What the hosted Webview does internally
Your app Hosted Webview Lynes API
| | |
| GET /… + Bearer JWT | |
|--------------------------->| |
| | auth-token cookie (5 min) |
| | POST /api/token-exchange |
| | POST /auth/token + JWT |
| |<-----------------------------|
| | session-token cookie |
| UI (userId = sub) | |
|<---------------------------| |Middleware
If the first request includes Authorization: Bearer …, the Webview stores it in:
| Cookie | TTL | Role |
|---|---|---|
auth-token | 5 min | Bridge token for client-side exchange |
auth-token-timestamp | 5 min | Freshness metadata |
Token exchange
POST {WEBVIEW_URL}/api/token-exchange
Authorization: Bearer <JWT>
Content-Type: application/json
{ "token": "<JWT without Bearer prefix>" }The Webview backend calls:
POST {API_BASE_URL}/auth/token
Authorization: Bearer <JWT>
Content-Type: application/json
{ "token": "<JWT without Bearer prefix>" }On success:
- HttpOnly cookie
session-token(server-side calls) - Client storage
sessionToken(session restore in the browser)
userId in the UI is always the session JWT claim sub.
Session lifetime
- Session cookie: typically 1 hour, or until
expiresAtfrom the API if sooner. - Client checks expiry about every 30 seconds.
- Expired or failed auth may navigate to your configured back callback.
Required JWT claims
| Claim | Role |
|---|---|
sub | User ID (must match IDs used in Lynes events) |
iss | Your tenant ID |
aud | Your project ID |
iat | Issued at (unix seconds) |
exp | Expiry (unix seconds) |
Anti-patterns
| Do not | Reason |
|---|---|
| Put JWT in query string | Leaks in logs and referrers |
Pass userId as query param | Ignored |
| Put API secret in the mobile app | Sign JWTs only on your server |
| Skip JWT on first load | No user session |
Verify JWT (debug)
GET {WEBVIEW_URL}/api/auth
Authorization: Bearer <JWT>Returns decodedFields.sub, exp, etc.
REST API vs. Webview
| REST API | Webview | |
|---|---|---|
| Token | JWT per request | JWT on first load, then internal session |
| Signing | API secret | Same API secret |
| User ID | sub | sub after exchange |