|
416 | 416 | - When this happens, the user must interact with the app to go through the OAuth flow again to get new tokens |
417 | 417 | - The refresh process uses database row-level locking to prevent race conditions from concurrent requests |
418 | 418 |
|
| 419 | +**5. Migrating Existing Shop Installations:** |
| 420 | + |
| 421 | +⚠️ **Important:** When you enable `offline_access_token_expires: true`, only **new shop installations** will automatically receive expiring tokens during the OAuth flow. Existing shop installations with non-expiring tokens will continue using their current tokens until manually migrated. |
| 422 | + |
| 423 | +To migrate existing shops to expiring tokens, use the `ShopifyAPI::Auth::TokenExchange.migrate_to_expiring_token` method. Here's an example background job to migrate all existing shops: |
| 424 | + |
| 425 | +```ruby |
| 426 | +# app/jobs/migrate_shops_to_expiring_tokens_job.rb |
| 427 | +class MigrateShopsToExpiringTokensJob < ActiveJob::Base |
| 428 | + queue_as :default |
| 429 | + |
| 430 | + def perform |
| 431 | + # Find shops that haven't been migrated yet (no refresh_token or expires_at) |
| 432 | + shops_to_migrate = Shop.where(expires_at: nil, refresh_token: nil, refresh_token_expires_at: nil) |
| 433 | + |
| 434 | + shops_to_migrate.find_each do |shop| |
| 435 | + begin |
| 436 | + # Migrate to expiring token |
| 437 | + new_session = ShopifyAPI::Auth::TokenExchange.migrate_to_expiring_token( |
| 438 | + shop: shop.shopify_domain, |
| 439 | + non_expiring_offline_token: shop.shopify_token |
| 440 | + ) |
| 441 | + |
| 442 | + # Store the new session with expiring token and refresh token |
| 443 | + Shop.store(new_session) |
| 444 | + |
| 445 | + Rails.logger.info("Successfully migrated #{shop.shopify_domain} to expiring token") |
| 446 | + rescue ShopifyAPI::Errors::HttpResponseError => e |
| 447 | + # Handle migration errors (e.g., shop uninstalled, network issues) |
| 448 | + Rails.logger.error("Failed to migrate #{shop.shopify_domain}: #{e.message}") |
| 449 | + rescue => e |
| 450 | + Rails.logger.error("Unexpected error migrating #{shop.shopify_domain}: #{e.message}") |
| 451 | + end |
| 452 | + end |
| 453 | + end |
| 454 | +end |
| 455 | +``` |
| 456 | + |
| 457 | +**Migration notes:** |
| 458 | +- This is a **one-time, irreversible operation** per shop |
| 459 | +- The shop must have the app installed and have a valid access token |
| 460 | +- After migration, the shop's offline token will have an expiration date and a refresh token |
| 461 | + |
419 | 462 | **Note:** If you choose not to enable expiring offline tokens, the `expires_at`, `refresh_token`, and `refresh_token_expires_at` columns will remain `NULL` and no automatic refresh will occur. Refresh tokens are only available for offline (shop) access tokens. Online (user) access tokens do not support refresh and must be re-authorized through OAuth when expired. |
420 | 463 |
|
421 | 464 | ## Migrating from shop-based to user-based token strategy |
|
0 commit comments