Buy
Buy

API Auth: Do you Need it? And its Parts

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Before we dive into the code, we need to have a heart-to-heart about API authentication. Because... I think there's some confusion out there that tends to make people over-complicate things. And I never want to over-complicate things.

Do you Need API Authentication

First, you need to ask yourself a very important question:

Do you actually need an API token authentication system?

There's a pretty good chance that the answer is... no, even if your app has API endpoints:

... lines 1 - 11
class AccountController extends BaseController
{
... lines 14 - 24
/**
* @Route("/api/account", name="api_account")
*/
public function accountApi()
{
... lines 30 - 31
return $this->json($user, 200, [], [
'groups' => ['main'],
]);
}
}

If you're creating API endpoints solely so that your own JavaScript for your own site can use them, then, you do not need an API token authentication system. Nope! Your life will be much simpler if you use a normal login form and session-based authentication.

Yep! You probably already know, that, once you login via a login form, you can instantly make authenticated AJAX requests from JavaScript, because those requests send the session cookie. So, if the only thing that needs to use your API is your own JavaScript, just use LoginFormAuthenticator.

Oh, and if you need to be fancier with your login form, sure! You can totally use JavaScript to make the login form submit via AJAX. Nothing would need to change in your authenticator, except that you would probably want to send back JSON on success, instead of redirecting:

... lines 1 - 19
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 22 - 74
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->router->generate('app_homepage'));
}
... lines 83 - 87
}

You would also override onAuthenticationError() and the start() method to do the same. We'll learn more about those methods soon.

Of course, even if your JavaScript will be the only thing using your API, you can still build an API token authentication system, if you want. And if you need other things to be able to access your API, then you need a token system.

Two Sides of API Token Authentication

If you're still here, then you've either decided that you do need an API token authentication system, or you just want to nerd out with us on this topic. Me too!

This brings us to important topic number 2! An API token authentication system has two, quite unrelated parts. The first is how your app processes an existing API token and logs in the user. The second is how those API tokens are created and distributed.

Part 1: Processing an API Token

For the first part, no matter how you build it, an API token is just a string that is somehow connected to a User in your system. The client that makes the request sets the token string on a header and then they become authenticated as that User. There are some variations on this, like, giving tokens "scopes" or "permissions" so that they can only do some things that a user can do, but that's the basic idea.

The way that the token string is related to the user can be done in a few different ways. For example, you could have an API token database table where each random API token has a relationship to a row in the user table. It's simple: our app reads the token string from a header, finds that API token in the database, finds the User it's related to, and authenticates as that user. We're going to build exactly this.

Another variation is JSON web tokens. In this case, instead of the token being a random string, the user's information - like the user id - is used to create a signed string. In that case, your app reads the header, verifies the signature on the string, and uses the id inside that string to query for the User.

Anyways, that is the first part of API token authentication: designing your app to be able to read API tokens from an API request, and use that information - somehow - to find the correct User and authenticate them.

Part 2: Creating & Distributing API Tokens

The second part of an API authentication system asks this question:

How are these API tokens created and distributed?

It turns out that this is a totally separate conversation. And, once again, there are several valid answers. I'll give you 3 examples with when each should probably be used. Actually, the GitHub API is an example of a system that allows you to do all three of these.

First, you could allow API tokens to be created through a web interface. Like, a user logs in, they navigate to some API token page, and then they create one or more API tokens that are tied to their account. This solution is dead simple. The negative is that there is no automated way to create an API token: you can't write a script that can create them. It must be done manually.

Second, you could write an API endpoint whose jobs is to create & return tokens. In this example, you would send your email & password to the API endpoint, it would validate them, then create & return the token. This is still pretty simple, but now it's programmable: you can write a script that can create tokens on its own. The downside is that this solution can't be used by third parties. What I mean is, it's okay for the user to write some code that sends their own email and password to an API endpoint in order to create a token. But, if some third-party were building an iPhone app for your site, that app should not use this method. Why? Because it would require the user to enter their email & password directly into the app, so that it could send the info to our API. Ideally, we never want users to give their password to a third-party.

This leads us to the third way of creating & distributing tokens: OAuth2. If you need third-parties to be able to securely create & get API tokens for your users, then you probably need OAuth. The only negative is that OAuth is more complex.

Phew! So, the whole second part of API token authentication... well, really has nothing to do with authentication at all! It's more about how these secrets keys are created and handed out to who needs them. So, we are not going to talk about that part of API authentication.

