Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
API Token Authentication in Laravel 5.2 & 5.3

I recently had the need to write a small url shortening application. I am aware that this problem has been solved quite a few times before, but what is being a developer if not reinventing the wheel just for the heck of it? Custom CMS anyone?

Knowing that this was going to be a tiny RESTful API and also knowing that Laravel 5.2 had API rate limiting built in, I was eager to give it a try. Taylor Otwell being Taylor Otwell shipped 5.2 with the rate limiting defaults set up out of the box and I had my application building out short url's in a matter of minutes. The problem for me came when I wanted to start associating those short urls with a user.

Typically my applications have a UI and authentication is done through a simple login page. Obviously for a RESTful API, having a login page isn't ideal. Instead, my hope was to have users append an api_token to the end of their query string and use that to authenticate their request. I was happy to find that 5.2 also ships with a TokenGuard(link) class that allows you to do exactly that, but the documentation on getting it to work was a bit thin, so here you go.

Set up Token based Authenticaton

1. Add an api_token

The first think you need to do is to add an api_token column to your users table. If you are just starting your application you can likely get away with modifying the user migration that ships with Laravel to include your new column.

	// add this to your users_table migration
	$table->string('api_token', 60)->unique();

Note: Be sure to generate and assign an api_token to new users. Something like str_random(60) should be sufficient.

2. Wrap your routes

Second, we need to make sure that any routes that will be using Token Authentication are being protected by the auth:api middleware. Use the following route group as an example of what your routes might look like.

	Route::group(['prefix' => 'api/v1', 'middleware' => 'auth:api'], function () {
    	Route::post('/short', 'UrlMapperController@store');
	});

Note: Typically when protecting routes from unauthenticated users, we use the auth middleware, but by appending :api to the end we are telling Laravel that we want to use the driver for the api guard which is set up in the config/auth.php and is defaulted to token.

At this point, any routes wrapped with your auth:api middleware are only accessible to those that visit the route with a valid api_token in their request.

3. Getting the User

To get the authenticated user for this API request, use the following snippet:

	Auth::guard('api')->user();

Just like when we called the middleware, we have to let Laravel know that we want the api guard instead of the default web guard.

Extras

In the App\Http\Middleware\Authenticate middleware, you might want to change the following lines:

Update: This has been merged into 5.2. Check out the current Authenticate Middleware here

	// Original
	    if ($request->ajax()) {
	        return response('Unauthorized.', 401);
	    } else {
	        return redirect()->guest('login');
	    }
	    
	// Updated
		if ($request->ajax() || $request->wantsJson()) {
	        return response('Unauthorized.', 401);
	    } else {
	        return redirect()->guest('login');
	    }

This will return a 401 status code to unauthorized API requests instead of redirect it to a login page.

Lastly, if you are planning on primarily using the TokenGuard to authenticate requests, change the default guard in config/auth.php to be api instead of web. That should prevent you from having to tell Laravel to use the api version of the middleware or guard since Laravel will use by default what you have set in config/auth.php.

Wrapping Up

Well there you have it, authenticating users to your api using nothing more than an api_token in the request and an api_token column on your user table. Hopefully this will save you the time it took me digging through the TokenGuard and Issues to figure it out.

published: true
preview: I was happy to find that Laravel 5.2 & 5.3 ships with a TokenGuard class that allows users to authenticate by sending an api_token along with their request, but the documentation on getting it to work is a bit thin, so here you go.
@rodrigopedra

This comment has been minimized.

Show comment Hide comment
@rodrigopedra

rodrigopedra Jan 21, 2016

Will you sen a pull request to add the $request->wantsJson() check to the shipped middleware?

I think it is a good idea, thanks for this tip. And nice article.

Will you sen a pull request to add the $request->wantsJson() check to the shipped middleware?

I think it is a good idea, thanks for this tip. And nice article.

@carc1n0gen

This comment has been minimized.

Show comment Hide comment
@carc1n0gen

carc1n0gen Jan 21, 2016

Is there any way to use this api_token authentication, but send the token in request body instead of query parameter?

Is there any way to use this api_token authentication, but send the token in request body instead of query parameter?

@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

JacobBennett Jan 22, 2016

@rodrigopedra can do. I'm going to add a PR for the docs as well to make it a little more clear.

Owner

JacobBennett commented Jan 22, 2016

@rodrigopedra can do. I'm going to add a PR for the docs as well to make it a little more clear.

@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

JacobBennett Jan 22, 2016

@carc1n0gen yes, if you look at https://github.com/laravel/framework/blob/master/src/Illuminate/Auth/TokenGuard.php#L81-L83 you can see that it uses the input which checks both the get and post for the api_token

Owner

JacobBennett commented Jan 22, 2016

@carc1n0gen yes, if you look at https://github.com/laravel/framework/blob/master/src/Illuminate/Auth/TokenGuard.php#L81-L83 you can see that it uses the input which checks both the get and post for the api_token

