# Security
At a fundamental level, Shopware PWA serves as a facade for users (customers) to interact with one or multiple backends (merchant services) of which Shopware will be at least one. We can achieve this type of architecture using APIs and following a so-called headless approach. This yields great freedom as to which services and tools can be integrated without affecting others.
However, due to the applications universal (isomorphic) nature and contrary to fully server-side-rendered applications this implies that some type of identification of the client application has to be achieved.
IMPORTANT
It is absolutely crucial for developers to keep in mind, that (universal) frontend applications have the architectural implication of exposing these identifications.
For communication with the Shopware Store API we do this using tokens that identifiy the client application and the user. A different method is used to communicate to the Shopware Admin API. A deeper discussion of that can be found below.
It is the duty of every core maintainer, extension developer, contributor, etc. to be aware of which keys, tokens or access credentials are provided with the application and to make sure that no credentials ever allow for malicious exploitation of sensitive data.
# Authentication and Identification
Shopware provides two API endpoints which are used by the PWA. The Store API and the Admin API. The following explanation shows how each of these APIs are guarded, how Shopware PWA uses them and why we need them.
# Store API
The Store API serves as an abstraction for every storefront in Shopware and can be thought of as the user's API. It allows applications to obtain user views on data like products, categories, carts or conduct user operations like a registration, payment, or login. Fundamentally it requires two tokens to identify a user:
Type | Key | Scope | Function |
---|---|---|---|
HTTP Header | sw-access-key | Global | Identifies the application as a certain sales channel |
HTTP Header | sw-context-token | User | Identifies the user context (login state, cart, selected language) |
As you can see, the purpose of both keys is to identify the given user. Note that there are two "users" here - the customer (identified by sw-context-token
) as well as the client application (identified by sw-access-key
). Both keys are visible to the user (agent) and that's totally fine.
# Access Key
Structurally, this could even be omitted, when we check for the request URL on the backend and associate that with a specific sales channel. However with regard to other applications that don't have such a thing as a "user agent" like native phone apps, we have included this primitive identification. In combination with a URL this also helps preventing cross-site-attacks.
The access key is provided along with your Shopware instance URL within the shopware-pwa.config.js
module.exports = {
shopwareEndpoint: "http://shopware-pwa-api.test",
shopwareAccessToken: "SWSCDE6YASXCB189CNM4PL4CUG",
};
# Context Token
This token protects highly sensitive data and should never be persisted in other places that the user agent. It is either automatically given to any "new" customer or can be obtained by logging in as a customer. It is similar to a Session ID and subject to the same vulnerabilities, such as session hijacking, if exposed in a non-sensitive way.
# Admin API
The Admin API is used to synchronise your PWA project with your Shopware 6 backend. This connection is only required during build time when running one of the following commands. The shopware-pwa
CLI uses it to request resources from Shopware plugins and install them.
$ npx @shopware-pwa/cli@canary init # Initialize a new project
$ yarn shopware-pwa plugins # Refresh plugins from SW instance
The data is transmitted as a .json
file containing plugin configurations and a .zip
file containing plugin resources like .js
or .vue
files or other assets.
IMPORTANT
A runtime connection to the Admin API is not required and Admin API credentials should never find their way out of your build environment or be accessible from the outside.
The Admin API is guarded with the OAuth 2.0 flow. Although it supports multiple grant types, the shopware-pwa
CLI User Password Credentials grant where one has to provide username and password of an admin user. These credentials will then be used to obtain a token that can be used for authentication.
Type | Key | Scope | Function |
---|---|---|---|
HTTP Body | username | Admin User | Identify admin user |
HTTP Body | password | Admin User | Authenticate admin user |
Please check our guide on authentication (opens new window) for the Admin API.
After the build process, there is no connection required anymore, so it is advised to only these credentials when executing the init
or plugins
command:
$ npx shopware-pwa/cli init --ci --username [user] --password [pass]
$ npx shopware-pwa/cli plugins --ci --username [user] --password [pass]
# Context-Awareness new (0.2.0)
As described above, universal
applications may share the data between different server requests. It's very important to keep this in mind. To ensure, that data is always assiociated with the correct invocation we introduced context awareness
for both - client and composables logic.
# Composables
To ensure, that the whole logic is connected, we now require that every composable usage needs to have th Vue
context as its first parameter. It's effortless and straightforward but ensures that all data is secured.
import { useUser } from "@shopware-pwa/composables"
// later in component
setup(props, { root }) {
const { isLoggedIn } = useUser(root) // you're passing root to any composable as a first argument
}
root
is an equivalent of the component context this
, so we're passing our Vue instance. In asyncData and Nuxt plugins, we use app
instead.
import { useUser } from "@shopware-pwa/composables"
// later in component
asyncData: async ({ params, app, }) => {
const { isLoggedIn } = useUser(app)
})
If you don't pass the Vue instance context to the composable, you'll see the following security warning.
It means that somewhere you're invoking useAddToCart
without passing the root. Find that in your code and add the context to fix it.
# API Client
Similar to composables
, the API client instance should be created for every request on the server. To ensure that all requests are secured and we still have the power of tree-shaking (having only code in your bundle which is used), we changed the way we invoke API methods, to pass them the API client context as well. Most of the API client logic is invoked under the hood inside composables, but if you need to invoke an API client method by yourself, you should add the context. To ensure there's a common way for composables and API client, we introduced getApplicationContext
method.
Usage example
import {
getShippingMethodDetails,
getPaymentMethodDetails,
getOrderPaymentUrl,
} from "@shopware-pwa/shopware-6-client"
import { getApplicationContext } from "@shopware-pwa/composables"
// later in component
setup(params, {root}) {
// we can pass component name, to easily track where the context is not passed
const { apiInstance } = getApplicationContext(root, "myComponent")
const someShippingMethodId = "123"
const shippingMethod = await getShippingMethodDetails(
someShippingMethodId,
apiInstance
)
}
You should check all your imports for @shopware-pwa/shopware-6-client
and add apiInstance
as the last parameter.
Additional resources
If you'd like to learn more about context awareness and shared contexts between requests, refer to the Vue Server-Side-Renderer documentation.