Skip to content
This repository has been archived by the owner on Apr 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1163 from Shopify/token-exchange-docs
Browse files Browse the repository at this point in the history
Token exchange docs
  • Loading branch information
rezaansyed authored Jan 31, 2024
2 parents 51f3c27 + 687a8eb commit 3eae4c7
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .changeset/tasty-lobsters-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions packages/shopify-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@

- eae5a4a8: Introduce token exchange API for fetching access tokens. This feature is currently unstable and is hidden behind the `unstable_tokenExchange` future flag.

:exclamation: To learn more about Token Exchange, see [Performing OAuth](./docs/guides/oauth.md)

## 8.0.2

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/shopify-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

This library provides support for the backends of TypeScript/JavaScript [Shopify](https://www.shopify.com) apps to access the [Shopify Admin API](https://shopify.dev/docs/api/admin), by making it easier to perform the following actions:

- Creating [online](https://shopify.dev/docs/apps/auth/oauth/access-modes#online-access) or [offline](https://shopify.dev/docs/apps/auth/oauth/access-modes#offline-access) access tokens for the Admin API via OAuth
- Creating [online](https://shopify.dev/docs/apps/auth/access-token-types/online) or [offline](https://shopify.dev/docs/apps/auth/access-token-types/offline) access tokens for the Admin API via OAuth
- Making requests to the [REST API](https://shopify.dev/docs/api/admin/rest/reference)
- Making requests to the [GraphQL API](https://shopify.dev/docs/api/admin/graphql/reference)
- Register/process webhooks
Expand Down
100 changes: 92 additions & 8 deletions packages/shopify-api/docs/guides/oauth.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,100 @@
# Performing OAuth

##### Table of contents
- [Supported types of OAuth Flow](#supported-types-of-oauth)
- [Token Exchange](#token-exchange)
- [Remix App](#remix-app)
- [Non-Remix App](#non-remix-app)
- [Detecting scope changes](#detecting-scope-changes)
- [Authorization Code Grant Flow](#authorization-code-grant-flow)
- [Detecting scope changes](#detecting-scope-changes-1)
- [After OAuth](#after-oauth)

----------------------------------------------------------------------------------
After you set up the library for your project, you'll be able to use it to interact with the APIs, and add your own functionality.
The first thing your app will need to do is to get a token to access the Admin API by performing the OAuth process. Learn more about [OAuth on the Shopify platform](https://shopify.dev/docs/apps/auth/oauth).

To perform OAuth, you will need to create two endpoints in your app:
## Supported types of OAuth
> [!TIP]
> If you are building an embedded app, we **strongly** recommend using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
with [token exchange](#token-exchange) instead of the authorization code grant flow.

1. Start the process by calling [shopify.auth.begin](../reference/auth/begin.md) to redirect the merchant to Shopify, to ask for permission to install the app.
1. Return the merchant to your app once they approve the app installation, by calling [shopify.auth.callback](../reference/auth/callback.md) to set up a session with an API access token.
1. [Token Exchange](#token-exchange)
- Recommended for embedded apps
- Doesn't require redirects, which makes it faster and prevents flickering when loading the app
- Access scope changes are handled by Shopify if you use [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
2. [Authorization Code Grant Flow](#authorization-code-grant-flow)
- Suitable for non-embedded apps
- Installations, and access scope changes are managed by the app

Once you complete the OAuth process, you'll be able to call [shopify.session.getCurrentId](../reference/session/getCurrentId.md) to fetch your session, and create API clients.
## Token Exchange
OAuth process by exchanging the current user's [session token](https://shopify.dev/docs/apps/auth/session-tokens) for an
[access token](https://shopify-dev-staging2.shopifycloud.com/docs/apps/auth/access-token-types/online.md) to make
authenticated Shopify API queries.

> **Note**: private apps are unable to perform OAuth, because they don't require an access token to interact with API.
This can replace authorization code grant flow completely if you also take advantage of [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).

Learn more about:
- [How token exchange works](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange)
- [Using Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
- [Configuring access scopes through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration)

## Detecting scope changes
###### Remix App
See ["new embedded app authorization Strategy"](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy) to enable this feature.

When the OAuth process is completed, the created session has a `scope` field which holds all of the scopes that were requested from the merchant at the time.
###### Non-remix App
1. Ensure your access scopes are available on Shopify:
- [configured through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration) or
- install your app through the [authorization code grant flow](#authorization-code-grant-flow) (not recommended)

When an app's scopes change, it needs to request merchants to go through OAuth again to renew its permissions. The library provides an easy way for you to check whether that is the case at any point in your code:
2. Turn on future flag `unstable_tokenExchange` when configuring shopifyApi

```ts
shopifyApi({
...
isEmbeddedApp: true,
future: {
unstable_tokenExchange: true,
},
})
```
3. Start the OAuth process by calling [shopify.auth.tokenExchange](../reference/auth/tokenExchange.md) to exchange user session token to access token.
4. Use the exchanged session token to make authenticated API queries, see [After OAuth](#after-oauth)

#### Detecting scope changes

##### Shopify managed installation
If your access scopes are [configured through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration), scope changes will be handled by Shopify automatically.
Learn more about [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).
Using token exchange will ensure that the access token retrieved will always have the latest access scopes granted by the user.

##### Not using Shopify managed installation - not recommended
If you don't have access scopes configured through the Shopify CLI, you can still use token exchange to exchange the current user's session token for access token.

> [!WARNING]
> This is not recommended because you'll have to manage both OAuth flows.
1. Use [authorization code grant flow](#authorization-code-grant-flow) to handle app installation so your app's access scopes will be
available in Shopify.
2. Once the app is installed for the user, you can use token exchange to exchange that user's session token to retrieve access token to refresh an expired token.
- Using token exchange will ensure you don't have to handle redirects through the [authorization code grant flow](#authorization-code-grant-flow) on subsequent authorization calls,
except when your requested access scopes changes.

## Authorization Code Grant Flow
> [!NOTE]
> If you are building an embedded app, we **strongly** recommend using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)
with [token exchange](#token-exchange) instead of the authorization code grant flow.

To perform [authorization code grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant), you will need to create two endpoints in your app:

1. Start the process by calling [shopify.auth.begin](../reference/auth/begin.md) to redirect the merchant to Shopify, to ask for permission to install the app.
1. Return the merchant to your app once they approve the app installation, by calling [shopify.auth.callback](../reference/auth/callback.md) to set up a session with an API access token.

#### Detecting scope changes

When the OAuth process is completed, the created session has a `scope` field which holds all of the access scopes that were requested from the merchant at the time.

When an app's access scopes change, it needs to request merchants to go through OAuth again to renew its permissions. The library provides an easy way for you to check whether that is the case at any point in your code:

```ts
const session: Session; // Loaded from one of the methods above
Expand All @@ -28,4 +106,10 @@ if (!shopify.config.scopes.equals(session.scope)) {

This is useful if you have a middleware or pre-request check in your app to ensure that the session is still valid.

## After OAuth

Once you complete the OAuth process, you'll be able to call [shopify.session.getCurrentId](../reference/session/getCurrentId.md) to fetch your session, and create API clients.

> **Note**: private apps are unable to perform OAuth, because they don't require an access token to interact with API.
[Back to guide index](../../README.md#guides)
19 changes: 18 additions & 1 deletion packages/shopify-api/docs/reference/auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,30 @@

This object contains functions used to authenticate apps, and redirect users to Shopify.

## Token Exchange

Learn more about [token exchange](../../guides/oauth.md#token-exchange).

| Property | Description |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [tokenExchange](./tokenExchange.md) | Performs token exchange to get access token from session token |

## Authorization Code Grant Flow

Learn more about [authorization code grant flow](../../guides/oauth.md#authorization-code-grant-flow).

| Property | Description |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [begin](./begin.md) | Redirect the user to Shopify to request authorization for the app. |
| [callback](./callback.md) | Receive Shopify's callback after the user approves the app installation. |
| [nonce](./nonce.md) | Generates a random string of characters to be used as the state value for OAuth. |

## Utility Functions

| Property | Description |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [safeCompare](./safeCompare.md) | Compares two strings or arrays in a way that's safe against timing attacks. |
| [getEmbeddedAppUrl](./getEmbeddedAppUrl.md) | Builds a URL to redirect the user back to the right Shopify surface based on the current request. |
| [buildEmbeddedAppUrl](./buildEmbeddedAppUrl.md) | Constructs the appropriate Shopify URL to redirect to. |

[Back to shopifyApi](../shopifyApi.md)
🔙 [Back to shopifyApi](../shopifyApi.md)
77 changes: 77 additions & 0 deletions packages/shopify-api/docs/reference/auth/tokenExchange.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# shopify.auth.tokenExchange

Begins the OAuth process by exchanging the current user's [session token](https://shopify.dev/docs/apps/auth/session-tokens) for an
[access token](https://shopify.dev/docs/apps/auth/access-token-types/online.md) to make authenticated Shopify API queries.

Learn more:
- [Token Exchange](../../guides/oauth.md#token-exchange)

## Examples

### Node.js
```ts
app.get('/auth', async (req, res) => {
const shop = shopify.utils.sanitizeShop(req.query.shop, true)
const headerSessionToken = getSessionTokenHeader(request);
const searchParamSessionToken = getSessionTokenFromUrlParam(request);
const sessionToken = (headerSessionToken || searchParamSessionToken)!;

await shopify.auth.tokenExchange({
sessionToken,
shop,
requestedTokenType: RequestedTokenType.OfflineAccessToken // or RequestedTokenType.OnlineAccessToken
});
});

function getSessionTokenHeader(request){
// Get session token from header `authorization`
// Header Format is: "{"Authorization", "Bearer this-is-the-session-token"}
// Return "this-is-the-session-token" from request header
}

function getSessionTokenFromUrlParam(request){
// Get session token from the request URL param
// The param is "id_token"
// Example: "${app_url}/?shop=${shop}&id_token=this-is-the-session-token"
// Return "this-is-the-session-token" from URL param
}
```

## Parameters

### sessionToken
`string` | :exclamation: required

The current user's session token, can be found in:
1. Request header:
```
{
"Authorization": "Bearer this-is-the-session-token
}
```
2. URL Param:
```
'${APP_URL}/?shop=someshop.myshopify.com&id_token=this-is-the-session-token
```

### shop
`string` | :exclamation: required

A Shopify domain name in the form `{exampleshop}.myshopify.com`.

### requestedTokenType
`enum` | :exclamation: required

[`RequestedTokenType` in token-exchange.ts](https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/lib/auth/oauth/token-exchange.ts)

- `RequestedTokenType.OnlineAccessToken` - Learn more about [online tokens](https://shopify.dev/docs/apps/auth/access-token-types/online.md)
- `RequestedTokenType.OfflineAccessToken` - Learn more about [offline tokens](https://shopify.dev/docs/apps/auth/access-token-types/offline.md)

## Return

`Promise<Session>`

See [Session](../session/README.md) reference.



0 comments on commit 3eae4c7

Please sign in to comment.