@juukie

This comment has been minimized.

Show comment Hide comment
@juukie

juukie Jan 22, 2016

Good to know! Thanks :)

juukie commented Jan 22, 2016

Good to know! Thanks :)

@juukie

This comment has been minimized.

Show comment Hide comment
@juukie

juukie Jan 22, 2016

@rodrigopedra it has been merged :)

juukie commented Jan 22, 2016

@rodrigopedra it has been merged :)

@mattkoch614

This comment has been minimized.

Show comment Hide comment
@mattkoch614

mattkoch614 Jan 23, 2016

What about if a third party gains access to the api token? It looks like they can then make requests without being "authenticated" in any way.

What about if a third party gains access to the api token? It looks like they can then make requests without being "authenticated" in any way.

@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

JacobBennett Jan 24, 2016

@noleafclover614 correct as the api_token is the secret used to authenticate the user. You would want to caution your users to protect this api_token just like they would a password.

Owner

JacobBennett commented Jan 24, 2016

@noleafclover614 correct as the api_token is the secret used to authenticate the user. You would want to caution your users to protect this api_token just like they would a password.

@izdiwho

This comment has been minimized.

Show comment Hide comment
@izdiwho

izdiwho Jan 24, 2016

what about putting it in the header? like how dingo does it with the authorization header. is that possible?

izdiwho commented Jan 24, 2016

what about putting it in the header? like how dingo does it with the authorization header. is that possible?

@paulocastellano

This comment has been minimized.

Show comment Hide comment
@paulocastellano

paulocastellano Jan 24, 2016

You can post any method example for use with both Auth (session) and Auth (Api) ?

You can post any method example for use with both Auth (session) and Auth (Api) ?

@andreliem

This comment has been minimized.

Show comment Hide comment
@andreliem

andreliem Jan 24, 2016

Thank you for this. Been looking for a simple solution to tokens. It's useful if you're building SPAs as well.

Thank you for this. Been looking for a simple solution to tokens. It's useful if you're building SPAs as well.

@baileylo

This comment has been minimized.

Show comment Hide comment
@baileylo

baileylo Jan 25, 2016

Considering laravel does not encrypt this token in any way, is this really safe way to implement authenticated requests? To me it looks like laravel is storing the password in plain text.

Considering laravel does not encrypt this token in any way, is this really safe way to implement authenticated requests? To me it looks like laravel is storing the password in plain text.

@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

JacobBennett Jan 26, 2016

@baileylo this is a great question. Looked at this article for possible solutions but you are correct, currently they are not encrypted.

You have any ideas for how this could be improved? I'm sure @taylorotwell would entertain a PR on it if it was solid.

Owner

JacobBennett commented Jan 26, 2016

@baileylo this is a great question. Looked at this article for possible solutions but you are correct, currently they are not encrypted.

You have any ideas for how this could be improved? I'm sure @taylorotwell would entertain a PR on it if it was solid.

@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

JacobBennett Jan 26, 2016

@andreliem same here. Had some trivial stuff that was just wide open, but this adds a layer of security that helps me sleep at night 👍

Owner

JacobBennett commented Jan 26, 2016

@andreliem same here. Had some trivial stuff that was just wide open, but this adds a layer of security that helps me sleep at night 👍

@calebeaires

This comment has been minimized.

Show comment Hide comment
@calebeaires

calebeaires Jan 31, 2016

Now things are more happy! What about built in CORS on this new api Gaurd?

Now things are more happy! What about built in CORS on this new api Gaurd?

@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

JacobBennett Feb 1, 2016

@calebeaires I am not aware of a CORS middleware out of the box with Laravel but you could certainly make one and PR it if you wanted. Could be a great addition. Know I have had to write that a few times in the past.

Owner

JacobBennett commented Feb 1, 2016

@calebeaires I am not aware of a CORS middleware out of the box with Laravel but you could certainly make one and PR it if you wanted. Could be a great addition. Know I have had to write that a few times in the past.

@JacobBennett

This comment has been minimized.

Show comment Hide comment
@JacobBennett

JacobBennett Feb 10, 2016

@simondepelchin that looks like an interesting way to solve the problem. I can't think of any catches for why this wouldn't work but I'm sure @taylorotwell would have more insight into that. You should submit a PR and see what he says. Looks like an interesting fix.

Owner

JacobBennett commented Feb 10, 2016

@simondepelchin that looks like an interesting way to solve the problem. I can't think of any catches for why this wouldn't work but I'm sure @taylorotwell would have more insight into that. You should submit a PR and see what he says. Looks like an interesting fix.

@playstation9

This comment has been minimized.

Show comment Hide comment
@playstation9

playstation9 Feb 11, 2016

Looks like token guard is just a simple token solution which is like using a password. TokenGuard is looking for the token in 3 places:

  1. in the URL for parameter ?api_token=XXX
  2. in the header for "Authorization: Bearer XXX". Which is used in JWT, Oauth, etc.
  3. in the header for "Authorization: Basic XXX". Which is Basic HTTP auth where XXX is base64 encoded username:password. The password is used as the token.

This doesn't prevent DB looking on every API call or I am missing something? How is this different than the clients sending "Cookie: somesessionid" in the header and the server looking up the session id in a file or DB? Where as JSON Web tokens decode the token to validate the user without a need to call DB. The token signature is encrypted by the server and can only be decrypted by it.

Looks like token guard is just a simple token solution which is like using a password. TokenGuard is looking for the token in 3 places:

  1. in the URL for parameter ?api_token=XXX
  2. in the header for "Authorization: Bearer XXX". Which is used in JWT, Oauth, etc.
  3. in the header for "Authorization: Basic XXX". Which is Basic HTTP auth where XXX is base64 encoded username:password. The password is used as the token.

This doesn't prevent DB looking on every API call or I am missing something? How is this different than the clients sending "Cookie: somesessionid" in the header and the server looking up the session id in a file or DB? Where as JSON Web tokens decode the token to validate the user without a need to call DB. The token signature is encrypted by the server and can only be decrypted by it.

@mtpultz

This comment has been minimized.

Show comment Hide comment
@mtpultz

mtpultz Feb 16, 2016

Nice article. How easy would it be to integrate JWT tokens using a custom Guard and Driver? Seems like the JWT-Auth package could really leverage this bit of Laravel's API and make the setup for JWT-Token use a bit simpler.

mtpultz commented Feb 16, 2016

Nice article. How easy would it be to integrate JWT tokens using a custom Guard and Driver? Seems like the JWT-Auth package could really leverage this bit of Laravel's API and make the setup for JWT-Token use a bit simpler.

@nicktc

This comment has been minimized.

Show comment Hide comment
@nicktc

nicktc Mar 10, 2016

@baileylo there is difference between encrypting and hashing. Passwords are hashed (one-way) with the purpose that you are not storing the actual plain password, but only a hashed version of it. Which is good in case your db is compromised for example. In the API token case you can better compare it with the session ID, which is similar like a token. But it's a secure token; only your browser and the server knows about it. Losing the session ID will give someone access to the application you are logged into. I think the same is true for the API token. Only the application and the client knows about this. I would only send it over HTTPS, so it's being send encrypted.

nicktc commented Mar 10, 2016

@baileylo there is difference between encrypting and hashing. Passwords are hashed (one-way) with the purpose that you are not storing the actual plain password, but only a hashed version of it. Which is good in case your db is compromised for example. In the API token case you can better compare it with the session ID, which is similar like a token. But it's a secure token; only your browser and the server knows about it. Losing the session ID will give someone access to the application you are logged into. I think the same is true for the API token. Only the application and the client knows about this. I would only send it over HTTPS, so it's being send encrypted.

@pay2all

This comment has been minimized.

Show comment Hide comment
@pay2all

pay2all Mar 11, 2016

its redirect to login page when using get method, any solution to display error message??

pay2all commented Mar 11, 2016

its redirect to login page when using get method, any solution to display error message??

@bruno-rodrigues

This comment has been minimized.

Show comment Hide comment
@bruno-rodrigues

bruno-rodrigues Apr 2, 2016

the wantsJson() part saved my day!! Thank you!

the wantsJson() part saved my day!! Thank you!

@michaeljcoyne

This comment has been minimized.

Show comment Hide comment
@michaeljcoyne

michaeljcoyne Apr 19, 2016

Does the column have to be called api_token? What if you want to call it something else?

Does the column have to be called api_token? What if you want to call it something else?

@malhal

This comment has been minimized.

Show comment Hide comment
@malhal

malhal May 9, 2016

Unless you use middleware 'api' I don't think you will get any throttling at all. To me it doesn't appear to be set up out of the box. I believe you have to create a mapApiRoutes method similar to mapWebRoutes to RouteServiceProvider and call it from map() if Request::is('api/*');

malhal commented May 9, 2016

Unless you use middleware 'api' I don't think you will get any throttling at all. To me it doesn't appear to be set up out of the box. I believe you have to create a mapApiRoutes method similar to mapWebRoutes to RouteServiceProvider and call it from map() if Request::is('api/*');

@liveNetworks

This comment has been minimized.

Show comment Hide comment
@liveNetworks

liveNetworks May 11, 2016

Nice article, but....

I need (I am sure I am not the only one) multiple tokens by user, so the user might create token for each device, and can revoke token for particular device.

Nice article, but....

I need (I am sure I am not the only one) multiple tokens by user, so the user might create token for each device, and can revoke token for particular device.

@ilvalerione

This comment has been minimized.

Show comment Hide comment
@ilvalerione

ilvalerione May 20, 2016

I have some problem with Policies. $this->authorize() method throw exception

