Adding 2FA to Laravel Passport based API

Viewed 2728

I was kind of surprised that I did not find any practical examples or best practices concerning two-factor authentication for laravel using passport.

As I would like to implement this step while a user logs in to the API and receives his access token I am not quiet sure how to approach in a secure manner.

The current process is as follows:

  1. User sees frontend login page (vue SPA).
  2. User enters username and password and hits submit.
  3. SPA sends request to API.
  4. API responds with access token or gives a not authenticated error if something was wrong.
  5. SPA can use access token to request other resources from API till token is invalidated.

As I would like to enable users to activate or deactivate 2FA individually (because some clients might not want to use it, some will), I would add a new column in my users table to store if 2FA is enabled or not.

But how to continue then? Should the user authenticate with username and password regularly as before, then API knows if he "needs" the second auth step and returns a flag so frontend knows to display another view to enter the token?

Thankful for any input :)

1 Answers

To quickly sum up my solution to this question: I have implemented a service class handling the OTP generation as well as user secret generation. The OTPs are compatible with the specified standard (I think RFC 6238, TOTP)

If a user is initialized for 2FA a secret will be generated and stored to database. The user has to validate it using the first code displayed via an authenticator app of his choice (like Google Authenticator). All the QR-Code generation etc is done by my frontend which is seperated from the API code.

As I am using Laravel Passport for API authentication I have extended the TokenGuard class to also handle an extra 2FA token - if 2FA is enabled for the user. Otherwise this will be skipped.

This 2FA token is generated uniquely on each successful code challenge after the user has logged in with his username/password combination and is send to the frontend to be also used within all upcoming requests (as seperate header).

This works pretty well for me and I did not need to implement any third party libraries at the end!

@ml59 thanks for your inputs which helped me get around!

Related