En este primer post sobre como crear una api segura usando Slim Framework y un servidor Oauth 2 para php, vamos a tratar el sistema de generación de access token para que los usuarios a los que se le de permiso puedan acceder a nuestra api.
En este ejemplo vamos a necesitar crear en nuestra Base de datos las siguientes tablas necesarias para trabajar con Oauth 2
CREATE TABLE oauth_clients (client_id VARCHAR(80) NOT NULL, client_secret VARCHAR(80), redirect_uri VARCHAR(2000) NOT NULL, grant_types VARCHAR(80), scope VARCHAR(100), user_id VARCHAR(80), CONSTRAINT clients_client_id_pk PRIMARY KEY (client_id));
CREATE TABLE oauth_access_tokens (access_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(255), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT access_token_pk PRIMARY KEY (access_token));
CREATE TABLE oauth_authorization_codes (authorization_code VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(255), redirect_uri VARCHAR(2000), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT auth_code_pk PRIMARY KEY (authorization_code));
CREATE TABLE oauth_refresh_tokens (refresh_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(255), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT refresh_token_pk PRIMARY KEY (refresh_token));
CREATE TABLE oauth_users (username VARCHAR(255) NOT NULL, password VARCHAR(2000), first_name VARCHAR(255), last_name VARCHAR(255), CONSTRAINT username_pk PRIMARY KEY (username));
CREATE TABLE oauth_scopes (scope TEXT, is_default BOOLEAN);
CREATE TABLE oauth_jwt (client_id VARCHAR(80) NOT NULL, subject VARCHAR(80), public_key VARCHAR(2000), CONSTRAINT jwt_client_id_pk PRIMARY KEY (client_id));
Además insertaremos un usuario válido en la tabla oauth_clients con el que haremos las pruebas de accesso
INSERT INTO `oauth_clients` (`client_id`, `client_secret`, `redirect_uri`) VALUES ('prueba', 'prueba', 'http://url.redireccion.com');
Para empezar con el código, cargamos las librerias Oauth 2 y configuramos PDO como Storage de nuestro sistema de acceso seguro a la api
require 'vendor/autoload.php';
$dsn = 'mysql:host=localhost;dbname=bdname';
$username = "user";
$password = "password";
$storage = new OAuth2\Storage\Pdo(array(
'dsn' => $dsn,
'username' => $username,
'password' => $password
));
Realizamos unas configuraciones en nuestro servidor Oauth para que con la petición se nos devuelva directamente un token de acceso, esto lo logramos con el parámetro allow_implicit igual a true (más info), el parámetro enforce_state es obligatorio ponerlo a false según la documentación, si no, nos devolverá un error al lanzar la petición (más info)
$config = array(
'enforce_state' => false,
'allow_implicit' => true,
);
Instanciamos el servidor con todo lo configurado previamente
$server = new OAuth2\Server($storage, $config);
Configuramos el scope(ambito donde daremos permiso al usuario) por defecto y todos los scope soportados, para la variable $defaultScope en este caso le he dado un valor directamente, lo suyo seria que en la petición le pasasemos como parñametro este valor, para hacerlo más funcional, pero como es un ejemplo nos vale así. Usamos la clase Scope() y el metodo setScopeUtil para pasar al servidor la configuración
$defaultScope = 'scope1';
$supportedScopes = array(
'scope1',
'scope2',
'scope3'
);
$scope = new OAuth2\Scope(array(
'default_scope' => $defaultScope,
'supported_scopes' => $supportedScopes
));
$server->setScopeUtil($scope);
Como vamos a trabajar con un tipo de autorización Implicit para que nos devuelva el token directamente configuramos el servidor con el grantType AuthorizationCode que es el necesario para estos casos
$grantType = new OAuth2\GrantType\AuthorizationCode($storage);
$server->addGrantType($grantType);
$request = OAuth2\Request::createFromGlobals();
$response = new OAuth2\Response();
Usamos la funcion validateAuthorizeRequest pasándole el request y el response.
Si la función encuentra algún problema lanzará el error y se parará la ejecución del código en caso contrario
if (!$server->validateAuthorizeRequest($request, $response)) {
$response->send();
die;
}
Muestra un form que nos pregunta si queremos autorizar al cliente y generar así un token de acceso para que este pueda acceder a la api. Si respondemos que sí
if (empty($_POST)) {
exit('
<form method="post">
<label>Quiere autoriar al cliente?</label><br />
<input type="submit" name="authorized" value="yes">
<input type="submit" name="authorized" value="no">
</form>');
}
$is_authorized controla la respuesta en el form y lo pasa como parámetro a la función que genera el token de acceso, el valor necesario es yes
$server->handleAuthorizeRequest genera el token y lo guarda en la base de datos
$is_authorized = ($_POST['authorized'] === 'yes');
$server->handleAuthorizeRequest($request, $response, $is_authorized);
Esta parte es solo para mostrar el codigo en pantalla, una vez salvado en la BD
$is_authorized = ($_POST['authorized'] === 'yes');
$server->handleAuthorizeRequest($request, $response, $is_authorized);
if ($is_authorized) {
$token = substr($response->getHttpHeader('Location') , strpos($response->getHttpHeader('Location') , 'access_token=') + 13, 40);
echo $token;
}
Código completo del ejemplo:
require 'vendor/autoload.php';
$dsn = 'mysql:host=localhost;dbname=bdname';
$username = "user";
$password = "password";
$storage = new OAuth2\Storage\Pdo(array(
'dsn' => $dsn,
'username' => $username,
'password' => $password
));
$config = array(
'enforce_state' => false,
'allow_implicit' => true,
);
$server = new OAuth2\Server($storage, $config);
$defaultScope = 'scope1';
$supportedScopes = array(
'scope1',
'scope2',
'scope3'
);
$scope = new OAuth2\Scope(array(
'default_scope' => $defaultScope,
'supported_scopes' => $supportedScopes
));
$server->setScopeUtil($scope);
$grantType = new OAuth2\GrantType\AuthorizationCode($storage);
$server->addGrantType($grantType);
$request = OAuth2\Request::createFromGlobals();
$response = new OAuth2\Response();
if (!$server->validateAuthorizeRequest($request, $response)) {
$response->send();
die;
}
if (empty($_POST)) {
exit('
<form method="post">
<label>Quiere autoriar al cliente?</label><br />
<input type="submit" name="authorized" value="yes">
<input type="submit" name="authorized" value="no">
</form>');
}
$is_authorized = ($_POST['authorized'] === 'yes');
$server->handleAuthorizeRequest($request, $response, $is_authorized);
$is_authorized = ($_POST['authorized'] === 'yes');
$server->handleAuthorizeRequest($request, $response, $is_authorized);
if ($is_authorized) {
$token = substr($response->getHttpHeader('Location') , strpos($response->getHttpHeader('Location') , 'access_token=') + 13, 40);
echo $token;
}
Para probar este ejemplo y generar un access token solo cargaremos la url donde se encuentra el código y pasaremos en la url los parámetros user_id y response type con el valor token que es el apropiado para el grant_type implicit . Ejemplo: http://urldelgeneradordetokens/file.php?response_type=token&client_id=prueba
En la segunda parte del post, crearemos una pequeña api usando Slimframework y accederemos de manera segura a los métodos con los access token generados en esta primera parte de la creación de la api.
Fuente de documentación: Bshaffer – Oauth2 Server for php