I have some problem with Policies. $this->authorize() method throw exception

@gargonlinejobs

This comment has been minimized.

Show comment Hide comment
@gargonlinejobs

gargonlinejobs May 22, 2016

I have two database tables in MySQL.

User Table
Class Table
I have a working code in Laravel 5.2 on localhost, where I can see Class screen only after authentication. So far everything is working fine.

Now, I have to access the list of class, Add/Update Class functions from Android App.

I am thinking to use Token Based authentication. I saw the User table that Laravel provides when we create a new project. We have remember_token in User Table. I was thinking to use same token for Android and for Website.

Here the problem is : Token will be expired if you logout from the website and if I use same token, then expired token can not be used in android requests even if Android has done the authentication already.

Please suggest the correct way to use Token based authentication

or I should create a new column called api_token in User Table. and then after authentication, I can use same token to send request from Android to Server and for responding from server to Android. Here I have one problem.

Can the token be stolen?

In that case should I update the api_token in db every time I get the request from Android and updated api_token should be passed from server to Android?

I have two database tables in MySQL.

User Table
Class Table
I have a working code in Laravel 5.2 on localhost, where I can see Class screen only after authentication. So far everything is working fine.

Now, I have to access the list of class, Add/Update Class functions from Android App.

I am thinking to use Token Based authentication. I saw the User table that Laravel provides when we create a new project. We have remember_token in User Table. I was thinking to use same token for Android and for Website.

Here the problem is : Token will be expired if you logout from the website and if I use same token, then expired token can not be used in android requests even if Android has done the authentication already.

Please suggest the correct way to use Token based authentication

or I should create a new column called api_token in User Table. and then after authentication, I can use same token to send request from Android to Server and for responding from server to Android. Here I have one problem.

Can the token be stolen?

In that case should I update the api_token in db every time I get the request from Android and updated api_token should be passed from server to Android?

@raidenz

This comment has been minimized.

Show comment Hide comment
@raidenz

raidenz May 26, 2016

maybe i can add some point Route::post('/short', 'UrlMapperController@store'); post will need csrf token, if you want to disable it on your link set protected $except = ['api/v1/*'] in VerifyCsrfToken.php

raidenz commented May 26, 2016

maybe i can add some point Route::post('/short', 'UrlMapperController@store'); post will need csrf token, if you want to disable it on your link set protected $except = ['api/v1/*'] in VerifyCsrfToken.php

@jonwhittlestone

This comment has been minimized.

Show comment Hide comment
@jonwhittlestone

jonwhittlestone Jun 9, 2016

Thanks, this is well-written but I am unable to get a response other than 'Unauthorized'

  • I have created the column, api_token in the users table
  • Made sure I'm using the correct api guard and have the middlewareGroup api in App\Http\Kernel
  • Set up a dummy route:
Route::group(['middleware' => 'auth:api'], function () {
    Route::get('data', function () {
        return ['here is some data'];
    });
});
  • removing the middleware parameter, I can see the output ['here is some data']

I must be missing something ....

This is my request

curl -X GET -H "Content-Type: application/json;charset=UTF-8" -H "Accept: application/json, text/plain, */*" -H "Cache-Control: no-cache" -H "Postman-Token: a6d4f21c-67db-4e48-0a66-f2ff7eb461b6" "http://cc.ivxs.uk/data?api_token=f926f2aeebd8b7fe1e8723b47ab741d4"

jonwhittlestone commented Jun 9, 2016

Thanks, this is well-written but I am unable to get a response other than 'Unauthorized'

  • I have created the column, api_token in the users table
  • Made sure I'm using the correct api guard and have the middlewareGroup api in App\Http\Kernel
  • Set up a dummy route:
Route::group(['middleware' => 'auth:api'], function () {
    Route::get('data', function () {
        return ['here is some data'];
    });
});
  • removing the middleware parameter, I can see the output ['here is some data']

I must be missing something ....

This is my request

curl -X GET -H "Content-Type: application/json;charset=UTF-8" -H "Accept: application/json, text/plain, */*" -H "Cache-Control: no-cache" -H "Postman-Token: a6d4f21c-67db-4e48-0a66-f2ff7eb461b6" "http://cc.ivxs.uk/data?api_token=f926f2aeebd8b7fe1e8723b47ab741d4"

@petersonpeter

This comment has been minimized.

Show comment Hide comment
@petersonpeter

petersonpeter Jun 11, 2016

Hi @JacobBennett, where do we put this code exactly?
Auth::guard('api')->user();

Hi @JacobBennett, where do we put this code exactly?
Auth::guard('api')->user();

@Hejafe

This comment has been minimized.

Show comment Hide comment
@Hejafe

Hejafe Jun 16, 2016

How can i inform the username and password to take the token? after take token i get access to other pages?

Hejafe commented Jun 16, 2016

How can i inform the username and password to take the token? after take token i get access to other pages?

@adam-devapp

