tutorial español laravel oauth-2.0 laravel-5.3 laravel-passport passport scopes

español - Alcance del pasaporte Laravel



laravel passport tutorial (6)

Estoy un poco confundido en la parte de laravel scopes.

Tengo un modelo de usuario y una tabla.

¿Cómo puedo asignar a un usuario el rol de usuario, cliente y / o administrador?

Tengo un SPA con vue y laravel api backend. Yo uso https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript

Passport::tokensCan([ ''user'' => ''User'', ''customer'' => ''Customer'', ''admin'' => ''Admin'', ]);

¿Cómo puedo asignar qué modelo de usuario tiene qué alcance (s)?

¿O los ámbitos no son lo mismo que los roles?

¿Cómo implementaría esto?

¡Gracias por adelantado!


¿O los ámbitos no son lo mismo que los roles?

La mayor diferencia entre los dos es el contexto al que se aplican. El control de acceso basado en roles (RBAC) gobierna el control de acceso de un usuario cuando usa la aplicación web directamente , mientras que el alcance Oauth-2 gobierna el acceso a los recursos API para un cliente externo en nombre de un usuario.

¿Cómo puedo asignar qué modelo de usuario tiene qué alcance (s)?

En general, el flujo de Oauth, se solicita a un usuario (como propietario de un recurso) que autorice a un cliente sobre las cosas que puede y no puede hacer en su nombre, esto es lo que usted llama alcance . En caso de autorización exitosa, el alcance solicitado por el cliente se asignará al token generado, no al usuario per se.

Según el flujo de concesión de Oauth que elija, el cliente debe incluir el alcance en su solicitud. En el flujo de concesión del código de autorización, el alcance debe incluirse en el parámetro de consulta HTTP GET al redirigir al usuario a la página de autorización, mientras que en el flujo de concesión de contraseña, el alcance debe incluirse en el parámetro del cuerpo HTTP POST para solicitar un token.

¿Cómo implementaría esto?

Este es un ejemplo con el flujo de concesión de contraseña, suponiendo que haya completado la configuración de laravel/passport antemano

Definir ámbitos para el rol de administrador y usuario. Sea lo más específico posible, por ejemplo: el administrador puede gestionar el pedido y el usuario solo lo lee.

// in AuthServiceProvider boot Passport::tokensCan([ ''manage-order'' => ''Manage order scope'' ''read-only-order'' => ''Read only order scope'' ]);

Prepare el controlador REST

// in controller namespace App/Http/Controllers; class OrderController extends Controller { public function index(Request $request) { // allow listing all order only for token with manage order scope } public function store(Request $request) { // allow storing a newly created order in storage for token with manage order scope } public function show($id) { // allow displaying the order for token with both manage and read only scope } }

Asigne la ruta con la protección de la API y el alcance

// in api.php Route::get(''/api/orders'', ''OrderController@index'') ->middleware([''auth:api'', ''scopes:manage-order'']); Route::post(''/api/orders'', ''OrderController@store'') ->middleware([''auth:api'', ''scopes:manage-order'']); Route::get(''/api/orders/{id}'', ''OrderController@show'') ->middleware([''auth:api'', ''scopes:manage-order, read-only-order'']);

Y al emitir un token, verifique primero el rol del usuario y otorgue el alcance en función de ese rol. Para lograr esto, necesitamos un controlador adicional que use el rasgo AuthenticatesUsers para proporcionar un punto final de inicio de sesión.

