OAuth2

e

Anderson Vinicius

S

Quem usa APIs?

Porque usar APIs?

APIs abrem portas para parcerias, crescimento de seu produto, ter aplicações pequenas e bem definidas, equipes com responsabilidades menores, assim mais fácil de alocar um novo desenvolvedor na equipe, pois não necessita-se conhecer todo o ecossistema do produto final e sim um domínio especifico ou seja a API que ele foi alocado.

 

Com o uso de APIs não ficamos presos a tecnologias de back-end, pois hoje posso escrever minha API de produtos versão 1.0.0 em PHP e a versão 2.0.0 em NodeJs.

 

Podemos escalar nossos serviços de forma mais precisa, assim economizando na nossa infraestrutura Cloud, reaproveitamos nossa lógica de negócio assim não replicando em diferentes sistemas da empresa.

 

Conseguimos expandir nossos produtos utilizando APIs de parceiros para melhor o potencial do nosso produto, todo negócio hoje é tecnológico caso não seja ele perde a competitividade no mercado.

Mas nem tudo são flores, vem as dificuldades com deploys, segurança, autenticação, provisionamento dessa infra em ambiente de desenvolvimento, homologação e produção.

A comunicação entre uma API com a outra, ter ferramentas para  governança e monitoramento dessas APIs.

Problemas no uso de APIs

  • Segurança.
  • Aplicações distribuídas.
  • API Externas.
  • Autenticação de usuários
  • Autenticação de clientes.

Nos dias de hoje que tudo é uma API, temos APIs conversando com outras, clientes ou aplicações consumindo serviços de nossas APIs, temos três tipos a Open, Restrict e Private.

Em APIs não guardamos estado, elas são e devem ser stateless. Assim temos que de alguma forma identificar o usuário ou cliente que está acessando nossa API, assim podemos verificar se ele possui autorização para determinada ação ou recurso, ter mecanismos de cache externo a aplicação assim podemos escalar o serviço quando necessário.

 

Gerenciar os builds, no caso da nossa empresa temos ambiente de desenvolvimento, homologação e produção que utiliza Docker de ponta a ponta, assim promovendo a cultura DevOps na equipe.

Não exponha sua API dessa forma isso gera um impacto negativo para seu negócio, seja RESTFull fique no padrão.  

Caminho de sucesso com APIs

  • Utilize padrões http://microservices.io
  • APIs RESTFull
  • API Gateway
  • OAuth2 (segurança)
  • Https
  • Microservices
  • Versionamento

O que eu vejo como mais importante para o desacoplamento e ponto único de entrada e autenticação é o APIGateway, não importa aonde esteja seu serviço ele será encontrado pelo seu cliente.

 APIGateway                                                             Versionamento

O que é OAuth2 ?

  • Protocolo de comunicação.
  • Padrão
  • Acesso de aplicações e API externas.
  • Aplicações e API externas são clientes.
  • Todo cliente tem o acesso limitado ao protocolo HTTP.
  • Acesso feito via token de acesso.

Temos a primeira versão do protocolo que é o OAuth1, mas hoje é amplamente usado o OAuth2, um exemplo de uso mais comum é quando se cadastramos em algum site que  possua autenticação com o Google por exemplo, autorizamos pegar os dados de nossa conta para utilizar no cadastro, é uma das formas de autenticação com OAuth2.

 

Diferentemente do JWT que é apenas a troca de tokens, OAuth2 é um servidor que vai controlar a autenticação e autorização das nossas APIs, temos todo o controle de tokens e aplicações, podemos definir escopos para as aplicações, por exemplo configurar uma autorização para um parceiro consumir uma parte da nossa API.

Devemos utilizar HTTPS no nosso server OAuth2, para segurança do token JWT que trafega na rede.

Nada impede que você  implemente uma biblioteca OAuth2, desde que siga os padrões que está na Internet Engineering Task Force (IETF), é um padrão bem complexo não é fácil de se implementar.

Hoje com o  Laravel 5.3 temos um pacote bem completo para trabalhar com OAuth no PHP que é o passport.

Client

Server OAuth2

API

Request de autorização

Token de acesso

Request em algum recurso

Valida o token

Devolve a resposta caso esteja autorizado

Funcionamento básico do OAuth2

  • O token de acesso possui um tempo de vida.
  • O token de acesso pode ser renovado (Refresh Token).
  • O token pode ser revogado (Revoke).

Características

  • Centralização e padronização
    • Indentity Manager
    • Autenticação
  • Papéis bem definidos
    • Resource Owner (o usuário)
    • Resource Server (API onde estão os dados)
    • Client (aplicação que quer acessar a API)
    • Authorization server (Google, Github, Focus)
  • Access token / Refresh token
  • HTTPS

Exemplo em uma aplicação de ecommerce