This comment has been minimized.

Show comment Hide comment
@adam-devapp

adam-devapp Jun 19, 2016

Added this as a new middleware option to support both API tokens and user logins:

public function handle($request, Closure $next)
{

    $api_token = Input::get('api_token');
    $user = User::where('api_token', $api_token)->first();

    if (!is_null($user)) {
        $this->auth->login($user);
    }

    if ((is_null($user)) || (is_null($api_token))) {
        if ($request->ajax()) {
            return response('Unauthorized.', 401);
        } else {
            return redirect()->guest('auth/login');
        }
    }

    return $next($request);
}

Added this as a new middleware option to support both API tokens and user logins:

public function handle($request, Closure $next)
{

    $api_token = Input::get('api_token');
    $user = User::where('api_token', $api_token)->first();

    if (!is_null($user)) {
        $this->auth->login($user);
    }

    if ((is_null($user)) || (is_null($api_token))) {
        if ($request->ajax()) {
            return response('Unauthorized.', 401);
        } else {
            return redirect()->guest('auth/login');
        }
    }

    return $next($request);
}
@emoran

This comment has been minimized.

Show comment Hide comment
@emoran

emoran Jul 21, 2016

Hi, how can I reset the password of an api user, in this case I would like to provide a reset password for a mobile application. Best

emoran commented Jul 21, 2016

Hi, how can I reset the password of an api user, in this case I would like to provide a reset password for a mobile application. Best

@maxCrowded

This comment has been minimized.

Show comment Hide comment
@maxCrowded

maxCrowded Aug 11, 2016

This is awesome. Thank you.
How could I use this approach if the API were remote? I need to pass the remote server the user credentials and then I would be given an access_token.

This is awesome. Thank you.
How could I use this approach if the API were remote? I need to pass the remote server the user credentials and then I would be given an access_token.

@BasConijn

This comment has been minimized.

Show comment Hide comment
@BasConijn

BasConijn Sep 4, 2016

@jonwhittlestone
Did you add the api_token to the user table?
Are you sure that the token in the request matches the token in the user table?

@jonwhittlestone
Did you add the api_token to the user table?
Are you sure that the token in the request matches the token in the user table?

@oliwin

This comment has been minimized.

Show comment Hide comment
@oliwin

oliwin Sep 4, 2016

Is it reliable default method to use token? Or better to install library? Also, how can I pass token without GET parameter? For example with HEAD field in request?

oliwin commented Sep 4, 2016

Is it reliable default method to use token? Or better to install library? Also, how can I pass token without GET parameter? For example with HEAD field in request?

@oliwin

This comment has been minimized.

Show comment Hide comment
@oliwin

oliwin Sep 7, 2016

How to register a new user using api?

oliwin commented Sep 7, 2016

How to register a new user using api?

@progmars

This comment has been minimized.

Show comment Hide comment
@progmars

progmars Sep 13, 2016

Not sure if this is any better than good old SessionId.

At least, SessionId is regenerated often (normally, on each log-in, if you have it implemented correctly), so even if someone stole it, it will be usable only for a short period of time.

Such database token, on the contrary, is not regenerated, and even if you encrypt it, it still is the same and being sent to the server and could be intercepted almost the same way as SessionID. Also, it does not offer any benefits - you still have to hit the database on each request. But in comparison, session could be implemented as a Redis cache, fast SSD storage - you name it.

If you really want to use true token mechanism, you have to implement it fully - to create a short term tickets (e.g. JWT) which contain claims about the caller identity, so you can grant access to the API service without hitting the database. All the other in-between solutions for sessionId<->token are redundant and not any better than using SessionId directly.

Not sure if this is any better than good old SessionId.

At least, SessionId is regenerated often (normally, on each log-in, if you have it implemented correctly), so even if someone stole it, it will be usable only for a short period of time.

Such database token, on the contrary, is not regenerated, and even if you encrypt it, it still is the same and being sent to the server and could be intercepted almost the same way as SessionID. Also, it does not offer any benefits - you still have to hit the database on each request. But in comparison, session could be implemented as a Redis cache, fast SSD storage - you name it.

If you really want to use true token mechanism, you have to implement it fully - to create a short term tickets (e.g. JWT) which contain claims about the caller identity, so you can grant access to the API service without hitting the database. All the other in-between solutions for sessionId<->token are redundant and not any better than using SessionId directly.

@progmars

This comment has been minimized.

Show comment Hide comment
@progmars

progmars Sep 13, 2016

Not sure if this is any better than good old SessionId.

At least, SessionId is regenerated often (normally, on each log-in, if you have it implemented correctly), so even if someone stole it, it will be usable only for a short period of time.

Such database token, on the contrary, is not regenerated, and even if you encrypt it, it still is the same and being sent to the server and could be intercepted almost the same way as SessionID. Also, it does not offer any benefits - you still have to hit the database on each request. But in comparison, session could be implemented as a Redis cache, fast SSD storage - you name it.