But we are going to build the first part: the true authentication part. Let's get to work!

Leave a comment!

  • 2019-03-05 Diego Aguiar

    Ah ok, so the website users will login in from the default login form and the app will use tokens? If that's your case, then I think what Ryan did on this tutorials is exactly what you need

  • 2019-03-05 Marcus

    Hi Diego,
    good point. There has to be some functionality to check if the user still have a valid token.

    The basic requirement is as follows: there is a basic website which provides a default login form. There is a protected area which displays some sort of data within tables.
    There is also an ios app which could push data via POST into the database. I followed this extremly useful tutorial (Thanks Ryan for this cool tutorial - really appreciate it! ) and did the authentication via token. The app has also a login screen with email and password. I have to send both to the endpoint, check if the user is valid and return a valid token.

  • 2019-03-04 Diego Aguiar

    Hey Marcus

    I think it depends on your situation, if you really need an endpoint for creating new tokens, then I think you can bypass Guard, but you should consider any possible exploits that your endpoint may have. What happens if the user still have a valid token?

  • 2019-03-04 Marcus

    Hi guys,

    just a little question about "Part 2: Creating & Distributing API Tokens". I've created a little script for the second option, ".. could write an API endpoint whose jobs is to create & return tokens."


    /**
    * @Route("/api/token", name="api_get_token", methods={"POST"})
    */
    public function newTokenAction(Request $request, EntityManagerInterface $em, UserPasswordEncoderInterface $passwordEncoder)
    {
    $user = $em->getRepository('App:User')->findOneBy(['email' => $request->request->get('email')]);

    if (!$user) {
    return new JsonResponse([
    'message' => 'Not found!',
    ], 401);
    }

    $isValid = $passwordEncoder->isPasswordValid($user, $request->request->get('password'));

    if (!$isValid) {
    return new JsonResponse([
    'message' => 'Invalid credentials',
    ], 401);
    }

    $apiToken = new ApiToken($user);

    $em->persist($apiToken);
    $em->flush();

    return new JsonResponse(['token' => $apiToken->getToken()]);
    }

    Though it feels like "bypassing" the whole guard thing. Is there maybe a better solution?

    *Phew, sorry this online-code-editor drives me crazy! Finally fixed it ;)

    Cheers, Marcus!

  • 2019-01-08 aguidis

    Hey weaverryan, Thank you for your detailed feedback. It totally makes sense !

  • 2019-01-07 weaverryan

    Hey aguidis!

    Yea, this can be a very confusing area :).

    > In your implementation each User can have many ApiTokens. Do you have a strategy to limit the maximum token number ? I mean is it "fine" to create a new token each time the previous one is expired and keep them all in the database ?

    I don't see any reason to limit the number of tokens - allowing the user to create more tokens isn't really less secure - if a "bad" person can create an access token, then allowing them to create many doesn't really change anything. And so yes, I think it's fine to create a new token each time the previous one expires and keep them all in the database. Once a token expires, it's useless - so you either delete it, or keep it for some sort of logging/legacy purpose. For example, you might decide that you want to log that a specific action was taken by an API token if you ever think that you might need to "track down" which access token was the cause of something that happened in the system. It's up to you.

    > So I'm a bit confused because I read some articles explaining that to store credentials in mobile devices you can use the Keychain Services for iOS and SharedPreferences for Android. So it looks "ok" to store credentials but again I'm not an expert in mobile development.

    I think your approach of sending a POST with the credentials to your API to get a token (that is then used for all other endpoints) is 100% perfect. The key line is this:

    > But, if some third-party were building an iPhone app for your site,

    YOUR app is NOT a third-party app :). You own both the API and the app. Specifically, you don't need to worry that the app is, for example, collecting the plain-text passwords and sending them to some bad party. Having an iPhone app where the user types in their email/password so that the app can send to your endpoint is basically the same as allowing people to enter their email/password into a form on YOUR site and hitting submit. That's totally normal :). What is NOT normal would be for me to put a login form on MY site, where people entered their email/pass for YOUR site, and I sent them to your site behind the scenes. That would be a crazy security hole! It's the exact same with apps.

    Let me know if that makes sense!

    Cheers!

  • 2019-01-03 aguidis

    Hey guys,

    thanks for the great overview from "API Auth: Do you Need it? And its Parts"

    I just have a few question regarding the "ApiToken Entity" and "Creating & Distributing API Tokens" courses.

    - In your implementation each User can have many ApiTokens. Do you have a strategy to limit the maximum token number ? I mean is it "fine" to create a new token each time the previous one is expired and keep them all in the database ?

    - I'm working on the internal API for a delivery comparny that wishes a mobile app. The first thing I thought was "I should force user to athenticate themselves before using any endpoints".
    In my head the mobile application would first ask a token via a POST request to /api/authenticate (with the crendentials in the body)
    And then tje client will consume the endpoints with the given token.

    Then I read this part:

    "But, if some third-party were building an iPhone app for your site, that app should not use this method. Why? Because it would require the user to enter their email & password directly into the app, so that it could send the info to our API. Ideally, we never want users to give their password to a third-party."

    So I'm a bit confused because I read some articles explaining that to store credentials in mobile devices you can use the Keychain Services for iOS and SharedPreferences for Android. So it looks "ok" to store credentials but again I'm not an expert in mobile development.

  • 2018-12-10 Galen Senogles

    Thank you very much for the very in-depth and through answer! I really appreciate it. This clears up a lot of stuff.

  • 2018-12-09 weaverryan

    Hey Galen Senogles!

    Yes, I an at least answer some questions about this to get you moving! First, in this chapter, we are implementing token-based authentication, which will look "similar" to how authentication works on a site that implements an "OAuth server". BUT, to make things very clear, if you want to allow your users to login via Facebook, you do NOT need an OAuth server - you just need to create an authenticator that is able to use an "oauth client" to communicate with some other OAuth server (e.g. Facebook).

    > Ideally I'd like to allow users to be able to register an account the traditional way via a registration form username/password, or be able to connect their facebook or google account and gain access to the site that way.

    We do this here on SymfonyCasts, so I can give you some hints :). Basically, allowing login via username/password and also via OAuth (e.g. Facebook) are 2 totally independent "options" for authentication and work quite well together. You will have one authenticator that allows users to login via an email/password and a second authenticator that allows users to "login via Facebook". In https://github.com/knpunive..., we have a spot on the docs that shows what a Guard authenticator setup might look like to login with Facebook. You will follow:

    * Step 3) Use the Client Service - https://github.com/knpunive... - but leave the the connectCheckAction method blank... because we will do that logic in the authenticator instead.

    * Authenticating with Guard - https://github.com/knpunive...

    The entire flow is this:

    A) User clicks "Login with Facebook" on your site to go to /connect/facebook.
    B) In that controller, we redirect to Facebook (see the linked docs - we show exactly how to do this). At this point, we have already configured the bundle so that Facebook knows to redirect the user back to /connect/facebook/check after success (the connect_facebook_check) route).
    C) After the user approves your app, they are redirected back to /connect/facebook/check.
    D) Your authenticator sees this URL, and makes an API request to fetch the User data, and then you either find the existing User in your database and return them from getUser() or create a new User, save them, and then redirect. All of this is shown in the example on that page.

    And, it's a bit bigger, but I'd also recommend going through the OAuth tutorial - http://symfonycasts.com/scr... - then you will *really* understand how all of this works.

    Alternatively, you can use https://github.com/hwi/HWIO.... It gives you some more "automatic" features and may be easier to set up, but it's harder to understand what's going on and also to extend (that's why the oauth2 client bundle exists).

    Let me know if that helps!

    Cheers!

  • 2018-12-05 Galen Senogles

    Is it possible there ever might be a write up on implementing OAuth2 into this or a project similar to this? My issue is I'm a bit confused on if you are intending to implement OAuth2, so for example connecting a facebook account to the site. I don't know if it's going to be possible to implement the site as described throughout this tutorial where you register with a username/password and that creates a new user. If I wanted to implement OAuth2 and allow a user to essentially register an account via facebook, how exactly would that fit into this flow?

    Is it possible to implement it to a site built like this tutorial that already has a user registration system? Documentation around the internet about this seems pretty sparse. Tutorials about this are mostly all outdated using symfony 2 and 3. I see there is a package knpuniversity/oauth2-client-bundle, but I'm unsure how I'd go about implementing that into this type of project. Ideally I'd like to allow users to be able to register an account the traditional way via a registration form username/password, or be able to connect their facebook or google account and gain access to the site that way.

    Is there anyway a write-up could be done that illustrates how one would go about doing that?

    Thank you.

  • 2018-10-26 Victor Bocharsky

    Hey Matt,

    Awesome! We glad it helpful for you. Actually, we've got many questions about this lately, so we decided to explain it in this course :)

    Cheers!

  • 2018-10-26 Matt Johnson

    This is what I was looking for :P