Fluxos de autorização

Authorization code

Implicit

Resource Owner Password

Client credentials

Como passar o token ?

As duas formas estão na IETF, mas a preferência é para a primeira passando por header no corpo da requisição.

Vamos para um exemplo com Laravel Passport

$ composer create-project --prefer-dist laravel/laravel oauth
$ cd oauth
$ ls -al
total 220
drwxrwxr-x 13 avinicius avinicius   4096 Dez  7 12:02 .
drwxrwxr-x  5 avinicius avinicius   4096 Dez  7 10:30 ..
drwxrwxr-x  6 avinicius avinicius   4096 Out  2 23:33 app
-rwxr-xr-x  1 avinicius avinicius   1646 Out  2 23:33 artisan
drwxrwxr-x  3 avinicius avinicius   4096 Out  2 23:33 bootstrap
-rw-rw-r--  1 avinicius avinicius   1283 Out  2 23:33 composer.json
-rw-rw-r--  1 avinicius avinicius 124495 Dez  7 10:32 composer.lock
drwxrwxr-x  2 avinicius avinicius   4096 Out  2 23:33 config
drwxrwxr-x  5 avinicius avinicius   4096 Out  2 23:33 database
-rw-rw-r--  1 avinicius avinicius    542 Dez  7 10:32 .env
-rw-rw-r--  1 avinicius avinicius    491 Out  2 23:33 .env.example
-rw-rw-r--  1 avinicius avinicius     61 Out  2 23:33 .gitattributes
-rw-rw-r--  1 avinicius avinicius     80 Out  2 23:33 .gitignore
-rw-rw-r--  1 avinicius avinicius    558 Out  2 23:33 gulpfile.js
drwxrwxr-x  2 avinicius avinicius   4096 Dez  7 13:31 .idea
-rw-rw-r--  1 avinicius avinicius    401 Out  2 23:33 package.json
-rw-rw-r--  1 avinicius avinicius    930 Out  2 23:33 phpunit.xml
drwxrwxr-x  4 avinicius avinicius   4096 Out  2 23:33 public
-rw-rw-r--  1 avinicius avinicius   1918 Out  2 23:33 readme.md
drwxrwxr-x  5 avinicius avinicius   4096 Out  2 23:33 resources
drwxrwxr-x  2 avinicius avinicius   4096 Out  2 23:33 routes
-rw-rw-r--  1 avinicius avinicius    563 Out  2 23:33 server.php
drwxrwxr-x  5 avinicius avinicius   4096 Out  2 23:33 storage
drwxrwxr-x  2 avinicius avinicius   4096 Out  2 23:33 tests
drwxrwxr-x 31 avinicius avinicius   4096 Dez  7 10:32 vendor
$ 

Instalando Laravel na sua versão estável.

Utilizaremos o sqlite para o exemplo ser bem simples, deixe seu arquivo .env igual o da imagem e crie na pasta database/ um arquivo database.sqlite.

$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
$

Rodando migrations para testar a conexão com SQLite.

$ php artisan make:seeder UsersTableSeeder
Seeder created successfully.

Criando um seeder para cadastrar alguns usuários.

Configurando a seeder para criar dois usuários e adicionar sua chamada no DatabaseSeeder.php.

$ php artisan db:seed
Seeded: UsersTableSeeder
$ sqlite3 database/database.sqlite
SQLite version 3.11.0 2016-02-15 17:29:24
Enter ".help" for usage hints.
sqlite> select * from users;
1|Herminio Glover|avinicius.adorno@gmail.com|$2y$10$jlNMwJoCCbfGqwbw1XK.euO1HZiiWDr5pLqOGZDIOAROcMDM7QQY2|8m2hxJPynO|2016-12-07 16:01:41|2016-12-07 16:01:41
2|Prof. Ursula Aufderhar V|anderson@focusconcursos.com.br|$2y$10$jlNMwJoCCbfGqwbw1XK.euO1HZiiWDr5pLqOGZDIOAROcMDM7QQY2|7ZlotJS4dm|2016-12-07 16:01:41|2016-12-07 16:01:41
sqlite> 

Agora bastar executar o comando a baixo no terminal e os dois usuários serão inseridos no banco de dados.

Criando login convencional para nossa aplicação que manterá os dados do nosso OAuth2.

$ php artisan make:auth
Authentication scaffolding generated successfully.
$ php artisan serve
Laravel development server started on http://localhost:8000/

Acesse o link http://localhost:8000/login e será aberto a tela de login já funcional, logue com um dos usuários criado com a seeder para fazer um teste, lembre-se que a senha padrão foi secret.

Toda a tela é montada usando Bootstrap 3 e VueJS, lembrando que isso foi gerado através do comando executado acima.

Agora já temos a aplicação para do nosso OAuth.