If you really want to use true token mechanism, you have to implement it fully - to create a short term tickets (e.g. JWT) which contain claims about the caller identity, so you can grant access to the API service without hitting the database. All the other in-between solutions for sessionId<->token are redundant and not any better than using SessionId directly.

Not sure if this is any better than good old SessionId.

At least, SessionId is regenerated often (normally, on each log-in, if you have it implemented correctly), so even if someone stole it, it will be usable only for a short period of time.

Such database token, on the contrary, is not regenerated, and even if you encrypt it, it still is the same and being sent to the server and could be intercepted almost the same way as SessionID. Also, it does not offer any benefits - you still have to hit the database on each request. But in comparison, session could be implemented as a Redis cache, fast SSD storage - you name it.

If you really want to use true token mechanism, you have to implement it fully - to create a short term tickets (e.g. JWT) which contain claims about the caller identity, so you can grant access to the API service without hitting the database. All the other in-between solutions for sessionId<->token are redundant and not any better than using SessionId directly.

@meredevelopment

This comment has been minimized.

Show comment Hide comment
@meredevelopment

meredevelopment Sep 22, 2016

Thanks for the article @JacobBennett, it filled a gap in the Laravel docs for me. I'm getting this working with 5.3, and despite the security concerns, and the fact that all new docs seem to point to Passport / OAuth solutions, I really just want this simple token auth for a simple API.

Overall this works for me, I can 'protect' some routes with an 'api_token' in a GET request. However I'm having trouble with the 'Extras' section above. 5.3 doesn't have App\Http\Middleware\Authenticate but does have App\Http\Middleware\RedirectIfAuthenticated and in there things seem to be reversed.

So Instead of this:

public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->guest()) {
            if ($request->ajax() || $request->wantsJson()) {
                return response('Unauthorized.', 401);
            }
            return redirect()->guest('login');
        }
        return $next($request);
    }

we have this:

public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            return redirect('/home');
        }

        return $next($request);
    }

and then in App\Exceptions\Handler we have:

protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }

        return redirect()->guest('login');
    }

Using the old code results in a redirect loop. Can anyone help me figure out how to get a JSON error returned when 'api_token' is incorrect, and not a redirect to 'login'? It seems that the 'expectsJson()' is always false or something.

EDIT / ANSWER
Turns out I needed to use the 'Accept' header in all requests and set it to 'application/json'.

meredevelopment commented Sep 22, 2016

Thanks for the article @JacobBennett, it filled a gap in the Laravel docs for me. I'm getting this working with 5.3, and despite the security concerns, and the fact that all new docs seem to point to Passport / OAuth solutions, I really just want this simple token auth for a simple API.

Overall this works for me, I can 'protect' some routes with an 'api_token' in a GET request. However I'm having trouble with the 'Extras' section above. 5.3 doesn't have App\Http\Middleware\Authenticate but does have App\Http\Middleware\RedirectIfAuthenticated and in there things seem to be reversed.

So Instead of this:

public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->guest()) {
            if ($request->ajax() || $request->wantsJson()) {
                return response('Unauthorized.', 401);
            }
            return redirect()->guest('login');
        }
        return $next($request);
    }

we have this:

public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::guard($guard)->check()) {
            return redirect('/home');
        }

        return $next($request);
    }

and then in App\Exceptions\Handler we have:

protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        }

        return redirect()->guest('login');
    }

Using the old code results in a redirect loop. Can anyone help me figure out how to get a JSON error returned when 'api_token' is incorrect, and not a redirect to 'login'? It seems that the 'expectsJson()' is always false or something.

EDIT / ANSWER
Turns out I needed to use the 'Accept' header in all requests and set it to 'application/json'.

@bot101

This comment has been minimized.

Show comment Hide comment
@bot101

bot101 Nov 11, 2016

Thanks for this heads up. I already did something similar and had to change from authToken to api_token on the users table and in code.

bot101 commented Nov 11, 2016

Thanks for this heads up. I already did something similar and had to change from authToken to api_token on the users table and in code.

@skygdi

This comment has been minimized.

Show comment Hide comment
@skygdi

skygdi Nov 14, 2016

@progmars I don't agree with you . because that token could be regenerate after login like session behavior. and as a database token , you could be flexible than session in expiration , remove/change it for an Interruption.
added, this token mechanism could be use at APP or the third part application through Internet which use a not standard HTTP protocol.

skygdi commented Nov 14, 2016

@progmars I don't agree with you . because that token could be regenerate after login like session behavior. and as a database token , you could be flexible than session in expiration , remove/change it for an Interruption.
added, this token mechanism could be use at APP or the third part application through Internet which use a not standard HTTP protocol.

@skygdi

This comment has been minimized.

Show comment Hide comment
@skygdi

skygdi Nov 14, 2016

