-
-
Save adamgoose/258622db0b60d5dbc63c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# SPA API Authentication | |
> Just a note, I did NOT test these code snippets at all. If you have issues, let me know. | |
## Some Considerations | |
1. jQuery's AJAX calls do not maintain a session, thus typical Laravel Authentication will not work for your API. | |
2. Every time your API, you will need to tell it who you are! | |
3. In my opinion, your API should ONLY serve up content that belongs to the authenticated user. See #7 in the approach workflow below. | |
## Approach Workflow | |
This is the workflow in which the authentication process will work. | |
1. The client "loggs in" by sending a username/email and password to an API Auth route on your Laravel app. | |
2. Your server attempts to authenticate the said user by using `Auth::once($credentials)`. | |
> The `Auth::once()` method does not attempt to create a session for the user, but authenticates them for the rest of the request. | |
3. Your server returns an encrypted version of the User's ID by using `Crypt::encode(Auth::user()->id)`. | |
> Laravel does secure encryption using the application secret. | |
4. Your JavaScript will then store that string in your `chrome.storage.sync`. | |
5. On all future requests, your JavaScript will append that "token" to the request, either via a header or a GET variable (I recommend header; it's harder to inspect). | |
6. All protected API routes will use a "before filter" that will check for the presence of said header, decrypt it, and log the user in for the remainder of the request with `Auth::onceById($decryptedId)`. | |
7. The remainder of your API will behave as if the user is logged in. Thus, methods like `/api/v1/quotes` will return `Auth::user()->quotes`, as opposed to `Quote::all()`. | |
## Here we go! | |
### Authentication Route | |
Create a route inside your API that will accept the user's credentials and return to them the token. | |
Route::group(['prefix' => 'api/v1'], function() | |
{ | |
Route::post('login', 'AuthController@postLogin'); | |
}); | |
Create an `AuthController` with a `postLogin()` method. | |
<?php | |
class AuthController extends Controller { | |
public function postLogin() | |
{ | |
$attempt = Auth::once(Input::only('email', 'password')); | |
// Input::only('email', 'password') will return an array | |
// similar to this one: | |
// [ | |
// 'email' => Input::get('email'), | |
// 'password' => Input::get('password'), | |
// ] | |
if($attempt == true) { | |
$token = Crypt::encrypt(Auth::user()->id); | |
return Response::json([ | |
'status' => 'success', | |
'token' => $token, | |
], 200); | |
// 200 = status code for "OK" | |
} else | |
{ | |
return Response::json([ | |
'status' => 'failure', | |
], 401); | |
// 401 = status code for "Unauthorized" - must be | |
// handled with the `error` callback on your AJAX | |
// request, not the `success` callback. | |
} | |
} | |
} | |
### Store the Token | |
In your JavaScript, retreive the token from the JSON data, and store it in local storage. | |
var email = '[email protected]', | |
password = 'myPassword'; | |
$.ajax({ | |
method: 'POST', | |
url: 'http://yourdomain.com/api/v1/login', | |
data: { | |
email: email, | |
password: password | |
}, | |
success: function(data) | |
{ | |
chrome.storage.sync.set('auth-token', data['token']); | |
// alert the user that they're successfully logged in. | |
}, | |
error: function(jqXHR, textStatus, errorThrown) | |
{ | |
var data = jqXHR.responseJSON; | |
// alert the user that they're not logged in. | |
}, | |
dataType: 'json' | |
}); | |
### Intercept all AJAX calls by setting the jQuery AJAX Defaults | |
We'll simply add the authentication header to all future jQuery AJAX requests so that you won't need to do it every time. | |
// Only applies to AJAX requests that expect a | |
// JSON data type, such as $.getJSON() | |
$.ajaxPrefilter('json', function(options) | |
{ | |
var token = chrome.storage.sync.get('auth-token'); | |
// I'd do some console.logs to determine what | |
// this will return if no auth-token is stored | |
// before you use this `if` expression | |
if(token != null) | |
{ | |
// Set the X-Auth Header | |
options.headers = { | |
'X-Auth': token | |
}; | |
} | |
}); | |
### Create a Filter! | |
This filter will be applied to all of your protected API routes. | |
Route::filter('api-auth', function($route, $request, $response) | |
{ | |
$token = $request->headers->get('x-auth'); | |
$userId = Crypt::decrypt($token); | |
Auth::onceUsingId($userId); | |
// You could also do some checking here to | |
// see if the authentication failed or not. | |
// e.x... | |
// if(!Auth::check()) | |
// return Response::json([ | |
// 'status' => 'unauthorized', | |
// ], 401); | |
}); | |
### Apply the Filter! | |
Here's an updated look at your `routes.php` file, now that we've applied the filter. | |
Route::group(['prefix' => 'api/v1'], function() | |
{ | |
Route::get('login', 'AuthController@getLogin'); | |
Route::group(['before' => 'api-auth'], function() | |
{ | |
// Protected routes go here. | |
// All routes accessed through here will | |
// be authenticated as the user. | |
}); | |
}); | |
## Server Considerations | |
Your server, and your local development environment, will NOT allow you to make AJAX requests to it, since it is a Cross-Origin Resource Sharing (CORS). In other words, it's a cross-domain request, where the client is `extension://your-extension-id` or something like that, and the host is `http://yourdomain.com`. This can be fixed by editing your nginx configuration. | |
Edit `/etc/nginx/sites-enabled/{your-site}`, and add the following lines somewhere inside the `server` declaration: | |
location ^~ /api/ { | |
add_header 'Access-Control-Allow-Origin' '*'; | |
add_header 'Access-Control-Allow-Credentials' 'true'; | |
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; | |
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,X-Auth'; | |
try_files $uri $uri/ /index.php?$query_string; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment