For my rest api I use yii\rest\Controller
class TweetController extends Controller
{
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBasicAuth::className(),
'auth' => [$this, 'auth']
];
$behaviors['contentNegotiator'] = [
'class' => ContentNegotiator::className(),
'formats' => [
'application\json' => Response::FORMAT_JSON,
]
];
return $behaviors;
}
// not solved yet
public function auth($pass)
{}
/**
* #param int $count
*
* #return array
* #throws \yii\base\InvalidConfigException
*/
public function actionLastTweets($count = 10)
{
/**
* #var TweetLastfinder $tweetLastFinder
*/
$tweetLastFinder = Yii::$app->get('tweetlastfinder');
return $tweetLastFinder->findLastTweets($count);
}
also i use prettyUrls 'GET tweet/last-tweets/<count>' => 'tweet/last-tweets'
actionLastTweets return array which one convert into json.
Idea is made simply authentication. In docs is example how to implements IdentityInterface in model. But i don`t work with AR directly. As i understand. I need to properly write auth() method.
I dont get it, whith value should be returned from auth() when authentification is passed? And how it will be send by request? (i mean http://localhost/index.php/tweet/last-tweets/50 without authentication, how it change?)
For simplicity there is a string value $password = 'qwerty' and i want to check if parameter $pass equal $password - authentication passed
some kinda:
public function auth($pass)
{
$password = 'qwerty';
if ($pass == $password) {
authentication passed
} else {
authentication failed
}
}
The subject of the authentication is your Identity (aka user) that has to implements the funtion findIdentityByAccessToken
see
\yii\web\IdentityInterface
to try your API on command line you can use cUrl as follows ( the token: XXXXXXXX_accessTokenString_XXXXXX )
note, the ":" divides user from password in the basic autentication, the token is used as user
POST a JSON to MessageController (the pluralization is not an error)
curl --user "XXXXXXXX_accessTokenString_XXXXXX:" -H "Accept:application/json" -H "Content-Type:application/json" -XPOST "http://rest.my-domain.com/messages" -d '{"email": "me#example.com"}'
POST a JSON to UserController enabling xdebug
curl --user "XXXXXXXX_accessTokenString_XXXXXX:" --cookie 'XDEBUG_SESSION=1221221' -H "Accept:application/json" -H "Content-Type:application/json" -XPOST "http://rest.my-domain.com/messages" -d '{"email": "me#example.com"}'
GET a JSON to UserController enabling xdebug
curl --user "XXXXXXXX_accessTokenString_XXXXXX:" -H "Accept:application/json" -H "Content-Type:application/json" -XGET "http://rest.my-domain.com/messages/123"
see also the Yii2 guide to REST services
http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html
Related
I'm using external identity provider to authenticate users, created a SPA client (got client_id & client_secret), configured API with audience & scope, so once users authenticated they will get access_token (will be authorized) to access multiple custom micro-services (APIs).
When my custom API receives a request with a bearer Access Token (JWT) the first thing to do is to validate the token. In order to validate JWT I need to follow these steps:
Check that the JWT is well formed (Parse the JWT)
Check the signature. My external identity provider only supports RS256 via the JWKS (JSON Web Key Set) URL (https://{domain}/.well-known/jwks.json), so I can get my public key following this URL.
Validate the standard claims
Check the Application permissions (scopes)
There are a lot of packages/libraries (i.e. https://github.com/tymondesigns/jwt-auth) to create JWT tokens but I can't find any to validate it using those steps above. Could anyone please help to find suitable Laravel/PHP package/library or move me to the right direction in order to achieve my goals (especially point #2).
I did something similar in the past, I don't know if this may help but I'll give it a try. To use a public key, you should download it, put it somewhere on the disk (storage/jwt/public.pem for example) and then link it in the jwt config config/jwt.php with the ALGO (you can see supported algorithms here
'keys' => [
// ...
'public' => 'file://'.storage_path('jwt/public.pem'),
// ...
],
'algo' => 'RS256',
Then, you should have a custom Guard, let's call it JWTGuard:
<?php
namespace App\Guard;use App\Models\User;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Tymon\JWTAuth\JWT;class JWTGuard implements Guard
{
use GuardHelpers;
/**
* #var JWT $jwt
*/
protected JWT $jwt;
/**
* #var Request $request
*/
protected Request $request;
/**
* JWTGuard constructor.
* #param JWT $jwt
* #param Request $request
*/
public function __construct(JWT $jwt, Request $request) {
$this->jwt = $jwt;
$this->request = $request;
}
public function user() {
if (! is_null($this->user)) {
return $this->user;
}
if ($this->jwt->setRequest($this->request)->getToken() && $this->jwt->check()) {
$id = $this->jwt->payload()->get('sub');
$this->user = new User();
$this->user->id = $id;
// Set data from custom claims
return $this->user;
}
return null;
}
public function validate(array $credentials = []) { }
}
This should do all your logic of validation, I used a custom user implementation, the class signature was like:
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements AuthenticatableContract {
// custom implementation
}
Finally, you should register the guard in the AuthServiceProvider and in the auth config
public function boot()
{
$this->registerPolicies();
$this->app['auth']->extend(
'jwt-auth',
function ($app, $name, array $config) {
$guard = new JWTGuard(
$app['tymon.jwt'],
$app['request']
);
$app->refresh('request', $guard, 'setRequest');
return $guard;
}
);
}
then allow it in the config
<?php
return [
'defaults' => [
'guard' => 'jwt',
'passwords' => 'users',
],
'guards' => [
// ...
'jwt' => [
'driver' => 'jwt-auth',
'provider' => 'users'
],
],
// ...
];
You can then use it as a middleware like this:
Route::middleware('auth:jwt')->get('/user', function() {
return Auth::user();
}
Does this sound good to you?
In the end I've used the Auth0 SDK for Laravel - https://auth0.com/docs/quickstart/backend/laravel/01-authorization. Nice and clean solution.
I'm use L5-Swagger 5.7.* package (wrapper of Swagger-php) and tried describe Laravel REST API. So, my code like this:
/**
* #OA\Post(path="/subscribers",
* #OA\RequestBody(
* #OA\MediaType(
* mediaType="application/json",
* #OA\Schema(
* type="object",
* #OA\Property(property="email", type="string")
* )
* )
* ),
* #OA\Response(response=201,description="Successful created"),
* #OA\Response(response=422, description="Error: Unprocessable Entity")
* )
*/
public function publicStore(SaveSubscriber $request)
{
$subscriber = Subscriber::create($request->all());
return new SubscriberResource($subscriber);
}
But when I try send request via swagger panel I get code:
curl -X POST "https://examile.com/api/subscribers" -H "accept: */*" -H "Content-Type: application/json" -H "X-CSRF-TOKEN: " -d "{\"email\":\"bademail\"}"
As you can see, accept is not application/json and Laravel doesn't identify this as an AJAX request. So, when I send wrong data and expect to get 422 with errors in real I get 200 code with errors in "session". Request (XHR) through the swagger panel is also processed incorrectly, CURL code just for clarity.
Also, I found that in the previous version was used something like:
* #SWG\Post(
* ...
* consumes={"multipart/form-data"},
* produces={"text/plain, application/json"},
* ...)
But now it's already out of date.
So, how get 422 code without redirect if validation fails? Or maybe add 'XMLHttpRequest' header? What is the best thing to do here?
The response(s) didn't specify a mimetype.
#OA\Response(response=201, description="Successful created"),
If you specify a json response, swagger-ui will send an Accept: application/json header.
PS. Because json is so common swagger-php has a #OA\JsonContent shorthand, this works for the response:
#OA\Response(response=201, description="Successful created", #OA\JsonContent()),
and the requestbody:
#OA\RequestBody(
#OA\JsonContent(
type="object",
#OA\Property(property="email", type="string")
)
),
you can use this, i use Request class,
on the file Request
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
public function rules()
{
return [
'currentPassword' => 'required',
'newPassword' => 'required|same:confirmPassword',
'confirmPassword' => 'required'
];
}
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'errors' => $validator->errors(),
'status' => true
], 422));
}
Good morning.
I'm currently trying to access the POST data from a curl request in a Symfony 4 Controller.
My controller looks like the following:
class OrderController extends AbstractController
{
/**
* #Route("/order", name="order")
*/
public function index(Request $request)
{
var_dump($request->request->all());die;
return $this->json([
'message' => 'Welcome to your new controller!',
'path' => 'src/Controller/OrderController.php',
]);
}
}
When I run php bin\console server:run and access the localhost/order in the browser I get the page it is supposed to get. If I curl the url with
curl http://127.0.0.1:8000/order
I also get the right results. But when I try to send a json body on the curl request, on the above controller I just get an empty array.
My request looks like the following:
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d '
{'json':{
"id": "1",
"customer-id": "1",
"items": [
{
"product-id": "B102",
"quantity": "10",
"unit-price": "4.99",
"total": "49.90"
}
],
"total": "49.90"
}}' http://127.0.0.1:8000/order
Any idea on what am I doing wrong?
The Request object will only return form parameters via $request->request methods. if you want to access the JSON body, you need to use $request->getContent(). If that content is json, your code will look something like this:
$json = json_decode($request->getContent(), true);
var_dump($json['json']);
Trying to get the header authorization key in controller for making an API. Request is making from fiddler.
$headers = apache_request_headers();
And the $header contains an array.
Array
(
[User-Agent] => Fiddler
[Host] => localhost:8000
[Content-Length] => 102
[Authorization] => TestKey
)
If am trying like this to fetch the Authorization , its throwing error.
$header['Authorization]
Error :
Undefined index: Authorization
Tried many ways to get the authorization, but not working anything. Is there any way to fetch this?
To get headers from the request you should use the Request class
public function yourControllerFunction(\Illuminate\Http\Request $request)
{
$header = $request->header('Authorization');
// do some stuff
}
See https://laravel.com/api/5.5/Illuminate/Http/Request.html#method_header
Though it's an old topic, it might be useful for somebody...
In new Laravel versions, it's possible to get bearer Authorization token directly by calling Illuminate\Http\Request's bearerToken() method:
Auth::viaRequest('costom-token', function (Request $request) {
$token = $request->bearerToken();
// ...
});
Or directly from a controller:
public function index(Request $request) {
Log::info($request->bearerToken());
// ...
}
If you use a specific package like "JWT" or "sanctum" you can use their own middleware to retrieve user information.
Also, Laravel provides many ways to get the authorization key like :
$request->bearerToken(); to get only token without 'Bearer' word the result will be like 44|9rJp2TWvTTpWy535S1Rq2DF0AEmYbEotwydkYCZ3.
$request->header('Authorization'); to get the full key like Bearer 44|9rJp2TWvTTpWy535S1Rq2DF0AEmYbEotwydkYCZ3
P.S. you can use request() shortcut instead of using $request variable
You can try install the jwt(JSON Web Token Authentication for Laravel & Lumen) http://jwt-auth.com via composer.
And create a middleware that verify if exists yours token key in the header request.
After to install jwt, Your middleware it could be as follows
<?php
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
class VerifyJWTToken
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
$user = JWTAuth::toUser($request->header('token'));
} catch (JWTException $e) {
if ($e instanceof TokenExpiredException) {
return response()->json([
'error' => 'token_expired',
'code' => $e->getStatusCode()
], $e->getStatusCode());
}
else if($e instanceof TokenInvalidException){
return response()->json([
'error' => "token_invalid",
'code' => $e->getStatusCode()
], $e->getStatusCode());
}
else {
return response()->json([
'error' => 'Token is required',
'code' => $e->getStatusCode(),
], $e->getStatusCode());
}
}
return $next($request);
}
}
I used token for example for a header key, but you can name it, as you like.
Then you could use this on any controller
There is a simple way to get headers in any file or controller with $_SERVER.
print_r($_SERVER); // check your header here
then, you can simply get your header:
$AuthToken = $_SERVER['HTTP_AUTHORIZATION'];
Okay so I have a following situation:
The system I am building is retrieving data from a REST api and saving that data into a database. What I am wondering is how could this be implemented and where would behaviour like this go in sense of Laravels structure (controller, model etc.)? Does Laravel have a built in mechanism to retrieve data from external sources?
Edit:
Buzz hasn't been updated for over a year, it's recomended to now use Guzzle, see Mohammed Safeer's answer.
I have used Buzz package in order to make API requests.
You can add this package by adding it to the require section in your composer.json file.
{
require: {
"kriswallsmith/buzz": "dev-master"
}
}
Then run composer update to get it installed.
Then in Laravel you can wrap it in a class (perhaps a repository-like class) that handles making API request and returning data for your app to use.
<?php namespace My\App\Service;
class SomeApi {
public function __construct($buzz)
{
$this->client = $buzz;
}
public function getAllWidgets()
{
$data = $this->client->get('http://api.example.com/all.json');
// Do things with data, etc etc
}
}
Note: This is pseudocode. You'll need to create a class that works for your needs, and do any fancy dependency injection or code architecture that you want/need.
As #Netbulae pointed out, a Repository might help you. The article he linked is a great place to start. The only difference between the article and what your code will do is that instead of using an Eloquent model to get your data from your database, you're making an API request and transforming the result into a set of arrays/objects that your application can use (Essentially, just the data storage is different, which is one of the benefits of bothering with a repository class in the first place).
We can use package Guzzle in Laravel, it is a PHP HTTP client to send HTTP requests.
You can install Guzzle through composer
composer require guzzlehttp/guzzle:~6.0
Or you can specify Guzzle as a dependency in your project's existing composer.json
{
"require": {
"guzzlehttp/guzzle": "~6.0"
}
}
Example code in laravel 5 using Guzzle as shown below,
use GuzzleHttp\Client;
class yourController extends Controller {
public function saveApiData()
{
$client = new Client();
$res = $client->request('POST', 'https://url_to_the_api', [
'form_params' => [
'client_id' => 'test_id',
'secret' => 'test_secret',
]
]);
$result= $res->getBody();
dd($result);
}
First you have to make routes in your app/routes.php
/*
API Routes
*/
Route::group(array('prefix' => 'api/v1', 'before' => 'auth.basic'), function()
{
Route::resource('pages', 'PagesController', array('only' => array('index', 'store', 'show', 'update', 'destroy')));
Route::resource('users', 'UsersController');
});
Note: If you are not required authentication for API call, you can remove 'before' => 'auth.basic'
Here you can access index, store, show, update and destroy methods from your PagesController.
And the request urls will be,
GET http://localhost/project/api/v1/pages // this will call index function
POST http://localhost/project/api/v1/pages // this will call store function
GET http://localhost/project/api/v1/pages/1 // this will call show method with 1 as arg
PUT http://localhost/project/api/v1/pages/1 // this will call update with 1 as arg
DELETE http://localhost/project/api/v1/pages/1 // this will call destroy with 1 as arg
The command line CURL request will be like this (here the username and password are admin) and assumes that you have .htaccess file to remove index.php from url,
curl --user admin:admin localhost/project/api/v1/pages
curl --user admin:admin -d 'title=sample&slug=abc' localhost/project/api/v1/pages
curl --user admin:admin localhost/project/api/v1/pages/2
curl -i -X PUT --user admin:admin -d 'title=Updated Title' localhost/project/api/v1/pages/2
curl -i -X DELETE --user admin:admin localhost/project/api/v1/pages/1
Next, you have two controllers named PagesController.php and UsersController.php in your app/controllers folder.
The PagesController.php,
<?php
class PagesController extends BaseController {
/**
* Display a listing of the resource.
*
* #return Response
* curl --user admin:admin localhost/project/api/v1/pages
*/
public function index() {
$pages = Page::all();;
return Response::json(array(
'status' => 'success',
'pages' => $pages->toArray()),
200
);
}
/**
* Store a newly created resource in storage.
*
* #return Response
* curl --user admin:admin -d 'title=sample&slug=abc' localhost/project/api/v1/pages
*/
public function store() {
// add some validation also
$input = Input::all();
$page = new Page;
if ( $input['title'] ) {
$page->title =$input['title'];
}
if ( $input['slug'] ) {
$page->slug =$input['slug'];
}
$page->save();
return Response::json(array(
'error' => false,
'pages' => $page->toArray()),
200
);
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
* curl --user admin:admin localhost/project/api/v1/pages/2
*/
public function show($id) {
$page = Page::where('id', $id)
->take(1)
->get();
return Response::json(array(
'status' => 'success',
'pages' => $page->toArray()),
200
);
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
* curl -i -X PUT --user admin:admin -d 'title=Updated Title' localhost/project/api/v1/pages/2
*/
public function update($id) {
$input = Input::all();
$page = Page::find($id);
if ( $input['title'] ) {
$page->title =$input['title'];
}
if ( $input['slug'] ) {
$page->slug =$input['slug'];
}
$page->save();
return Response::json(array(
'error' => false,
'message' => 'Page Updated'),
200
);
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
* curl -i -X DELETE --user admin:admin localhost/project/api/v1/pages/1
*/
public function destroy($id) {
$page = Page::find($id);
$page->delete();
return Response::json(array(
'error' => false,
'message' => 'Page Deleted'),
200
);
}
}
Then you have model named Page which will use table named pages.
<?php
class Page extends Eloquent {
}
You can use Laravel4 Generators to create these resources using php artisan generator command. Read here.
So using this route grouping you can use the same application to make API request and as a front-end.
You can choose what to use:
Guzzle
CURL
file_get_contents :
$json = json_decode(file_get_contents('http://host.com/api/v1/users/1'), true);
Referrer
Try looking into the external API's manuals. There you will find info on how to retrieve information.
Then the best plan is to build an Interface.
Check this out:
http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories/
It's up to you how you use php to solve this.