--- title: Authenticate checkouts description: >- Prefill checkout with buyer account information using Checkout Kit and the Customer Account API. source_url: html: >- https://shopify.dev/docs/storefronts/mobile/checkout-kit/authenticate-checkouts?extension=swift md: >- https://shopify.dev/docs/storefronts/mobile/checkout-kit/authenticate-checkouts.md?extension=swift --- # Authenticate checkouts Authenticate checkouts with Checkout Kit and the Customer Account API. When a buyer signs in, Shopify prefills checkout with their email, shipping address, and payment method. Use the **Platform** dropdown under **Project** to switch between Swift, Kotlin, and React Native. The steps and code update for each platform. ## What you'll learn In this tutorial, you'll: * Obtain a Customer Account API access token using OAuth and Proof Key for Code Exchange (PKCE). * Use the access token to create an authenticated cart. * Present checkout to a signed-in buyer using Checkout Kit. ![Sequence diagram showing the OAuth flow for authenticated checkouts: app requests authorization, buyer signs in, app exchanges code for token, and presents checkout.](https://shopify.dev/assets/assets/images/custom-storefronts/csk-auth.md-1-dWlrYKLk.svg) ## Requirements [Enable new customer accounts](https://shopify.dev/docs/storefronts/headless/building-with-the-customer-account-api/getting-started#step-1-enable-customer-accounts) New customer accounts enabled on your store. [Set up Checkout Kit](https://shopify.dev/docs/storefronts/mobile/checkout-kit) Checkout Kit installed in your app. [Configure Storefront API access](https://shopify.dev/docs/storefronts/headless/building-with-the-storefront-api/getting-started#step-1-enable-storefront-api-access) Storefront API access for your app. ## Project [View on GitHub](https://github.com/Shopify/example-mobile--storefront--swift) ## Configure Customer Account API access Configure [Customer Account API access](https://shopify.dev/docs/storefronts/headless/building-with-the-customer-account-api/getting-started#step-2-configure-customer-account-api-access) as a public mobile client. Save the callback URL and client ID because you'll need them in the next step. ### Set up the mobile client In the Dev Dashboard, configure a Customer Account API client for your app. Set the client type to **Public** and save these values: * **Client ID**: Required for the authorization URL and token exchange. * **Callback URL**: The redirect URI for the OAuth flow. By default, the format is `shop.{your_shop_id}.app://callback`. ## Authenticate the buyer Implement an [OAuth 2.0 authorization flow](https://shopify.dev/docs/storefronts/headless/building-with-the-customer-account-api/getting-started#step-3-perform-authorization-requests-and-make-queries) with PKCE to sign the buyer in through the Customer Account API. ### Generate a code challenge and verifier Generate a PKCE code verifier and challenge. These prove that the client exchanging the authorization code is the same client that started the request, as defined in [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636). Save the code verifier because you'll need it when you exchange the authorization code for a token. ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ### Discover authentication endpoints Fetch the authorization and token endpoints from your shop's OpenID configuration. ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ### Build the authorization URL Combine the authorization endpoint, client ID, redirect URI, code challenge, and a random state parameter into an authorization URL. Open the URL in a web view so the buyer can sign in. *** [Authorization endpoint](https://shopify.dev/docs/api/customer#step-authorization) ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ## Handle the authorization callback After the buyer signs in, the web view redirects to your `redirect_uri` with a `code` query parameter. Extract the authorization code and close the web view. ### Extract the authorization code Listen for navigation events in the web view. When the URL matches your redirect URI, extract the `code` and `state` parameters. Verify that the `state` matches the value you sent in the authorization URL to prevent CSRF attacks. ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ## Exchange the code for an access token Submit the authorization code and the code verifier to the token endpoint to [obtain a Customer Account API access token](https://shopify.dev/docs/api/customer#step-obtain-access-token). ### Send the token request Send a `POST` request with the authorization code and code verifier to the token endpoint. The response includes `access_token`, `refresh_token`, and `expires_in`. *** Access tokens expire after the `expires_in` duration. Refresh an expired token by making a [`POST` request](https://shopify.dev/docs/api/customer#step-using-refresh-token) to the token endpoint with `grant_type` set to `refresh_token`. Store tokens securely using your platform's built-in security features (or equivalent secure storage): Use [Keychain Services](https://developer.apple.com/documentation/security/keychain-services) to store tokens on iOS. **Caution:** Never store tokens in plain text. Compromised tokens could allow unauthorized access to buyer accounts. ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ## Create an authenticated cart Include the access token in the [buyer identity input](https://shopify.dev/docs/api/storefront/latest/input-objects/CartBuyerIdentityInput#field-customeraccesstoken) of your `cartCreate` mutation. Shopify uses the token to prefill checkout with the buyer's account information. ### Pass buyer identity in the cart mutation Pass the `access_token` from the token response as the `customerAccessToken` in the cart's `buyerIdentity`. The response includes a `checkoutUrl` that you pass to Checkout Kit. *** **Caution:** Present the `checkoutUrl` soon after receiving it. Authenticated checkout URLs expire, and an expired URL starts an unauthenticated checkout session. [cart​Create mutation](https://shopify.dev/docs/api/storefront/latest/mutations/cartCreate) [Cart​Buyer​Identity​Input](https://shopify.dev/docs/api/storefront/latest/input-objects/CartBuyerIdentityInput) ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ## Present the checkout URL in Checkout Kit Pass the `checkoutUrl` to Checkout Kit's `present()` method. The buyer sees checkout prefilled with their account information. ### Open Checkout Kit with the prefilled URL Call `present()` with the `checkoutUrl` from the cart response. Checkout Kit opens with the buyer's shipping address and payment methods prefilled. *** **Note:** After checkout, sign the buyer out by sending a request to the [logout endpoint](https://shopify.dev/docs/api/customer#logging-out) and clearing any locally stored tokens and authenticated carts. ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ## /AuthClient.swift ```swift import Foundation import CommonCrypto import Security import ShopifyCheckoutSheetKit class AuthClient { let shopDomain: String let customerAccountsApiClientId: String let customerAccountsApiRedirectUri: String let storefrontAccessToken: String private var codeVerifier: String? private var savedState: String? init(shopDomain: String, clientId: String, redirectUri: String, storefrontAccessToken: String) { self.shopDomain = shopDomain self.customerAccountsApiClientId = clientId self.customerAccountsApiRedirectUri = redirectUri self.storefrontAccessToken = storefrontAccessToken } // MARK: - PKCE func generateCodeVerifier() -> String { var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) return base64Encode(Data(buffer)) } func generateCodeChallenge(for codeVerifier: String) -> String { guard let data = codeVerifier.data(using: .utf8) else { fatalError() } var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) data.withUnsafeBytes { bytes in _ = CC_SHA256(bytes.baseAddress, CC_LONG(data.count), &digest) } ``` ## Tutorial complete! You've built an authenticated checkout flow that signs buyers in through the Customer Account API and prefills checkout with their account information. ### Next steps [Handle offsite payments\ \ ](https://shopify.dev/docs/storefronts/mobile/checkout-kit/offsite-payments) [Bring buyers back to your app after offsite payment redirects.](https://shopify.dev/docs/storefronts/mobile/checkout-kit/offsite-payments) [Privacy compliance\ \ ](https://shopify.dev/docs/storefronts/mobile/checkout-kit/privacy-compliance) [Pass visitor consent for GDPR, CCPA, and App Tracking Transparency.](https://shopify.dev/docs/storefronts/mobile/checkout-kit/privacy-compliance) [Accelerated checkouts\ \ ](https://shopify.dev/docs/storefronts/mobile/checkout-kit/accelerated-checkouts-overview) [Add Shop Pay and Apple Pay buttons for one-tap purchases.](https://shopify.dev/docs/storefronts/mobile/checkout-kit/accelerated-checkouts-overview)