namespace App/Http/Controllers/Auth; use App/Http/Controllers/Controller; use Illuminate/Foundation/Auth/AuthenticatesUsers; use Illuminate/Http/Request; use Illuminate/Support/Facades/Route; class ApiLoginController extends Controller { use AuthenticatesUsers; protected function authenticated(Request $request, $user) { // implement your user role retrieval logic, for example retrieve from `roles` database table $role = $user->checkRole(); // grant scopes based on the role that we get previously if ($role == ''admin'') { $request->request->add([ ''scope'' => ''manage-order'' // grant manage order scope for user with admin role ]); } else { $request->request->add([ ''scope'' => ''read-only-order'' // read-only order scope for other user role ]); } // forward the request to the oauth token request endpoint $tokenRequest = Request::create( ''/oauth/token'', ''post'' ); return Route::dispatch($tokenRequest); } }

Agregar ruta para el punto final de inicio de sesión de API

//in api.php Route::group(''namespace'' => ''Auth'', function () { Route::post(''login'', ''ApiLoginController@login''); });

En lugar de hacer POST a / oauth / token route, POST al punto final de inicio de sesión de API que proporcionamos antes

// from client application $http = new GuzzleHttp/Client; $response = $http->post(''http://your-app.com/api/login'', [ ''form_params'' => [ ''grant_type'' => ''password'', ''client_id'' => ''client-id'', ''client_secret'' => ''client-secret'', ''username'' => ''[email protected]'', ''password'' => ''my-password'', ], ]); return json_decode((string) $response->getBody(), true);

Tras una autorización exitosa, se emitirá un access_token y un refresh_token basado en el alcance que definimos antes para la aplicación cliente. Guarde eso en algún lugar e incluya el token en el encabezado HTTP cada vez que realice una solicitud a la API.

// from client application $response = $client->request(''GET'', ''/api/my/index'', [ ''headers'' => [ ''Accept'' => ''application/json'', ''Authorization'' => ''Bearer ''.$accessToken, ], ]);

La API ahora debería volver

{"error":"unauthenticated"}

siempre que se use un token con privilegios inferiores para consumir puntos finales restringidos.


¡Gracias por esto, esta pregunta estuvo en mi mente por un tiempo! Tomé la solución de Raymond Lagonda y la personalicé un poco para Laravel 5.6, usando la limitación de velocidad incorporada, usando un solo cliente de thirdparty (o más personalizado si es necesario), mientras le daba a cada usuario una lista de permisos (ámbitos).

