Laravel sanctum csrf cookie every request? - php

I'm using Laravel sanctum (former Airlock) and have a question about it. I read in the docs:
To authenticate your SPA, your SPA's login page should first make a
request to the /sanctum/csrf-cookie route to initialize CSRF
protection for the application:
axios.get('/sanctum/csrf-cookie').then(response => {
// Login... });
Once CSRF protection has been initialized, you should make a POST request to the typical Laravel /login route. This
/login route may be provided by the laravel/ui authentication
scaffolding package.
Does this mean that for every request I make, I should first check if the cookie has already been set? Because let's say I have a user that registers. Before making the POST request to register a user I should first make a GET request to get the CSRF-Cookie from my backend and then make the POST request to register the user.
Now the user gets redirected to the login webpage and is asked to login. Does the frontend then first have to check if there's a CSRF-Cookie, and if there isn't should it first again make the GET request to get the cookie?
This last bit also confuses me, because when calling the register method a user doesn't actually get logged in so the user has to be redirect to the login page to log in with the credentials the user just filled in to register which for me seems like a bad user experience, right?

I know it's been a while since this question was asked but just for anyone searching out there, No. You don't have to call /sanctum/csrf-cookie with every request. Before you make a post | put | delete... request, you can check to see if the XSRF-TOKEN cookie is set. If it is not, make a call to the /sanctum/csrf-cookie route (or whatever you have configured it to be). After the request has completed, (the XSRF-TOKEN cookie would have been set by your browser automatically) you can now proceed with the initial request.
The best place to do this is in an interceptor (if your http library supports it). I'm going to assume you are using axios.
// Install with 'npm i js-cookie'. A library that helps you manage cookies
// (or just build your own).
import Cookies from 'js-cookie';
// Create axios instance with base url and credentials support
export const axiosInstance = axios.create({
baseURL: '/api',
withCredentials: true,
});
// Request interceptor. Runs before your request reaches the server
const onRequest = (config) => {
// If http method is `post | put | delete` and XSRF-TOKEN cookie is
// not present, call '/sanctum/csrf-cookie' to set CSRF token, then
// proceed with the initial response
if ((
config.method == 'post' ||
config.method == 'put' ||
config.method == 'delete',
/* other methods you want to add here */
) &&
!Cookies.get('XSRF-TOKEN')) {
return setCSRFToken()
.then(response => config);
}
return config;
}
// A function that calls '/api/csrf-cookie' to set the CSRF cookies. The
// default is 'sanctum/csrf-cookie' but you can configure it to be anything.
const setCSRFToken = () => {
return axiosInstance.get('/csrf-cookie'); // resolves to '/api/csrf-cookie'.
}
// attach your interceptor
axiosInstance.interceptors.request.use(onRequest, null);
export default axiosInstance;
The XSRF-TOKEN cookie comes with a time of expiry. After that time, the browser deletes it. So as long as you can find the cookie, it is safe to make a request without calling /sanctum/csrf-cookie or whatever you have configured it to be.

When you get the csrf token, in the following request, laravel will update the token automatic, so you dont need focus this after axios.get('/sanctum/csrf-cookie').

Once you hit axios.get('/sanctum/csrf-cookie') API, after that you don't have to hit it again and again for every request, Because this/sanctum/csrf-cookie will save the XSRF token on browser and Axios will send it with the request.
You can learn about it in detail in this video: https://www.youtube.com/watch?v=8Uwn5M6WTe0

Related

Laravel passport: clear session from mobile

I'm using authorization code grant with PKCE. I tried to revoke the token via api without issue (the route is under auth:api middleware). However, the server side session is not over and when i try to login again, it skips the login form and jumps to the authorization prompt or just to callback page. I tried to create a route in the web middleware which kills the session but always stores the cookie 'laravel_session' and 'XSRF-TOKEN' and can't delete them.
I would like to let user click logout button from mobile app and user shall go through whole oauth2 flow when login again instead of skip the login form at server side.
public function logoutAPI(){
//clear server side session
Auth::guard('web')->logout();
Session::flush();
// logout and revoke mobile app token
Auth::user()->token()->revoke();
$tokenId = Auth::user()->token()->id;
$tokenRepository = app('Laravel\Passport\TokenRepository');
$refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');
$tokenRepository->revokeAccessToken($tokenId);
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
return response()->json([
'msg' => 'You have been succesfully logged out'
],200);
Noted that, I will not use other grant type as reference here https://oauth2.thephpleague.com/authorization-server/which-grant/
You may use prompt=login when redirecting for authorization on Laravel Passport >= 11.3 This causes the app to always prompt the user to re-login to the application, even if they already have an existing session.
Check docs for more info: https://laravel.com/docs/9.x/passport#requesting-tokens-redirecting-for-authorization

Laravel Http set custom user agent on request

I've got a cron set up in my Laravel project that makes a GET request to an endpoint, I'd like to set my own customer user agent that this request uses so that it can easily be identified amongst other potential requests to the same server.
My current code within my function is:
$response = Http::timeout($timeout)->get($url);
$code = $response->getStatusCode();
I'd like to set a custom user agent for this request like so:
Mozilla/5.0+(compatible; EXAMPLE/2.0; http://www.EXAMPLE.com/)
Can't seem to find anything in the docs to suggest how to do this
Since Laravel v8.8 (this pull request) you can use the withUserAgent method to set your own user agent.
Http::withUserAgent($userAgent)->get($url);
Before that you could set your own header like this:
Http::withHeaders(['User-Agent' => $userAgent])->get($url);

How to implement authentication on a REST architecture with Parse

I am currently redoing a legacy web application that uses the PHP Parse SDK, and I am in the login authentication part. In the old application, we used $ _SESSION and ParseToken when doing ParseUser::signIn() and ParseUser::currentUser() to check if you have a session with a valid token, however the new application is being made using the REST architecture, where one of the REST concepts is that the server must not keep state, that is, be stateless, and in that case it would be the client that would have to send the necessary data.
When searching the internet and forums, I saw that it is common for developers to authenticate with JWT, where the client would make a request for a server's route and the server would return a token, and through that token authentication would take place.
I even implemented something using Firebase / jwt-php, where the client [Postman] makes a request for the route /login sending via body [username, password] and in case of success, returns the token to be used in secure route requests.
NOTE: Code is as simple as possible, without validation and cleaning just to show the example.
Action /login
$username = $request->getParsedBody()['username'];
$password = $request->getParsedBody()['password'];
$userAuthenticated = ParseUser::logIn($username, $password);
$payload = [
'data' => $userAuthenticated,
'exp' => time() + 3600
];
$token = JWT::encode($payload, $_ENV['JWT_SECRET_KEY']);
echo json_encode(['token' => $token]);
And the protected routes have a middleware that checks if the time has expired, and if this has happened, an exception with a 401 code is launched.
So far so good, authentication works, the problem I don't know if it's right to do it this way, since I need to give a ParseUser::logIn(), just to generate a session in the database and I don't even use it this session to do some authentication, with the exception of operations in the bank, because from what I saw in the documentation, if there is no valid session in the database, the application will return invalid session token error and also when making the request for another route ParseUser::currentUser() returns null, and this may be a problem in the future.
Does anyone have any idea how I can implement authentication for a REST application made in PHP? I appreciate the help !!
I believe the easiest way would be just replacing the default session storage (which uses $_SESSION) to something else that stores the session in, for example, Redis. Reference: https://docs.parseplatform.org/php/guide/#session-storage-interface
But the way you are doing should also work. You will only have to make sure that, every time that a request comes, you will decode the JWT, get the Parse Session token from there, and use ParseUser::become to set the current user: https://docs.parseplatform.org/php/guide/#setting-the-current-user

How can I re-acquire a Shopify OAuth access token for a store that has previously installed my application?

I requested authorization for a public application to be able to access store data via the Shopify API.
The store successfully authorized my application via an authorization request URL such as
https://some-store.myshopify.com/admin/oauth/authorize?client_id=123abc&scope=read_inventory%2Cread_products&redirect_uri=http%3A%2F%mysite.com%2Fauth.php&state=123456
and the response was passed back to my application. This response (containing the code that can be exchanged for a permanent access token) was mishandled by my application (an error on the page meant that the access token was not stored).
Everything I read regarding requesting these tokens involves authorization by the store - but given the store has already authorized my application, passed back the code and that code has already successfully been exchanged for a token: is there a way my application can request that same token or a fresh one using my API keys given that the application is already authorized?
The only method I currently can find for requesting a token requires starting back at the beginning and fetching a code for exchange etc.
I working in PHP and using Luke Towers' php shopify wrapper
This stage was completed successfully:
function check_authorization_attempt()
{
$data = $_GET;
$api = new Shopify($data['shop'], [
'api_key' => '123',
'secret' => '456',
]);
$storedAttempt = null;
$attempts = json_decode(file_get_contents('authattempts.json'));
foreach ($attempts as $attempt) {
if ($attempt->shop === $data['shop']) {
$storedAttempt = $attempt;
break;
}
}
return $api->authorizeApplication($storedAttempt->nonce, $data);
}
$response = check_authorization_attempt();
and I would have been able to read the access token from :
$access_token = $response->access_token;
But this was the stage at which my application hit an error in accessing a database in which to write said token.
I cannot repeat it without repeating the auth request because the data in $_GET that's passed to this function comes from Shopify's response to the shop owner authorizing the access, and includes amoung other things the code for exchange.
You have to re-ask for authorization. It is no one's fault but yours that your persistence layer code was incorrect. So there is nothing you can do to change that. Ensure your code works. Since the client has no token in your App persistence layer, your App will retry the authorization token exchange. They do not have to delete your App first. So basically, the next time your client tries to use the App, YES they will asked to approve it, but who cares, they will, and you'll get a good auth token to store. You have fixed your code (right), so that will work. You are one step closer to glory.
Shopify does return the Permanent Access Token, but the ACCESS_MODE must be "Offline" for the token to be permanent.
With ACCESS_MODE offline, your app receives the permanent access token
to make requests whenever you want, without the user's permission.
Documentation:
https://shopify.dev/tutorials/authenticate-with-oauth#step-2-ask-for-permission
https://shopify.dev/concepts/about-apis/authentication#api-access-modes

trying to have user attend event via api

I'm trying to use Facebook's API code to have a user attend an event, but it's not working. I can run the post comment on my browser window but as a php script it's not working
$fields = array('access_token'=>$access_token);
$result = HTTP_POST("https://graph.facebook.com/4XXXXX663/invited?access_token=$access_token",// URL to query
$fields, // POST fields; associative array
USER_AGENT, // user-agent value
"", // cookie storage and retrieval
"", // proxy; type:ip:port[:user:pass]; supported types: http, socks5
true, // return the data or not
false, // include headers in the return data
"", // set value for REFERER header
true, // automatically follow "redirects" ("Location" header)
false); // enable or disable multipart post (if uploading, set to true)
You're passing access_token with the URL as well as the POST data.
Remove the ?access_token=$access_token part in your URL and try again.
Also it would be good if you could post the error you're getting, So we can see what's the actual issue and also make sure you post that HTTP_POST function so we can know if the cURL settings are correct (I'm assuming this function is using cURL).
EDIT 1: Removed, Just realized you're trying to attend an event and not invite.
Edit 2: You need to ask your users to give rsvp_event permissions in order to make them attend your event via your App. You also need to make an HTTP POST to EVENT_ID/attending (This is for attending the event) and not to EVENT_ID/invited (This is for inviting users/friends to your event).

Categories