CORS is a mechanism that uses the addition of HTTP headers to tell the browser whether an application can access resources of another web application that is not in the same domain. In other words, a web application is called a cross-origin HTTP request when it can retrieve resources on another origin (domain, protocal or porrt) from itself.
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own.
The meaning of CORS
As you know the same-origin policy is a privacy policy that all present programs adhere to. This policy helps prevent unauthorized access to resources from other domains for malicious purposes.
Let’s imagine a scenario like this:
- A hacker using javascript code wrote an ajax request to get your contact information from gmail on a malicious website abc.example.com . Assuming api gets the directory as https://api.gmail.com/contacts
- You are logged into gmail in the same browser as the malicious website abc.example.com
Same-origin policy was created to prevent such scenarios to protect users, making it safer to surf the web. If you attempt to access unauthorized data, you will receive an error as shown below
1 2 3 4 5 6 | $.get('https://api.gmail.com/contacts') Access to XMLHttpRequest at ' https://api.gmail.com/contacts' from origin 'abc.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. |
However, in practice there are problems that you need to allow to access resources from another domain. For example, you have a user.example.com site where users register and log in, the second site is blog.example.com is a blog site that users need to log in to read the contents of the blog site. this. Also, you don’t want to have to build two places to store user information. To solve this problem, you need to allow the second site to know the login status of the first site. This article I will introduce you to the steps to do this on a Laravel website.
Install CORS on Laravel
Site server part
Rotute
I will configure an api url to perform login checking:
1 2 3 4 | Route <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">group</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'prefix'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">'api'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'as'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">'api.'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'middleware'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">'validate_api_token'</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> Route <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'api/checkLogin'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">' <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> '</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">name</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'check_login_status'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
This is just a regular route setup. Here I add a ‘validate_api_token’ middleware, this middleware works to validate the token that comes from the blog site, you need this token to confirm that it is generated from a secret key and an algorithm that both parties already know, Other sites cannot get this token, which increases security. You can use any encryption algorithm to generate tokens. I will not go into details on this.
Controller
In the controller, we need to write a function to return the login state and some other user information. Yes you can see the code as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">getLoginStatus</span> <span class="token punctuation">(</span> Request <span class="token variable">$request</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$loginInfo</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'isLogin'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token boolean constant">false</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token variable">$user</span> <span class="token operator">=</span> <span class="token function">auth</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">user</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$loginInfo</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'isLogin'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token boolean constant">true</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'userId'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token variable">$user</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token property">id</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'email'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">' <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> '</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'name'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">'User1'</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token function">response</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token variable">$loginInfo</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
In the getLoginStatus
function, I simply get the user information from laravel’s heppler auth()->user()
, if this object is not null then it means that the login is in reverse. I will return the json data type so that I can call them in the ajax function,
Middleware
The most noticeable part is here. You need to set up a response to tell the browser which domain allows the application, which method and how to access user.example.com site resources . To do that I created a middleware as shown below
1 2 3 4 5 6 7 8 9 | <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">handle</span> <span class="token punctuation">(</span> <span class="token variable">$request</span> <span class="token punctuation">,</span> Closure <span class="token variable">$next</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token variable">$next</span> <span class="token punctuation">(</span> <span class="token variable">$request</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">header</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'Access-Control-Allow-Origin'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'http://blog.example.com'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">header</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'Access-Control-Allow-Methods'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'*'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">header</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'Access-Control-Allow-Credentials'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'true'</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">header</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'Access-Control-Allow-Headers'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'X-CSRF-Token'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
I will explain each component to you:
Access-Control-Allow-Origin
: This setting indicates which sites are allowed to access CORS, the value here can be a string, an array. If an array you set toheader('Access-Control-Allow-Origin', ['http://blog.example.com', ;'http://127.0.0.1:8000'])
. As you can see, we can specify a local address for CORS.Access-Control-Allow-Methods
: Specifies which HTTP Methods are allowed to execute.*
means all methods. You can specify specific methods that the application allows, for example, GET, POST, PUT, HEAD, etc.Access-Control-Allow-Credentials
: This configuration tells the browser whether or not it can attach the user.example.com site’s cookies in the blog.example.com site’s Cors requests. Note that this header only works if the client also sets the valuewithCredentials = true
Access-Control-Allow-Headers
: Allows any header to be attached to CORS requests. Because the laravel application will request a csrf_token with a POST request, so we set theX-CSRF-Token
Kernel
I will set the middleware just created above in the middleware
group in Kernel.php. When you set up middleware in this group, all reuquestments allow CORS
1 2 3 4 5 6 | <span class="token keyword">protected</span> <span class="token variable">$middleware</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token package">App Http Middleware Cors</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">,</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> |
If you don’t want that, you can configure only the url that allows CORS. You will configure the middleware in the routeMiddlewares
in the Kernel
1 2 3 4 5 6 | <span class="token keyword">protected</span> <span class="token variable">$routeMiddleware</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token package">App Http Middleware Cors</span> <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token punctuation">,</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> |
Also reset the route as follows:
1 2 3 4 | Route <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">group</span> <span class="token punctuation">(</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'prefix'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">'api'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'as'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token single-quoted-string string">'api.'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'middleware'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'cors'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">'validate_api_token'</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> Route <span class="token punctuation">:</span> <span class="token punctuation">:</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'api/checkLogin'</span> <span class="token punctuation">,</span> <span class="token single-quoted-string string">' <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> '</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token function">name</span> <span class="token punctuation">(</span> <span class="token single-quoted-string string">'check_login_status'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Ok, got it server api part ( user.example.com ) is done. Next, we need to make a request on the client side ( blog.example.com )
On the client site
Html
We create a simple button to check the login status:
1 2 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> button</span> <span class="token attr-name">type</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> button <span class="token punctuation">"</span></span> <span class="token attr-name">id</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> btn-check-log <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> Checklogin <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> button</span> <span class="token punctuation">></span></span> |
Check CORS site server
To know if the site server allows CORS or not, issue the following command:
1 2 3 4 5 | curl <span class="token operator">-</span> i <span class="token operator">-</span> X OPTIONS <span class="token operator">-</span> H <span class="token string">"Origin: http://blog.example.com"</span> <span class="token operator">-</span> H <span class="token string">'Access-Control-Request-Method: POST'</span> <span class="token operator">-</span> H <span class="token string">'Access-Control-Request-Headers: X-CSRF-Token'</span> <span class="token string">"http://user.example.com/api/checkLogin"</span> |
The request does not make a direct request to check the login state but rather a preflight. This means it will send a request to check if the server allows CORS. If so, the server will return a response containing the following components:
1 2 3 4 | Access <span class="token operator">-</span> Control <span class="token operator">-</span> Allow <span class="token operator">-</span> Origin: http: <span class="token operator">/</span> <span class="token operator">/</span> blog <span class="token punctuation">.</span> example <span class="token punctuation">.</span> com Access <span class="token operator">-</span> Control <span class="token operator">-</span> Allow <span class="token operator">-</span> Methods: POST Access <span class="token operator">-</span> Control <span class="token operator">-</span> Allow <span class="token operator">-</span> Headers: X <span class="token operator">-</span> CSRF <span class="token operator">-</span> Token |
Otherwise, you will not be able to perform a CORS with the POST method above.
Create an ajax request
I created a simple jquery ajax request like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">'#btn-check-login'</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">click</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> $ <span class="token punctuation">.</span> <span class="token function">ajax</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> url <span class="token operator">:</span> <span class="token string">"http://user.example.com/api/checkLogin"</span> <span class="token punctuation">,</span> type <span class="token operator">:</span> <span class="token string">'POST'</span> <span class="token punctuation">,</span> dataType <span class="token operator">:</span> <span class="token string">'json'</span> <span class="token punctuation">,</span> data <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">'API_TOKEN'</span> <span class="token operator">:</span> <span class="token string">'api_token'</span> <span class="token comment">// Một chuỗi mã hóa bất kì mà bạn tạo ra</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> xhrFields <span class="token operator">:</span> <span class="token punctuation">{</span> withCredentials <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">done</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">response</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> response <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">fail</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">err</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
As you can see, it is just a function familiar to us, the only configuration that needs attention is xhrFields: { withCredentials: true }
. With this configuration, our request will include the site’s user.example.com cookie in the option. We all know the login status of the site is set by the session and the cookie and only when we send this cookie will the user be able to login. However, for this setting to work, the server side needs to set the header('Access-Control-Allow-Credentials', 'true')
, otherwise it will not make sense. Note, the cookie in this case is a third-party cookie and the storage and access of cookies are still strictly in accordance with the same-origin policy
. Therefore, we cannot access cookies with document.cookie
. It is fully handled automatically by the browser.
After executing the above request, we get the following successful result:
1 2 3 4 5 6 7 | <span class="token punctuation">{</span> <span class="token property">"isLogin"</span> <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> <span class="token property">"userId"</span> <span class="token operator">:</span> <span class="token number">12345</span> <span class="token punctuation">,</span> <span class="token property">"email"</span> <span class="token operator">:</span> <span class="token string">" <a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a> "</span> <span class="token punctuation">,</span> <span class="token property">"name"</span> <span class="token operator">:</span> <span class="token string">"User1"</span> <span class="token punctuation">}</span> |
Ok, so all done. We can make a complete CORS request.
Discuss
This article, I want to introduce what CORS is, what it means and the settings to make a simple CORS request in Laravel. Hopefully, these will give you a basic and applicable look into your problem. Thank you for following the article.