  • Utiliza la concesión de password Laravel Passport y sigue el flujo de Oauth
  • Le brinda la capacidad de establecer roles (ámbitos) para diferentes usuarios
  • no exponga / libere la identificación del cliente o el secreto del cliente, solo el nombre de usuario (correo electrónico) y la contraseña del usuario, más o menos una concesión de password , menos las cosas del cliente / concesión

Ejemplos en la parte inferior

rutas / api.php

Route::group([''namespace'' => ''ThirdParty'', ''prefix'' => ''thirdparty''], function () { Route::post(''login'', ''ApiLoginController@login''); });

ThirdParty / ApiLoginController.php

<?php namespace App/Http/Controllers/ThirdParty; use Hash; use App/User; use App/ThirdParty; use Illuminate/Http/Request; use Illuminate/Support/Facades/Route; use App/Http/Controllers/Controller; use Illuminate/Foundation/Auth/AuthenticatesUsers; class ApiLoginController extends Controller { use AuthenticatesUsers; /** * Thirdparty login method to handle different * clients logging in for different reasons, * we assign each third party user scopes * to assign to their token, so they * can perform different API tasks * with the same token. * * @param Request $request * @return Illuminate/Http/Response */ protected function login(Request $request) { if ($this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); } $user = $this->validateUserLogin($request); $client = ThirdParty::where([''id'' => config(''thirdparties.client_id'')])->first(); $request->request->add([ ''scope'' => $user->scopes, ''grant_type'' => ''password'', ''client_id'' => $client->id, ''client_secret'' => $client->secret ]); return Route::dispatch( Request::create(''/oauth/token'', ''post'') ); } /** * Validate the users login, checking * their username/password * * @param Request $request * @return User */ public function validateUserLogin($request) { $this->incrementLoginAttempts($request); $username = $request->username; $password = $request->password; $user = User::where([''email'' => $username])->first(); abort_unless($user, 401, ''Incorrect email/password.''); $user->setVisible([''password'']); abort_unless(Hash::check($password, $user->password), 401, ''Incorrect email/password.''); return $user; } }

config / thirdparties.php

<?php return [ ''client_id'' => env(''THIRDPARTY_CLIENT_ID'', null), ];

ThirdParty.php

<?php namespace App; use Illuminate/Database/Eloquent/Model; class ThirdParty extends Model { protected $table = ''oauth_clients''; }

.env

## THIRDPARTIES THIRDPARTY_CLIENT_ID=3

php artesanal make: migración add_scope_to_users_table --table = users

// up Schema::table(''users'', function (Blueprint $table) { $table->text(''scopes'')->nullable()->after(''api_access''); }); // down Schema::table(''users'', function (Blueprint $table) { $table->dropColumn(''scopes''); });

(nota: api_access es un indicador que decide si un usuario puede iniciar sesión en el sitio web / parte frontend de la aplicación, para ver paneles / registros, etc.),

rutas / api.php

Route::group([''middleware'' => [''auth.client:YOUR_SCOPE_HERE'', ''throttle:60,1'']], function () { ...routes... });

MySQL - Ámbitos de usuario

INSERT INTO `users` (`id`, `created_at`, `updated_at`, `name`, `email`, `password`, `remember_token`, `api_access`, `scopes`) VALUES (5, ''2019-03-19 19:27:08'', ''2019-03-19 19:27:08'', '''', ''[email protected]'', ''YOUR_HASHED_PASSWORD'', NULL, 1, ''YOUR_SCOPE_HERE ANOTHER_SCOPE_HERE'');

MySQL - ThirdParty Oauth Client

INSERT INTO `oauth_clients` (`id`, `user_id`, `name`, `secret`, `redirect`, `personal_access_client`, `password_client`, `revoked`, `created_at`, `updated_at`) VALUES (3, NULL, ''Thirdparty Password Grant Client'', ''YOUR_SECRET'', ''http://localhost'', 0, 1, 0, ''2019-03-19 19:12:37'', ''2019-03-19 19:12:37'');

cURL: iniciar sesión / solicitar un token

curl -X POST / http://site.localhost/api/v1/thirdparty/login / -H ''Accept: application/json'' / -H ''Accept-Charset: application/json'' / -F [email protected] / -F password=YOUR_UNHASHED_PASSWORD

{ "token_type": "Bearer", "expires_in": 604800, "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciO...", "refresh_token": "def502008a75cd2cdd0dad086..." }

¡Use access_token / refresh_token de larga duración como de costumbre!

Acceder al alcance prohibido

{ "data": { "errors": "Invalid scope(s) provided." }, "meta": { "code": 403, "status": "FORBIDDEN" } }


Con la solución @RaymondLagonda. Si obtiene un error de alcance de clase no encontrado, agregue el siguiente middleware a la propiedad $routeMiddleware de su archivo app/Http/Kernel.php :

''scopes'' => /Laravel/Passport/Http/Middleware/CheckScopes::class, ''scope'' => /Laravel/Passport/Http/Middleware/CheckForAnyScope::class,`

Además, si recibe el error Error de Type error: Too few arguments to function , debería poder obtener el $user de la solicitud como se muestra a continuación.

(Estoy usando laratrust para gestionar roles)

public function login(Request $request) { $email = $request->input(''username''); $user = User::where(''email'',''='',$email)->first(); if($user && $user->hasRole(''admin'')){ $request->request->add([ ''scope'' => ''manage-everything'' ]); }else{ return response()->json([''message'' => ''Unauthorized''],403); } $tokenRequest = Request::create( ''/oauth/token'', ''post'' ); return Route::dispatch($tokenRequest); }


Implemente la respuesta Raymond Lagonda y funciona muy bien, solo para tener cuidado con lo siguiente. Debe anular algunos métodos de los rasgos AuthenticatesUsers en ApiLoginController:

/** * Send the response after the user was authenticated. * * @param /Illuminate/Http/Request $request * @return /Illuminate/Http/Response */ protected function sendLoginResponse(Request $request) { // $request->session()->regenerate(); // coment this becose api routes with passport failed here. $this->clearLoginAttempts($request); return $this->authenticated($request, $this->guard()->user()) ?: response()->json(["status"=>"error", "message"=>"Some error for failes authenticated method"]); } /** * Get the failed login response instance. * * @param /Illuminate/Http/Request $request * @return /Illuminate/Http/RedirectResponse */ protected function sendFailedLoginResponse(Request $request) { return response()->json([ "status"=>"error", "message"=>"Autentication Error", "data"=>[ "errors"=>[ $this->username() => Lang::get(''auth.failed''), ] ] ]); }

Si cambió el campo de inicio de sesión: nombre de usuario a un campo de nombre de usuario personalizado, por ejemplo: correo electrónico. Debe refinar el método de nombre de usuario como en su LoginController. También debe redefinir y editar los métodos: validateLogin, intenté iniciar sesión, credenciales ya que una vez que se valida el inicio de sesión, la solicitud se reenvía al pasaporte y debe llamarse nombre de usuario.


Logré que esto funcione, con la solución @RaymondLagonda, para Laravel 5.5 con Sentinel , pero debería funcionar también sin Sentinel.

La solución necesita una alteración temporal de los métodos de clase (así que tenga esto en cuenta para futuras actualizaciones) y agrega cierta protección a sus rutas de API (no exponiendo client_secret, por ejemplo).

El primer paso es modificar su ApiLoginController para agregar la función de construcción:

public function __construct(Request $request){ $oauth_client_id = env(''PASSPORT_CLIENT_ID''); $oauth_client = OauthClients::findOrFail($oauth_client_id); $request->request->add([ ''email'' => $request->username, ''client_id'' => $oauth_client_id, ''client_secret'' => $oauth_client->secret]); }

En este ejemplo, debe definir var (''PASSPORT_CLIENT_ID'') en su .env y crear el Modelo OauthClients, pero puede omitir esto de manera segura colocando aquí los valores de prueba adecuados.

Una cosa a tener en cuenta es que estamos configurando $request->email value a username, solo para cumplir con la convención Oauth2.

El segundo paso es, para anular, el método sendLoginResponse que está causando errores como el Session storage not set , no necesitamos sesiones aquí, porque es api.

protected function sendLoginResponse(Request $request) { // $request->session()->regenerate(); $this->clearLoginAttempts($request); return $this->authenticated($request, $this->guard()->user()) ?: redirect()->intended($this->redirectPath()); }

El tercer paso es modificar sus métodos autenticados como lo sugiere @RaymondLagonda. Debe escribir su propia lógica aquí, y especialmente configurar sus ámbitos.

Y el último paso (en caso de que esté utilizando Sentinel) es modificar AuthServiceProvider . Añadir

$this->app->rebinding(''request'', function ($app, $request) { $request->setUserResolver(function () use ($app) { return /Auth::user(); // return $app[''sentinel'']->getUser(); }); });

justo después de $this->registerPolicies(); en el método de arranque.

Después de estos pasos, debería poder hacer que su API funcione, proporcionando nombre de usuario (''esto siempre será correo electrónico, en esta implementación''), contraseña y grant_type = ''contraseña''

En este punto, puede agregar a los ámbitos de scopes:... o scope:... para proteger sus rutas.

Espero que realmente ayude ...


Sé que esto es un poco tarde, pero si está consumiendo una API de back-end en un SPA usando CreateFreshApiToken en el middleware web, entonces simplemente puede agregar un middleware ''admin'' a su aplicación:

php artisan make:middleware Admin

Luego, en /App/Http/Middleware/Admin haga lo siguiente:

public function handle($request, Closure $next) { if (Auth::user()->role() !== ''admin'') { return response(json_encode([''error'' => ''Unauthorised'']), 401) ->header(''Content-Type'', ''text/json''); } return $next($request); }

Asegúrese de haber agregado el método de role a /App/User para recuperar el rol de usuario.

Ahora todo lo que necesita hacer es registrar su middleware en la app/Http/Kernel.php $routeMiddleware , así:

protected $routeMiddleware = [ // Other Middleware ''admin'' => /App/Http/Middleware/Admin::class, ];

Y agregue eso a su ruta en routes/api.php

Route::middleware([''auth:api'',''admin''])->get(''/customers'',''Api/CustomersController@index'');

Ahora, si intenta acceder a la API sin permiso, recibirá un error "401 no autorizado", que puede verificar y manejar en su aplicación.