how to convert my laravel project to an api? - php

I have a laravel project, which is about Students and their courses.
This project has CRUD functions, where the user(mainly the school's admin) can create users and assign their courses. I have successfully created this project.
But now how do I make this an API?. I need to share the students courses to another website, therefore I need to create an API right?
I have never created an API in laravel, when I checked the documentation of Laravel, it has this topic called API authentication using passport, is this talking about requiring users to login first before accessing the API
?.
Would be very helpful if someone can explain a little bit about creating an API and where to start from.

You are trying to give another website access to view the students courses. Is the data publicly available ? If yes, you don't need Authentication. But that means literally everybody with the API URL can access the data. Using Authentication on your API is entirely up to you.
Now, to create an API, it's very easy with Laravel. There are tons of free resources online like this one: https://tutorials.kode-blog.com/laravel-5-rest-api. Just google the topic for more.
Basically, the API works exactly like what you did in terms of controller and routing. In laravel 5.4, there is a separate route file for api located in routes/api.php. That's where you define your routes.
Route::get('courses', 'Api\CourseController#index');
This route will respond to https://www.website.com/api/courses and will link to the function index() located in app/Http/Controllers/Api/CourseController.php. You will have to create that controller yourself.
Looking at that route file, you can see something out of the box
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
It means the route /api/user will require authentication because of the auth:api middleware.
In your controllers so far, you return views. In api controller you will return response
public function index()
{
$courses = Course::all();
return response()->json($courses);
}
The application calling the website will receive a JSON object containing an array of courses. More about responses here: https://laravel.com/docs/5.4/responses
About api_token
The default API authentication offered by laravel works by checking api_token attribute from the users table. That api_token has to been present in the headers of the request sent to the api. Laravel will then use that api_token to identify the user making the request. In default application, to get a user, you do
$user = Auth::user();
Within your API controllers, you do
$user = Auth::guard('api')->user();
//Auth will look for api_token to get the user making the request

Related

Laravel Authentication API Sanctum – With Custom Database

First important information: I’m new to Laravel, so your patience is appreciated.
I’m currently migrating a framework of mine to Laravel and still in the early stages. I know that Laravel has it’s own database construction mechanism that is recommended to use the migrations and the Models, however, for my purpose, I’d like to use my own database that I use in other systems that I’ve built in the past. The idea for this system is to have a shared database, but operable through different tech stacks.
This is my current scenario:
Laravel 8
Sanctum 2.14
Frontend (Laravel):
I’ve built a very simple login page that has a controller and sends data (user and password) to my backend (Laravel). In the backend (different server), I grab the data and check if the data is correct. Being correct, I send a json response with some data, like:
returnStatus = true
loginVerification = true
IDCrypt = asdfasd4fa654sd54a (encrypted ID to grab in the frontend again)
Up till here, it’s working fine, as I wanted and very similar to my legacy systems.
My idea would be to get this response in the frontend, via auth token managed by Sanctum and use a middleware to check the token in order to let the user access some web routes.
I’ve watched some videos, but I’m only finding videos that use all the database mechanism that Laravel provides.
However, my intention would be to generate the token with data from my own table and data objects I created (without any existing Laravel´s models).
Is there a way for me to do this?
How would I set the token in the backend and include in my response?
How would I grab the token in the frontend in a secure way?
Lets say you have a model LegacyUser and this is your existing authenticable entity.
In this model simply override methods defined in the Laravel\Sanctum\HasApiTokens trait. Specifically createToken and the tokens relation for your use case by the sounds.
Then you can create tokens anywhere like usual with
$user = LegacyUser::find( $id );
$token = $user->createToken('token-name');
Then us the token as usual.
NOTE: if you're also changing how the tokens are stored/retrieved you'll need to set the token model, docs cover that here: https://laravel.com/docs/8.x/sanctum#overriding-default-models
If you want to avoid using authenticable entites (ie, no laravel models) entirely that's going to be more complicated and Passport might be a better shout, as client_credentials dont need to be associated to a user entity.
Alternatively: Write your own middleware that is compatbile with your existing auth process.

Using Laravel Passport to only access own account on a few Routes

My whole application has a lot of api routes that serve my frontend with ajax responses. I use Laravel Passport xsrf token protection to protect my routes and manage authentication. However, we are planning to serve an api that a specific type of user can consume. To go in more detail, here is a (fictional) example of what we want to achieve:
A company can have a lot vacancies.
My api routes are a lot, now I want to give the company owner the ability to get all his vacancies over our sdk and place them on his website. The Problem I am facing: As far as I understand passport correctly, personal access tokens are the thing I need. I don’t want a „log in with my application“ functionality (jet), how do I disable this? I want only the user to access his own informations, not from other users. And I probably want in the future to let the user register for different apis. So that he has multiple access tokens for different routes, but the routes are all together in one api.php routes file. I think I would need to use scopes for this, but how do I safely assign the scopes to the tokens, since this only does a single route?
Can someone help me to understand the concept of passport correctly?
I do not use passport, however it seems this issue could be solved with scopes (not needing to be tied to an access token). If you are struggling with getting the authenticated user, you could follow this post:
Get authenticated user with Laravel Passport and grant password
If you are struggling with how to write your scope, here is what I am thinking
You have a table containing companies called companies
You have a table containing employees called employees
You have a table containing vacancies called vacancies
You have a join table called company_employee
Now you can create a scope on your Vacancy model
class User {
public function companies()
{
return $this->hasMany(App\CompanyEmployee::class)
}
}
class Employee {
}
class Company {
}
class CompanyEmployee {
}
class Vacancy {
public function scopeMine($query)
{
$my_companies = auth()
->user()
->companies()
->select('company_id')
->pluck('company_id')
->get()
->toArray();
return $query->whereIn('company_id', $my_companies);
}
}
Then no matter how you are querying the vacancies (through an API or front end GUI), you can simply add the scope to your query presumably in your controller.
class VacancyController {
public function index()
{
return App\Vacancy::mine()->get();
}
}

Laravel Broadcast authentication

How do I authenticate an user to a channel with a custom auth method?
For example, in my app I use a token auth (stored in the db for each user) for my API, passed via header and read by a custom middleware.
How do I control the access with these custom auth methods for channels? How would be the client and server side? I am using socket.io and the Laravel docs are quite unclear of how auth methods works for sockets.
USE CASE:
I have users stored in DB with default Laravel migration. However, how my app is just the API, it is stateless, then, rather than using remember_token to store session tokens, I use it to store an unique authentication token, generated in the login endpoint.
Every endpoint of the API, the request pass through a middleware, where the user is retrieved by the token present in the headers
$token = Request::header('X-token');
$request->user = User::findByToken($token);
return $next($request);
Now the question comes in. Laravel docs isn't much clear on how private channels works.
How would a broadcast client pass the mentioned token and get authorized to join a given channel?
The Laravel documentation on broadcasting is pretty comprehensive about this, although it doesn't go into minute detail on client libraries that it doesn't support out of the box.
In this case, you're wanting the section on authorizing channels. From the docs:
In the BroadcastServiceProvider included with your Laravel application, you will see a call to the Broadcast::routes method. This method will register the /broadcasting/auth route to handle authorization requests
When defining broadcast routes, the currently authenticated user is passed automatically as a parameter. Since you're wanting to change the authentication mechanism ahead of the broadcast route definition so that the correct user is passed along, your broadcast client would pass along the token (however you like - HTTP header, URL parameter, POST data, etc.) and then you would customise the /broadcasting/auth route to parse that data and use it to authenticate the user. From there you would use the standard Broadcast::channel() definitions to ensure that the authenticated user is also an authorized user on that channel.
As per the documentation, Broadcast::routes() takes an optional array of $attributes for it to use the web middleware. From the source:
$attributes = $attributes ?: ['middleware' => ['web']];
So if you wish, you may change the middleware authentication to 'api' by passing it to the Broadcast::routes() call (or any other standard route parameters).
Since the route is already created for you, if you want to customise it at a lower level that route functions/middleware then you would need to change the auth() method on whichever BroadcastProvider you're using. More specifically, when the route is set up, it calls the authenticate() method on the BroadcastController, which in turn simply calls the auth() method on the Broadcast facade. The facade would then call whichever provider you're using - there are a few providers defined out of the box which should give you a good starting point if you want to write your own. That being said, as long as you don't have a problem with the provider itself, it's probably easier to just write your own middleware and pass that to Broadcast::routes() as an attribute if you need something particular with your authentication (if it differs from the api authentication middleware).
As an extra, since you've tagged this with socket.io, you may also want to read up on Laravel Echo. There's also a section on talking to socket.io specifically in the broadcasting introduction.
On top of Leith's answer, those scratching head and wondering why creating a custom middleware and using it in BroadcastServiceProvider.php would throw back an error.
public function boot()
{
Broadcast::routes(['middleware' => ['custom.middleware']]);
require base_path('routes/channels.php');
}
Inside Broadcaster.php there is a method called retrieveUser() which should return $request->user() as a result of successful authentication from within your custom middleware.
In my case, I was trying to pass access token to API's custom middleware; and once authenticating the user, I simply passed on my original request return $next($request)
For the above code to work, I had to fetch the user model by access token and then merge it to the original request like so
$request->merge(['user' => $user ]);
$request->setUserResolver(function () use ($user) {
return $user;
});
return $next($request);
So now retrieveUser() method from Broadcaster.php is able to retrieve the authenticated user with return $request->user() command, and it gets passed to Broadcast::channel method as a first parameter
Broadcast::channel('private-channel-name', function ($user) {
// if you get here, you've been authenticated (within a custom middleware)
return true;
});

Laravel OAuth token for one user only

I am creating a Web App where I need to access an API from Mobile Apps. I have implemented some Public API methods, which are fine and work great.
Now, I am working on the restricted API methods i.e. updating User info etc.
I have started implementing the OAuth server package lucadegasperi/oauth2-server-laravel
It is all setup and dishing out tokens no problem, using the documentation from the GitHub repo and this tutorial.
I can get an OAuth token using a POST call to my public API method oauth/access_token, this returns an Access Token no problem.
Now, my issue is:
Once I have the Access Token, how do I know who it was issued to and what they have access to? There is no user_id associated with this token.
Can I simply create a column in the table and use Eloquent models to find out and how would I associate this token with a user?
Ok, so after scouring the Source Code and documentation many times I gave up and posted a question here. I then thought "one more time". I found this gem.
And right down the bottom (not in TOC or anything) is:
Finding access token owner
use LucaDegasperi\OAuth2Server\Facades\Authorizer;
Authorizer::getResourceOwnerId();
Pair this with:
$user = User::find($user_id);
Like so:
Route::get('protected', ['middleware' => 'oauth', function() {
$user_id = Authorizer::getResourceOwnerId();
$user = \App\User::find($user_id);
return $user;
}]);
And Bob's your uncle. We have who made the request.

Laravel Dual Auth Mechanism: Database and REST API

I have to set up an authentication system on Laravel 5.1.19 that will handle two types of users: CMS admin users and regular end users:
The CMS admin credentials will be mapped in Laravel's database so i
can use the default Laravel auth mechanism (Eloquent model & all)
The regular users will be stored on a different db that won't be
directly accessible. The credential checking for regulars is
implemented via a REST call to a dedicated server that can query that
db.
Admins and regulars will have different login pages. My questions are:
How can I have two auth mechanisms mapped to two separate routes?
Say /login/admin is the login URL for CMS admins. This request should be handled by the default auth.
Say /login/users is the login URL for regulars. This should be handled by a custom auth that will issue REST requests to a different server for credential checking and retrieval of other necessary user info.
How can I implement a custom auth mechanism that doesn't store any info in Laravel's db?
Making REST calls should be easy. My problem is that all auth tutorials assume that the user will be mapped in a db table and my requirements make it impossible to do so. I read a post on laravel.io where somebody said that he tried to save the User object in Session but he didn't get into much detail.
Update: A Preliminary Solution
Following this laravel.io post I managed to create a custom UserProvider class that extends Illuminate\Auth\EloquentUserProvider that is overriding the retrieveById($identifier) method such that it first tries to retrieve the user object from the Session. If nothing's found then it delegates the operation to its parent, EloquentUserProvider:
class CustomUserProvider extends EloquentUserProvider {
public function __construct(HasherContract $hasher, $model)
{
parent::__construct($hasher, $model);
}
public function retrieveById($identifier)
{
$user = getSessionUser($identifier);
if ($user) {
return $user;
} else {
return parent::retrieveById($identifier);
}
}
...
Other than this, I just stored the current user in Session in my LoginController.
The setup of a custom UserProvider is documented in the above-mentioned post.
Hope this helps someone and if you guys have something to add, please do.
Thanks!

Categories