$ composer require laravel/passport
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing phpseclib/phpseclib (2.0.4)
    Downloading: 100%         

  - Installing psr/http-message (1.0.1)
    Loading from cache
...

Instalando e configurando passport.

Adicionando provider.

$ php artisan migrate
Migrated: 2016_06_01_000001_create_oauth_auth_codes_table
Migrated: 2016_06_01_000002_create_oauth_access_tokens_table
Migrated: 2016_06_01_000003_create_oauth_refresh_tokens_table
Migrated: 2016_06_01_000004_create_oauth_clients_table
Migrated: 2016_06_01_000005_create_oauth_personal_access_clients_table
$

Rodando as migrations para criar nossas tabelas de uso do passport.

<?php
namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
    use Notifiable, HasApiTokens;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];
}

Adicionando trait  HasApiTokens para podermos vincular tokens a usuários.

Preparando passport para uso.

$ php artisan passport:install
Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 1
Client Secret: q8eLMplXLrVadOiu07W6rPt2dhH8AjJB6GlPFJa7
Password grant client created successfully.
Client ID: 2
Client Secret: hxVuvD70KPYl5ULcigk7PQmGBwyFtAwlhQTAA8kK

Gerando keys RSA para geração dos tokens.

Gerando as rotas do passport.

Adicionando as a chamada das rotas do passport no AuthServiceProvider.

$ php artisan route:list

Executando o comando abaixo mostra as rotas declaradas no projeto, incluindo as rotas do passport.

Gerando o front-end do passport.

$ npm install
...
$ php artisan vendor:publish --tag=passport-components
Copied Directory [/vendor/laravel/passport/resources/assets/js/components] 
To [/resources/assets/js/components/passport]
Publishing complete for tag [passport-components]!
Vue.component('passport-clients', require('./components/passport/Clients.vue'));
Vue.component('passport-authorized-clients', require('./components/passport/AuthorizedClients.vue'));
Vue.component('passport-personal-access-tokens', require('./components/passport/PersonalAccessTokens.vue'));

Adicione esse components no resourses/assets/js/app.js.

E rode o gulp para compilar essas mudanças no JS.

$ gulp
[15:04:19] Using gulpfile ~/workspace/apis/oauth/gulpfile.js
[15:04:19] Starting 'all'...
[15:04:19] Starting 'sass'...
[15:04:20] Finished 'sass' after 673 ms
[15:04:20] Starting 'webpack'...
[15:04:21] 
[15:04:21] Finished 'webpack' after 1.39 s
[15:04:21] Finished 'all' after 2.07 s
[15:04:21] Starting 'default'...
┌───────────────┬───────────────────────────────┬────────────────────────────────┬────────────────────┐
│ Task          │ Summary                       │ Source Files                   │ Destination        │
├───────────────┼───────────────────────────────┼────────────────────────────────┼────────────────────┤
│ mix.sass()    │ 1. Compiling Sass             │ resources/assets/sass/app.scss │ public/css/app.css │
│               │ 2. Autoprefixing CSS          │                                │                    │
│               │ 3. Concatenating Files        │                                │                    │
│               │ 4. Writing Source Maps        │                                │                    │
│               │ 5. Saving to Destination      │                                │                    │
├───────────────┼───────────────────────────────┼────────────────────────────────┼────────────────────┤
│ mix.webpack() │ 1. Transforming ES2015 to ES5 │ resources/assets/js/app.js     │ public/js/app.js   │
│               │ 2. Writing Source Maps        │                                │                    │
│               │ 3. Saving to Destination      │                                │                    │
└───────────────┴───────────────────────────────┴────────────────────────────────┴────────────────────┘
[15:04:21] Finished 'default' after 4.12 ms
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <passport-clients></passport-clients>
            <passport-authorized-clients></passport-authorized-clients>
            <passport-personal-access-tokens></passport-personal-access-tokens>
        </div>
    </div>
</div>
@endsection

Adicionando os components na nossa view home que é autenticada.

Colocando driver de rotas api para passport, assim podemos consumir nossos endpoints de api se existirem em nosso Server.

Criando um personal token para acessar uma rota do nosso app, os personal tokens tem um período de vida bem longo, geralmente usado para desenvolvimento.

Podemos validar esse nosso token no site https://jwt.io/.

O token foi validado com nossas chaves RSA, e conseguimos ver os dados do payload.

Entenda como o JWT funciona

Acessando uma rota de API no nosso server que está o passport instalado. Lembrando que podemos ter APIs fora desse server de autenticação, esse exemplo usamos um endpoint dentro do mesmo server do OAuth2 para testar se ele está validado os tokens.

Referências 

OAuth2 e APIs

By Anderson Vinicius

OAuth2 e APIs

OAuth2 com laravel passport e o uso de APIs

  • 524