@meredevelopment I think you should deal those requests from api_token with an individual middleware instead of the original one. and then echo a Json message instead of redirect.

skygdi commented Nov 14, 2016

@meredevelopment I think you should deal those requests from api_token with an individual middleware instead of the original one. and then echo a Json message instead of redirect.

@morganzysman

This comment has been minimized.

Show comment Hide comment
@morganzysman

morganzysman Dec 27, 2016

Hello, Thank you for your article, it works great with users. I want to be able to do this using an other table called clients.

Could you specify the steps needed.

Thanks in advance
Morgan,

Hello, Thank you for your article, it works great with users. I want to be able to do this using an other table called clients.

Could you specify the steps needed.

Thanks in advance
Morgan,

@andrew-za

This comment has been minimized.

Show comment Hide comment
@andrew-za

andrew-za Jan 18, 2017

Adding api_token to the header in Vueify files:
http://stackoverflow.com/a/41714231/3273588

Adding api_token to the header in Vueify files:
http://stackoverflow.com/a/41714231/3273588

@pdesign

This comment has been minimized.

Show comment Hide comment
@pdesign

pdesign Jan 24, 2017

@sojic @JacobBennett yea, i am pulling my hair off... how to have multiple access and refresh tokens for a user... also should there be device token on the same table to be able to send notifications to the related device

pdesign commented Jan 24, 2017

@sojic @JacobBennett yea, i am pulling my hair off... how to have multiple access and refresh tokens for a user... also should there be device token on the same table to be able to send notifications to the related device

@pdesign

This comment has been minimized.

Show comment Hide comment
@pdesign

pdesign Jan 24, 2017

@sojic @JacobBennett yea, i am pulling my hair off... how to have multiple access and refresh tokens for a user... also should there be device token on the same table to be able to send notifications to the related device

pdesign commented Jan 24, 2017

@sojic @JacobBennett yea, i am pulling my hair off... how to have multiple access and refresh tokens for a user... also should there be device token on the same table to be able to send notifications to the related device

@skobak

This comment has been minimized.

Show comment Hide comment
@skobak

skobak Jan 25, 2017

to @jonwhittlestone
I faced the same problem , my solution is little changes Middlewar/Authenticate.php:

use Closure;
use Illuminate\Contracts\Auth\Guard;

class Authenticate {

/**
 * The Guard implementation.
 *
 * @var Guard
 */
protected $auth;

/**
 * Create a new filter instance.
 *
 * @param  Guard  $auth
 * @return void
 */
public function __construct(Guard $auth)
{
	$this->auth = $auth;
}

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next, $guard = null)
{
	if (\Auth::guard($guard)->guest()) {
		if ($request->ajax() || $request->wantsJson()) {
			return response('Unauthorized.', 401);
		}
		return redirect()->guest('auth/login');
	}
	return $next($request);

}

skobak commented Jan 25, 2017

to @jonwhittlestone
I faced the same problem , my solution is little changes Middlewar/Authenticate.php:

use Closure;
use Illuminate\Contracts\Auth\Guard;

class Authenticate {

/**
 * The Guard implementation.
 *
 * @var Guard
 */
protected $auth;

/**
 * Create a new filter instance.
 *
 * @param  Guard  $auth
 * @return void
 */
public function __construct(Guard $auth)
{
	$this->auth = $auth;
}

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next, $guard = null)
{
	if (\Auth::guard($guard)->guest()) {
		if ($request->ajax() || $request->wantsJson()) {
			return response('Unauthorized.', 401);
		}
		return redirect()->guest('auth/login');
	}
	return $next($request);

}

@skobak

This comment has been minimized.

Show comment Hide comment
@skobak

skobak Jan 25, 2017

to avoid Unauthorized response, just change Middleware/Authenticate.php
public function handle($request, Closure $next, $guard = null)
{
if (\Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('auth/login');
}
return $next($request);
}

skobak commented Jan 25, 2017

to avoid Unauthorized response, just change Middleware/Authenticate.php
public function handle($request, Closure $next, $guard = null)
{
if (\Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('auth/login');
}
return $next($request);
}

@cedch

This comment has been minimized.

Show comment Hide comment
@cedch

cedch Jan 29, 2017

Very useful.
But is there any news that would allow the encryption of the token ?

cedch commented Jan 29, 2017

Very useful.
But is there any news that would allow the encryption of the token ?

@samjadps

This comment has been minimized.

Show comment Hide comment
@samjadps

samjadps Mar 8, 2017

Thank you sir the concept is clear

samjadps commented Mar 8, 2017

Thank you sir the concept is clear

@mohammadhosseinime

This comment has been minimized.

Show comment Hide comment
@mohammadhosseinime

mohammadhosseinime Apr 17, 2017

laravel 5.4 remove authenticate.php :|

laravel 5.4 remove authenticate.php :|

@MevlutOzdemir

This comment has been minimized.

Show comment Hide comment
@MevlutOzdemir

