1

I've created a Laravel backend with an API. In addition, I added Laravel passport for authentication.

Right now I want to access the API with my ReactJS Frontend application and with a React-Native application. The ReactJS application is on the same server as the Laravel backend.

Now I'm looking for the best way to connect everything with the best security.

I've been looking around and checking tutorials, HowTos, ... for over a week now and I don't know what is best.

Right now my "setup" is, because of some threads and stuff, like this:

I checked the Laravel documentation and several HowTos and implemented the Password Grant Client in my application:

public function login(Request $request){

      $http = new GuzzleHttp\Client;

      $response = $http->post(env('APP_URL') . '/oauth/token', [
        'form_params' => [
          'grant_type' => 'password',
          'client_id' => env('PASSWORD_CLIENT_ID'),
          'client_secret' => env('PASSWORD_CLIENT_SECRET'),
          'username' => $request->input('email'),
          'password' => $request->input('password'),
          'scope' => '',
        ],
      ]);

      $responseJSON = json_decode($response->getBody(), true);
      $output = array(
        'access_token' => $responseJSON['access_token'],
        'expires_in' => $responseJSON['expires_in']
      );

      return response($output)
                ->header('Content-Type', 'application/json')
                ->cookie('refreshToken', $responseJSON['refresh_token'], 14400, null, null, true, true);
    }

and for refreshing the token:

public function tryRefresh(Request $request) {
      $http = new GuzzleHttp\Client;

      $refreshToken = $request->cookie('refreshToken');

      $response = $http->post(env('APP_URL') . '/oauth/token', [
        'form_params' => [
        'grant_type' => 'refresh_token',
        'refresh_token' => $refreshToken,
        'client_id' => env('PASSWORD_CLIENT_ID'),
        'client_secret' => env('PASSWORD_CLIENT_SECRET'),
        'scope' => '',
        ],
      ]);

      $responseJSON = json_decode($response->getBody(), true);
      $output = array(
        'access_token' => $responseJSON['access_token'],
        'expires_in' => $responseJSON['expires_in']
      );

      return response($output)
                ->header('Content-Type', 'application/json')
                ->cookie('refreshToken', $responseJSON['refresh_token'], 14400, null, null, true, true);
    }

Access Token is set to 30 min and refresh token to 10 days. Everything works but I don't know if this is a good practice because:

  • I read that JS applications can't save the access token securely
  • Refresh Token should not be saved in cookies
  • Laravel provides something for JS Frontends
  • (and // or) For consuming the API as a first party app, this procedure is not needed

So my question is: What is a good/best practice for my kind of use case, so the authentication is secure and working on both (ReactJs Web-App and React-Native Mobile App).

2 Answers 2

1

Given the frameworks you want to use, the token based authentication you have implemented IS good practice.

You can also look into JWT as an alternative to Password Grant type authentication.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your answer! What about the refresh token as a Cookie? Good practice, too?
0

The idea is to do as follows:

  1. Create token on the backend
  2. Send token to client on login (in my Vue.js application expiration is set to 1 year) and save it in cookies with secure option, not the httpOnly one (this will never pass the cookie value to the client)
  3. Pass the token as Authorization header to the backend from your client, i'm using axios, so it is as simple as request.headers.common['Authorization'] = `Bearer ${token}`;
  4. Implement refresh token logic on the backend, no need to store it on the client side, basically whenever your main token will expire, the next request to the server with expired token should either refresh it, or redirect user back to login page (i've chosen the second approach to eliminate possible issues with bad logic on the server side)

Here are the examples of my log in and log out methods:

/**
 * Authenticate user.
 *
 * @param Request $request
 *
 * @return JsonResponse
 */
public function authenticate(Request $request): JsonResponse
{

    $validator = Validator::make($request->all(), [
        'email' => 'required|string|email',
        'password' => 'required|string|min:8',
        'remember_me' => 'boolean',
    ]);

    if ($validator->fails()) {
        return $this->sendError('errors.invalid_request', $validator->errors()->toArray(), Response::HTTP_BAD_REQUEST);
    }

    $credentials = request(['email', 'password']);

    if (!\Auth::attempt($credentials)) {
        return $this->sendError('errors.account.invalid_credentials', [], Response::HTTP_FORBIDDEN);
    }

    $user = $request->user();
    if (!$user->active) {
        return $this->sendError('errors.account.inactive', [], Response::HTTP_CONFLICT);
    } elseif (null !== $user->deleted_at) {
        return $this->sendError('errors.account.deleted', [], Response::HTTP_CONFLICT);
    }

    $createdToken = $user->createToken(sprintf('Access token for %s', $user->username));
    $token = $createdToken->token;

    if ($request->remember_me) {
        $token->expires_at = now()->addYears(1);
    }

    $token->save();

    return $this->sendResponse('account.status.authenticated', [
        'access_token' => $createdToken->accessToken,
        'token_type' => 'Bearer',
        'expires_at' => Carbon::parse($createdToken->token->expires_at)->toDateTimeString(),
    ]);
}

/**
 * Logout user and revoke access token.
 *
 * @param Request $request
 *
 * @return JsonResponse
 */
public function logout(Request $request): JsonResponse
{
    $request->user()->token()->revoke();

    return $this->sendResponse('account.status.logged_out');
}

2 Comments

So you don‘t use a refresh token? I read that long life Access token are bad
You obviously can, and probably should, but i forgot to mention that i also using "some sort of" ip matching, if user logs in from another location -> flush token and ask to login again. So since it is the same device that accessed the website before, for me personally, long life access tokens are fine. You can recall them any time, it is all on you. Every request to user data on the SPA/PWA/whatever should be made whenever you are accessing sensitive information, so you can detect "malicious" user at any time (if you are worried about that)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.