تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Adding Force Scope Flag0:00

Let's add a new column to the users table to force redirect the user to the grant page to accept new scopes. So let's open the terminal, we'll do vendor/bin/sail artisan make:migration, call it AddForceScopeUpdateColumnToUsersTable, let's open that migration, we'll add the new column here, so this will be boolean, forceScopeUpdate, let's set it to default false, and in here we'll drop the column, so dropColumn, forceScopeUpdate, let's run the migrations, we'll do artisan migrate, let's open the users table, we have that column right here, I'm going to set this to 1 manually here and update it, and as you can imagine this part would be part of some kind of command based on your requirements, so if you had 20 stores for example that had your application installed and you wanted to add a feature that required a new scope,

Creating Scope Check Middleware0:56

kind of command based on your requirements, so if you had 20 stores for example that had your application installed and you wanted to add a feature that required a new scope, you would run some update query through a command that would flip this flag to 1 and then have the users be redirected to the grant page to accept the new scopes. Now to make the actual redirect happen we should add a new middleware, so let's close this out, close that out, open the terminal, we'll run php artisan make:middleware CheckAccessScopes, let's open that middleware, and in here we should check if the user has that ForceScopeUpdate flag set to 1. So first let's get the user instance from the request, so we'll do $shop = $request->user(), and then we can do if $shop->ForceScopeUpdate, then we'll do the redirect in here. Now if we open the VerifyShopifyMiddleware and search for the install redirect

shop equals request user, and then we can do if shop forceScopeUpdate, then we'll do the redirect in here. Now if we open the VerifyShopify middleware and search for the installRedirect method, we see that this does something similar, this handles the redirect for us. So we could basically copy this logic and paste it in our middleware right here. We'll replace this with shop name and this should work as expected. Now here is where it gets tricky. The current user instance is actually null at this stage of the request because user is technically not logged in yet. Remember we are in SPA mode and session tokens are handled in the front end and are passed on Axios requests. So if user does a full page refresh and this middleware gets triggered, we'll get an error that we're trying to access the property on a null value because user is essentially null. Because again on page refresh we have no session token until the frame

Handling SPA Auth Null User2:35

we'll get an error that we're trying to access the property on a null value because user is essentially null. Because again on page refresh we have no session token until the frame loads and Axios gets set up. Only then the session token is passed and user is authenticated and available. So we have two options in here. First option is to fetch the User model manually using the shop name, and the shop name is available on the request. So we could do something like if user is available on the request we get it, or otherwise we can do something like User::where('name', '=', request->get('shop')). And then this will work if the user is authenticated and is available on the request then we'll get that, otherwise we'll fetch the User using the shop and we need to add first in here. We would also have to add the check here to only do this redirect if the request is non-Ajax because we can't do redirects like this if it's an Axios.

shop and we need to add first in here. We would also have to add the check here to only do this redirect if the request is non-Ajax because we can't do redirects like this if it's an Axios request for example right. So on top right here before we even do any query lookups or anything we can do something like if request is Ajax then simply just proceed with the request. This way it will only redirect the user to the grant page on full page load. Now another option is that we could do this check here only for Ajax requests. So in here we can flip this and say that if it's not an Ajax request then we'll just proceed as regular, otherwise if it's an Ajax request then we don't need to do this anymore because user should be authenticated at this stage and should be available on the request. Then in here instead of doing the redirect this way we can respond with JSON. So we can do something like return response()->json() and in here we can include the

Axios Interceptor Redirect4:13

available on the request. Then in here instead of doing the redirect this way we can respond with JSON. So we can do something like return response()->json() and in here we can include the redirect URL that we want our front end to redirect the user to. Now we can use the route helper function to get the proper route for the redirect without actually doing the redirect. So we can take this and put it in here. We can get rid of that and we should set the status of 403. Then we can open our useAxios hook and we can add a response interceptor here to hook into the error response and check if the status is 403. So we can do something like const responseInterceptor = Axios.interceptors.response.use() and do the check here if the error status is 403. So we can do if error.response.status is 403 and the error.response.data contains the forceRedirect property then we can do the redirect. So we can check that using error.response

status is 403. So we can do if errorResponse.status is 403 and the errorData contains the forceRedirect property then we can do the redirect. So we can check that using errorResponse.data.forceRedirect and then do the redirect in here. Now we can do the actual redirect by using the useNavigate hook from Shopify React Outreach. So let's do that in here we'll do const navigate = useNavigate() and then redirect in here. So we'll do navigate(errorResponse.data.forceRedirect) and I think this supposed to be forceRedirectUrl if I remember it correctly yeah we have forceRedirectUrl so let's copy that paste it in here and we should also eject this responseInterceptor the same way we're doing with the requestInterceptor. So let's actually duplicate this in here and change this to response and responseInterceptor. Both solutions work and it is up to you which one you want to implement in your applications. For SPAs

