> Deprecated: > Product subscription app extensions won't be supported as of December 3, 2025. You should migrate existing product subscription app extensions to [purchase options extensions](/docs/apps/build/purchase-options/purchase-options-extensions). App Bridge Admin apps use session tokens to authenticate requests between the extension and your app's backend server. Session tokens are secure packets of data about a merchant session in the Shopify Admin, similar to cookies. A session token provides the information required to validate that a request is coming from Shopify, and also provides the IDs of the user and shop. Learn more about [session tokens](/docs/apps/build/authentication-authorization/session-tokens). This tutorial describes how to authenticate requests between your extension and your app's backend server using session tokens. ## What you'll learn In this tutorial, you'll learn how to do the following tasks: - Make a request to your app's server using a session token - Receive a request and decode the session token - Validate a session token ## Requirements - Complete the [Getting started tutorial](/docs/apps/build/purchase-options/product-subscription-app-extensions). - Familiarize yourself with [data and UI component rendering and extension points](/docs/apps/build/purchase-options/product-subscription-app-extensions/extension-points). ## Step 1: Make requests to your app’s server using a session token When an extension built with App Bridge Admin loads in the Shopify Admin, you can fetch the session token using the session token API. Include the token with every request that you make to your app's backend server. The token is signed using a shared secret so that your app's backend can verify if the request is valid. ### Configure your app's backend server Extensions built using App Bridge Admin are hosted on Shopify's servers. To receive requests from your extension, you need to enable cross-domain requests on your app's backend server. If you're using Rails, then add the following code in the `config/application.rb` file to enable cross-domain requests: ```ruby Rails.application.config.middleware.insert_before(0, Rack::Cors) do allow do origins '*' # Allow access to this api from any domain resource '*', # Allow all origins access to all resources headers: ['authorization', 'content-type', 'context'], # Restrict headers to relevant keys methods: [:post] # Restrict the methods to only the ones expected to be used by the extension end end ``` ### Fetch a session token from the Shopify Admin In your extension, use the session token API to fetch a new session token. Session tokens expire every minute, so always fetch a new token before making a request to your app's backend server. ```typescript?title: 'JavaScript' import {extend, TextField} from '@shopify/admin-ui-extensions'; function MyExtension(root, api) { const sessionToken = api.sessionToken; const text = root.createComponent(TextField, { disabled: true, value: '', label: 'Session Token', }); sessionToken.getSessionToken().then((newToken) => { text.updateProps({ value: newToken, }); }); root.appendChild(text); root.mount(); } extend('MyExtensionPoint', MyExtension); ``` ```typescript?title: 'React' import {TextField} from '@shopify/admin-ui-extensions'; import {extend, render, useSessionToken} from '@shopify/admin-ui-extensions-react'; function App() { const {getSessionToken} = useSessionToken(); const [token, setToken] = useState(''); useEffect(() => { getSessionToken().then((newToken) => { setToken(newToken); }); }, []); return ; } extend('MyExtension', render(() => )); ``` ### Make a request to your app's backend server After your extension has received a session token, you can include it in requests to your server. How you include the session token in the request is up to you, but we suggest including it in the request header as follows: ```typescript?title: 'JavaScript' import {extend, Button} from '@shopify/admin-ui-extensions'; extend('MyExtension', (api, root) => { const sessionToken = api.sessionToken; const sendRequest = async () => { const token = await sessionToken.getSessionToken(); const response = await fetch('https://server-url-here', { headers: { 'any-header-key': token || 'unknown token', }, }); console.log('Response', response.text()); }; const button = root.createComponent(Button, { title: 'Send request', onClick: sendRequest, }); root.append(button); }); ``` ```typescript?title: 'React' import React, {useCallback} from 'react'; import {extend, render, useSessionToken, Button} from '@shopify/admin-ui-extensions-react'; function App() { const {getSessionToken} = useSessionToken(); const sendRequest = useCallback(async () => { const token = await getSessionToken(); const response = await fetch('https://server-url-here', { headers: { 'any-header-key': token || 'unknown token', }, }); console.log('Response', response.text()); }, [getSessionToken]); return