Sessions
SCAYLE Storefront leverages robust session management and authentication mechanisms to ensure secure and personalized user experiences. This includes supporting user logins, persistent baskets and wishlists, and seamless Single Sign-On (SSO) capabilities via specifiable Identity Provider (IDP).
Maintaining User State via Sessions
Storefront maintains stateful sessions, crucial for user login and the persistence of baskets and wishlists. These sessions are designed to be consistent across various servers, thanks to a shared persistent storage provider.
Session Storage
Session data is stored using a shared persistent and configurable storage provider, abstracted via Unstorage. Configuration is managed via the storefront.storage.session
options.
The in-memory storage provider should only be used for development.
Check Storefront Guide - Storage for detailed information on how to configure the session storage.
For production, a reliable persistent storage solution is essential. Refer to the Storefront Guide - Caching for detailed storage configuration.
Session Cookies
The session ID is stored in a cookie, named $session-${shopId}
by default. The session cookies are HttpOnly
for security, preventing JavaScript access.
- The session cookie is shop-based via its cookie name but applies to all requests, lacking path scope.
- The session cookie's domain is set to the current domain.
Guest and Logged-In Users
Guest users receive a randomly generated UUID as their session ID. Logged-in users have session IDs that combine their user ID with a random UUID.
Session Configuration
Session storage and cookie serialization are configurable through the session
property in the module configuration:
Parameter | Type | Description |
---|---|---|
session.cookieName | string | The name used for the session cookie. Defaults to $session-${shopId} . |
session.sameSite | 'none' | 'lax' | 'strict' | The sameSite policy to use for the session cookie. Defaults to 'lax' . |
session.maxAge | number | The default maxAge (in seconds) set on the session cookie and default TTL for session store. Defaults to 86400 (one day). |
session.secret |
| The secret used for signing session cookies. If an array is passed, the last value is used for signing new cookies, but all values are used to verify cookies. This allows to rotate secrets in case your session secret get's leaked. |
session.domain | string | Controls the domain option on the session cookie |
Configuration can be set globally or per shop to allow for shop-specific behavior, with shop-specific settings taking precedence.
The session.provider
configuration option has been removed with scalye/storefront-nuxt@8.0.0
; use storage.session
instead.
Configuration Example
// (...)
runtimeConfig: {
// (...)
storefront: {
// (...)
session: {
cookieName: '$session',
sameSite: 'none',
maxAge: 2419200, // four weeks in seconds
secret: 'my-secret',
domain: 'my.domain',
},
// (...)
},
// (...)
}
// (...)
User-Bound Sessions
Upon login, the session ID is updated to include the user ID. Existing guest user basket and wishlist items are merged into the logged-in user's data.
RPCContext Session Methods
RPC methods can access and manipulate sessions through RPCContext
:
Method | Description |
---|---|
context.user: ShopUser | Provides access to the user data saved on the session |
context.updateUser: (user: ShopUser) => void | Update the user data saved on the session |
context.destroySession: () => Promise<void> | Destroys the current session, logging out the user in the process |
context.destroySessionsForUserId: (userId: number, sessionsToKeep?: string[]) => Promise<void> | Destroys all sessions for a specific user, except for those listed in sessionsToKeep |
Securing User Access via Authentication:
Storefront supports secure authentication features, including token-based authentication (JWKS), password reset URLs, Identity Providers (IDPs), and app keys for baskets and wishlists. Storefront relies hereby on OAuth to communicate with the SCAYLE Checkout.
Token-Based Authentication (JWKS)
Token-based authentication allows to implement custom login forms instead of relying on forms provided by the Checkout service.
To implement custom login forms, Storefront acts as an OAuth client and uses the Get JSON Web Key Set Authentication API endpoint ({OAUTH_API_HOST}/.well-known/jwks.json
):

Token-based authentication flow

Token verification
Configuration is done via the runtimeConfig.storefront.oauth
property:
Create a new API client using the SCAYLE Panel to get oauth.clientId
and oauth.clientSecret
.
nuxt.config.ts
// (...)
runtimeConfig: {
// (...)
storefront: {
// (...)
oauth: {
apiHost: 'https://{{tenant-space}}.storefront.api.scayle.cloud/v1/',
clientId: '1',
clientSecret: '', // Set in .env NUXT_STOREFRONT_OAUTH_CLIENT_SECRET
},
auth: {
redirect: {
home: '',
logout: ''
}
},
// (...)
},
// (...)
}
// (...)
These configuration values can be overridden via the following environment variables:
OAUTH_API_HOST | URL of the Oauth service |
---|---|
OAUTH_CLIENT_ID | ID that the authorization server assigned to this application |
OAUTH_CLIENT_SECRET | Secret is known only to the application and the authorization server. It is essentially the application’s password. |
Protected RPC
Protected RPCs must verify user access based on the access token and user data.
For this the RPC in question must check if there is a user within the request and the requested data / action can be requested / done by the user. This can be achieved by e.g. matching user IDs within the access token against the user ID within an order.
For user-related actions, like changing the password, this will be handled by the underlying SCAYLE Authentication Service itself.
Password Reset URL
A password reset URL can be manually configured in nuxt.config.ts
, including the hash={hash}
query parameter on a per-shop basis on the runtimeConfig.storefront.shops.{UNIQUE_IDENTIFIER}.auth.resetPasswordUrl
property:
nuxt.config.ts
// (...)
runtimeConfig: {
// (...)
storefront: {
// (...)
shops: {
// (...)
auth: {
resetPasswordUrl: `
https://${baseShopDomain(shop.code)}/${shop.locale}/signin/
`, // 'http://localhost:3000/signin?hash={hash}'
}
},
// (...)
},
// (...)
}
// (...)
The password reset URL should include the query param hash={hash}
. When the password-forgotten
mail is sent, {hash}
will be replaced with an actual hash, which is needed to reset the password.
Identity Providers (IDPs)
Identity Provider (IDP) support enables the use Single Sign-On (SSO). This simplifies the login process by reducing the number of passwords users need to remember, as one set of credentials is used across multiple websites.
The backbone of the Storefront integrated IDP support consists of two functionalities:
- An internal part that runs
useIDP()
composable and generates redirect URLs for all of the configured IDPs. - A
handleIDPLoginCallback
function performs the actual login..
For more information, check the Authentication API Guide.
Configure Identity Provider
The available Identity Providers can be configured in the nuxt.config.ts
, where Google, Facebook, Keycloak, Apple, and Okta are currently supported:
// (...)
runtimeConfig: {
storefront: {
// Global IDP configuration for all shops
idp: {
enabled: true,
idpKeys: ['facebook', 'google'],
idpRedirectURL: 'http://localhost:3000/signin',
},
//...
shops: {
1001: {
// (...)
// Shop-specific overrides for the IDP configuration
idp: {
enabled: true,
idpKeys: ['facebook'],
idpRedirectURL: 'http://localhost:3000/signin',
},
},
// (...)
},
// (...)
},
// (...)
}
// (...)
IDP Redirect URL
Redirects towards the URLs for the respective configured IDPs are handled by the useIDP
composable.
Calling the useIDP
composable checks if IDP is enabled, validates necessary configurations, and returns a list of redirect URLs for each IDP based on your shop configuration as the data
return value:
const idpParams = computed(() => ({
queryParams:
typeof route.query.redirectUrl === 'string'
? { redirectUrl: route.query.redirectUrl }
: undefined,
}))
const idp = useIDP(idpParams)
const externalIDPRedirects = computed(() => {
return idp.data.value && Object.keys(idp.data.value).length > 0
? (idp.data.value as Record<string, string>)
: undefined
})
Login with IDP
Once the user clicks on the IDP button, the user is redirected to the IDP login page. After successful login, the user is redirected to the callback URL. That is the shop URL with the query parameter code
appended to it. This code
is used to generate the access token and refresh token.
const { loginIDP } = useAuthentication('login')
const idpCode = computed(() => {
const code = route.query.code
return typeof code === 'string' ? code : undefined
})
onMounted(async () => {
if (!idpCode.value) {
return
}
await loginIDP(idpCode.value)
})

IDP Authentication flow
App keys for baskets and wishlists
The appKeys
configuration defines how unique keys are generated for baskets and wishlists. These keys are used to create a relation between the user (session) and the respective basket or wishlist.
appKeys
must remain confidential. Exposure may enable unauthorized modifications to customer baskets and wishlists.
wishlistKey
and basketKey
are template strings. When creating the key Storefront Core will replace {shopId}
and {userId}
with the current shopId
and userId
. It will then hash the result with the chosen hash algorithm.
sha256
is recommended as hash algorithm due to increased security, md5
is only supported to provide backward-compatibility. Further usage is discouraged.
// (...)
runtimeConfig: {
storefront: {
// (...)
appKeys: {
wishlistKey: 'wishlist_{shopId}_{userId}',
basketKey: 'basket_{shopId}_{userId}',
hashAlgorithm: 'sha256',
},
// (...)
},
// (...)
},
// (...)
}
// (...)