actually duplicate this in here and change this to response and responseInterceptor. Both solutions work and it is up to you which one you want to implement in your applications. For SPAs I prefer doing it in the JavaScript and I think it's better UX in general. All right let's now add this middleware to all of our routes. So let's open web.php and we have this middleware verifyShopify in here so let's make this into an array and in here we'll add checkAccessScopes. Now let's test this out so let's go to the browser let's do a full page reload and the app loads regularly without any issues. Now let's select 10 products, 5 customers, click generate which is going to trigger the Axios request and as you can see it is doing the redirects and we're redirected to this grant page. Now if we click on this update button it's going to grant us the access to the new scopes but it's not going to automatically flip the

Reset Flag After Auth6:58

the redirects and we're redirected to this grant page. Now if we click on this update button it's going to grant us the access to the new scopes but it's not going to automatically flip the forceScopeUpdate flag back to zero. So we need to somehow update the forceScopeUpdate to zero after the User agrees to the updated scopes. Laravel Shopify actually provides a way for us to execute a job after the authorization. Let's go back to the code and open ShopifyApp configuration. Let's search for after and we have this configuration option called afterAuthenticateJob and as described here, this option is for firing a job after a shop has been authenticated. So let's make a new job we'll do vendor/bin/sail artisan make:job AfterAuthenticate. Let's add this job in here so we'll uncomment this and we'll set this to be by default AfterAuthenticate class and this one will flip to true to dispatch it immediately. false basically.

authenticate. Let's add this job in here so we'll uncomment this and we'll set this to be by default after authenticate class and this one will flip to true to dispatch it immediately. false basically dispatches job in queue. Now let's open our afterAuthenticate and in constructor whenever this job is dispatched it receives the Shop model or the User model instance in the constructor. So we can actually do private readonly Shop $shop and then in here we can do if $this->shop ->forceScopeUpdate if we have the forceScopeUpdate set then we can do $this->shop->forceScopeUpdate equals to false and save(). Now it's highlighting it here because of the IDE we are type hinting this to Shop model but we could also type hint to User because essentially it's the User model that's coming in. Let's test this out so let's open the users table again we have the forceScopeUpdate set to 1. Let's go back to the browser click update app and we're brought back to our app.

coming in. Let's test this out so let's open the users table again we have the forceScopeUpdate set to one. Let's go back to the browser click update app and we're brought back to our app. Our app is loading fine now let's check the database so let's refresh this and sure enough forceScopeUpdate is set to zero. Let's go back to the browser let's switch to the telescope and as you can see within the jobs we see this afterAuthenticate job being dispatched. Now let's try to create 10 products and 5 customers. Let's switch back to telescope and as you can see the createProducts processed successfully but createCustomers failed again. So let's inspect this let's check the exception message and this time the error is a little bit different which is good. Basically what this means is that our scope logic worked User has granted us access to their customer data but Shopify has additional protection to protect customer data which kind of makes sense.

Requesting Protected Customer Data9:40

Basically what this means is that our scope logic worked User has granted us access to their customer data but Shopify has additional protection to protect customer data which kind of makes sense because remember this is the store owners customers we are talking about and they can contain very sensitive information. There are some processes to request access to customer data and is described very well in the Shopify documentation. So if we go to Shopify's protected customer data documentation we see a section here that details on how to request access to the protected customer data. Now we only plan to use customer data for development stores because our app essentially is only meant for development stores to create fake data. We're not accessing any real customer information we are just generating and deleting the fake customers through our app. If in your app you need access to customer data for whatever reasons you can request access but make sure to

information we are just generating and deleting the fake customers through our app. If in your app you need access to customer data for whatever reasons you can request access but make sure to follow the guideline described in this documentation and only request access for minimal data there your app needs. Let me show you how you can request access from the partner dashboard. So let's open the Shopify partner dashboard go to the app setup scroll all the way down and in here we see section to request access to protected customer data. So let's click on that and in here you just need to follow the steps to request this data. It's like a questionnaire asking the reasoning behind why you're asking for the protected customer data and so on for our purposes that's app functionality. So let's click save then it's asking us to select only the protected customer fields that we actually need. In our case I think we only need the name so we'll just make this again for app

So let's click save then it's asking us to select only the protected customer fields that we actually need. In our case I think we only need the name so we'll just make this again for app functionality. We need email also for app functionality and I think that's it. If you need anything else you can select it in here and finally you provide some more details. Now since I already put in the request I got an approval so I'm going to click on generate this time. Let's switch back to telescope both of these jobs are in pending. The createProducts has processed and createCustomers has processed successfully as well. Now if we go back to the store we can check the products we have products created and if we check the customers we see those five fake customers in here.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