MevlutOzdemir May 5, 2017

Thanks!!!!

Thanks!!!!

@rabb-bit

This comment has been minimized.

Show comment Hide comment
@rabb-bit

rabb-bit May 13, 2017

thank you so much! simple and well explained!

thank you so much! simple and well explained!

@technoknol

This comment has been minimized.

Show comment Hide comment
@technoknol

technoknol Jun 16, 2017

everyone explains about passing api_token but how to Login via API and get api_token, no one has written about this.

everyone explains about passing api_token but how to Login via API and get api_token, no one has written about this.

@sanchitmahajan

This comment has been minimized.

Show comment Hide comment
@sanchitmahajan

sanchitmahajan Sep 19, 2017

I am using Passport..My User table contains only id, email and password fields.... When user logged in, the user gets api_token.. When user post some data such as title and description to server. in this what the steps i have to follow, how i can use api_token.. what will be database table structure... I have other table contains title and description fields.. pls help..thank you.

I am using Passport..My User table contains only id, email and password fields.... When user logged in, the user gets api_token.. When user post some data such as title and description to server. in this what the steps i have to follow, how i can use api_token.. what will be database table structure... I have other table contains title and description fields.. pls help..thank you.

@azazqadir

This comment has been minimized.

Show comment Hide comment
@azazqadir

azazqadir Oct 9, 2017

Why not use Laravel Passport for authenticating your Laravel Rest API. https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/

Why not use Laravel Passport for authenticating your Laravel Rest API. https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/

@azazqadir

This comment has been minimized.

Show comment Hide comment
@azazqadir

azazqadir Oct 9, 2017

Why not use Laravel Passport for authenticating your Laravel Rest API. https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/

Why not use Laravel Passport for authenticating your Laravel Rest API. https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/

@azazqadir

This comment has been minimized.

Show comment Hide comment
@azazqadir

azazqadir Oct 26, 2017

Since this REST API is based on older version of Laravel, I think there is a need to mention how devs can create a REST API on latest version of Laravel, which is currently 5.5 and use a new package for Authentication. We now have Laravel Passport that can be used for easy and quick Laravel REST API authentication (https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/ ). This package provides a full OAuth2 server implementation.

Since this REST API is based on older version of Laravel, I think there is a need to mention how devs can create a REST API on latest version of Laravel, which is currently 5.5 and use a new package for Authentication. We now have Laravel Passport that can be used for easy and quick Laravel REST API authentication (https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/ ). This package provides a full OAuth2 server implementation.

@azazqadir

This comment has been minimized.

Show comment Hide comment
@azazqadir

azazqadir Oct 26, 2017

Since this REST API is based on older version of Laravel, I think there is a need to mention how devs can create a REST API on latest version of Laravel, which is currently 5.5 and use a new package for Authentication. We now have Laravel Passport that can be used for easy and quick Laravel REST API authentication (https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/ ). This package provides a full OAuth2 server implementation.

Since this REST API is based on older version of Laravel, I think there is a need to mention how devs can create a REST API on latest version of Laravel, which is currently 5.5 and use a new package for Authentication. We now have Laravel Passport that can be used for easy and quick Laravel REST API authentication (https://www.cloudways.com/blog/rest-api-laravel-passport-authentication/ ). This package provides a full OAuth2 server implementation.

@Milanchhatralia

This comment has been minimized.

Show comment Hide comment
@Milanchhatralia

Milanchhatralia Dec 21, 2017

How can we authorize user using Auth::guard('api')->user(); ...
I mean how to check email and password??
Or if it is Using token than how to compare the tokens??

How can we authorize user using Auth::guard('api')->user(); ...
I mean how to check email and password??
Or if it is Using token than how to compare the tokens??

@hosembafer

This comment has been minimized.

Show comment Hide comment
@hosembafer

hosembafer Jan 9, 2018

How can I pass email and password of user and get api_token?

How can I pass email and password of user and get api_token?

@hosembafer

This comment has been minimized.

Show comment Hide comment
@hosembafer

hosembafer Jan 9, 2018

How I can pass email and password of user to get api_token?
Do I need write my own route for that?

How I can pass email and password of user to get api_token?
Do I need write my own route for that?

@azazqadir

This comment has been minimized.

Show comment Hide comment
@azazqadir

azazqadir Mar 31, 2018

It is better to use Laravel passport for this. Laravel passport automatically generates api token in Laravel. You just have to install and configure it with your application.

It is better to use Laravel passport for this. Laravel passport automatically generates api token in Laravel. You just have to install and configure it with your application.

@azazqadir

This comment has been minimized.

Show comment Hide comment
@azazqadir

azazqadir Mar 31, 2018

It is better to use Laravel passport for this. Laravel passport automatically generates api token in Laravel. You just have to install and configure it with your application.

It is better to use Laravel passport for this. Laravel passport automatically generates api token in Laravel. You just have to install and configure it with your application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment