# API Neste documento vou explicar como criar uma **API** (Application Programming Interface) para um aplicativo **Rails**. Vou começar criando uma **API** simples, depois mostrando como criar versões dela, depois vou mostrar como criar um sistema de tokens para adicionar segurança e por ultimo vou criar um sistema de autenticação de usúarios via oAuth. ## Criando uma API basica Em Qualquer controlador do rails, é possivel criar uma resposta em **JSON** além da resposta em **HTML**.
class ProductsController < ApplicationController
def index
@products = Products.all
respond_to do |format|
format.html
format.json { render json @products }
end
end
end
Ao entrar na página ```http://localhost:300/products.json```, você vai receber um arquivo em **JSON** com o conteúdo dos produtos.
## Criando rotas para a API
Para criar uma rota como ```http://localhost:3000/api```, modifique o arquivo ```config/rountes.rb```
Store::Application.routes.draw do
namespace :api do
# /api/... API::
namespace :api, defaults: {format: 'json'} do
namespace :v1 do
resources :products
end
namespace :v2 do
resources :products
end
end
end
resources :products
root to: 'products#index'
end
## O Controlador da API
Cria o arquivo ```/app/controllers/api/v1/products_controller.rb```:
module Api
module V1
class ProductsController < ApplicationController # ou API::BaseController
# Hacks para que certos atributos continuem `backword compatiple`
# Aqui estamos modificando a funcionalidade da classe neste controlador
class Product < ::Product
def as_json(options = {})
super.merge(released_on released_at.to_date)
end
end
respond_to :json
def index
respond_with Product.all
end
def show
respond_with Product.find(params[:id])
end
def create
respond_with Product.cerate(params[:product])
end
def update
respond_with Product.update(params[:id], params[:product])
end
def destroy
respond_with Product.destroy(params[:id])
end
end
end
end
## Usando accept headers para versão da API
Em vez de usar um endereço como ```http://localhost:3000/api/v1/```, alguns sites como o [github](http://github.com) usam o **accept header** para especificar a versão do API.
Para configurar as rotas para ceitar o **accept header**, modifique o arquivo ```config/rountes.rb```
require 'api_constraints'
Store::Application.routes.draw do
namespace :api, defaults: {format: 'json'} do
scope module: :v1, constraints: ApiContraints.new(version: 1) do
resources :products
end
scope module: :v2, constraints: ApiConstraints.new(version: 2, default: true) do
resources :products
end
end
resources :products
root to: 'products#index'
end
Depois no arquivo ```lib/api_constraints.rb```:
class ApiConstraints
def initialize(options)
@version = options[:version]
@default = options[:default]
end
def matches?(req)
@default || req.headers['Accept'].include?("application/vnd.example.v#{@version}")
end
end
Agora ao visitar a url ```http://localhost:3000/api/products```, você vai acessar o **API** v2 e receber o arquivo em **JSON**.
Para acessar outra versão da **API** via *curl* no *terminal*:
``` curl -H 'Accept: application.vnd.example.v1' http://localhost:3000/api.products ```
## Autenticação basica
O jeito mas simples de restringir acesso ao **API** é usando a autenticação basica provida pelo **rails**.
No arquivo ```/app/controllers/api/v1/products_controller.rb```:
module Api
module V1
class ProductsController < ApplicationController
http_basic_authenticate_with name: "admin", password: "secret"
respond_to :json
...
Para testar, no terminal, digite o comando:
$ curl http://localhost:3000/api/products
HTTP Basic: Access denied.
$ curl http://localhost:3000/api/products -I
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Application"
Content-Type: text/html; charset=utf-8
...
$ curl http://localhost:3000/api/products -u 'admin:secret'
JSON response
...
## Access Token
Em vez de usar um sistema de autenticação, podemos restringir acesso a API requerindo um **token de acesso**. Se na chamada para o API o cliente não enviar o **token**, o acesso será negado.
Primeiro vamos criar um modelo para guardar os **tokens**. Execute o comando:
```rails generate model api_key access_token```
É possivel tb adicionar outras colunas como ```user_id``` para guardar o dono do **token**, ou ```expires_at``` para destruir chaves antigas ou a colona ```role``` para adicionar permissões diferentes para cada **token**.
Em seguida vamos modificar o modelo em ```app/models/api_key.rb```. Quando um novo *api_key* for criado, ele vai gerar um número randomico unico para está chave.
class ApiKey < ActiveRecord::Base
before_create :generate_access_token
private
def generate_access_token
begin
self.access_token = SecureRandom.hex
end while self.class.exists?(access_token: access_token)
end
end
Para testar, entre no [console](rails_console.md) e digite o comando: ```ApiKey.create!```. Isso vai gerar um novo **access_token** com um número hexadecimal unico.
Agora vamos restringir o acesso ao **API** modificando o controlador. No arquivo ```/app/controllers/api/v1/products_controller.rb```:
module Api
module V1
class ProductsController < ApplicationController
before_filter :restrict_access
respond_to :json
...
private
def restrict_access
api_key = ApiKey.find_by_access_token(params[:access_token])
head :unauthorized unless api_key
end
end
end
end
Para acessar o **API** com o **access_token**, podemos utilizar a seguinte url:
```http://localhost:3000/api/products?access_token=c576f0136149a2e2d9127b3901015545```
Este metodo não é muito seguro, por que as pessoas podem copiar e colar os codigos de acesso junto com a url sem querer. Para melhorar este exemplo, vamos passar o **access_token** atravez de um parametro no *head* do *request* para o servidor.
Vamos mudar de novo o arquivo ```/app/controllers/api/v1/products_controller.rb```:
def restrict_access
authenticate_or_request_with_http_token do |token, options|
ApiKey.exists?(access_token: token)
end
end
Para testar, no terminal vamos accessar de novo a url do **API** mas desta vez passando o **access_token** junto com o *header*:
```curl http://localhost:3000/api/products -H 'Authorization: Token token="c576f0136149a2e2d9127b3901015545"'```
## Autenticação de usuários com OAuth
A questão das soluções anteriores é que estamos criando um sistema de autenticação global. Não temos como saber exatamente qual usuário está acessando a **API**. Então se você necessista de autenticação de usuário via o **API**, o jeito mais comum para fazer isso é utilizando o [OAuth2](http://oauth.net/2/).
> **OAauth** é um protocolo aberto para autorização segura de um **API** de um jeito simples e comum para aplicativos *web*, *mobile* e *desktop*.
O protocolo **OAuth** foi criado pela equipe do twitter e várias outras empresas adotaram e usam o protocolo como o Google, Facebook e GitHub. Ele é utilizado para que um aplicavo possa acessar informações de outro aplicativo através da autorização de um usúario.
O *gem* [doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) serve para simplificar a criação de um serviço de **OAuth** em um aplicativo *rails*.
### Configurando o DoorKeeper
Primeiro, mude para ```false``` a configuração ```whitelist_attributes``` no arquivo ```config/application.rb```:
config_active_record.whitelist_attributes = false
Depois no ```gemfile``` adicione no fim:
gem 'doorkeeper'
Execute o comando ```bundle install``` para instalar o *gem* e depois rode os comandos para instalar o **doorkeeper** e migrar o banco de dados:
rails g doorkeeper:install
rake db:migrate
Varias novos arquivos e tabelas no banco de dados vão ser criados. Em seguida modifique o arquivo ```config/initializers/doorkeeper.rb```:
DoorKeeper.configure do
resource_owner_authenticator do |routes|
#raise "Please configure doorkeeper"
# Put your resource owner authentication logic here
# If you want to use named routes from your app you need
# to call them on routes object eg.
# routes.new_user_session_path
User.find_by_id(session[:user_id]) || redirect_to(routes.new_user_session_url)
end
...
### Repositorios
* [doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) - Doorkeeper is an OAuth 2 provider for Rails and Grape
* [ionic-doorkeeper](https://github.com/emilsoman/ionic-doorkeeper) - Sample ionic app that authenticates with a Rails API protected with Doorkeeper
### Links
* [OAuth Implicit Grant with Grape, Doorkeeper and AngularJS](http://codetunes.com/2014/oauth-implicit-grant-with-grape-doorkeeper-and-angularjs/)
* [How can I pre-authorize a client app for my user on my oauth provider that uses doorkeeper?](http://stackoverflow.com/questions/11129676/how-can-i-pre-authorize-a-client-app-for-my-user-on-my-oauth-provider-that-uses)
* [Using Resource Owner Password Credentials flow](https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow)
* [Doorkeeper + Devise](http://morodeercoding.blogspot.ru/)
* [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
## Criando um API com o Grape gem
### Repositorios
* [grape](https://github.com/intridea/grape) - An opinionated micro-framework for creating REST-like APIs in Ruby.
* [wine_bouncer](https://github.com/antek-drzewiecki/wine_bouncer) - A Ruby gem that allows Oauth2 protection with Doorkeeper for Grape Api's
* [grape-doorkeeper](https://github.com/fuCtor/grape-doorkeeper) - Integration Grape with Doorkeeper*
### Links
* [Build Great APIS with Grape](http://www.sitepoint.com/build-great-apis-grape/)
* [Introduction to building APIs with Grape](http://codetunes.com/2014/introduction-to-building-apis-with-grape/)
* [Building RESTful API using Grape in Rails](http://funonrails.com/2014/03/building-restful-api-using-grape-in-rails/)
* [OAuth 2.0 Tutorial: Protect Grape API with Doorkeeper](http://blog.yorkxin.org/posts/2013/11/05/oauth2-tutorial-grape-api-doorkeeper-en/)
* [authenticate grape api with doorkeeper](http://stackoverflow.com/questions/26472217/authenticate-grape-api-with-doorkeeper)
* [Grape API authentication using Devise Auth Token](http://funonrails.com/2014/03/api-authentication-using-devise-token/)
## Autenticação apenas com o Devise
### Links
* [APIs with Devise](http://soryy.com/blog/2014/apis-with-devise/)
* [API JSON authentication with Devise](https://gist.github.com/jwo/1255275)
## API Reference
* [A Well Designed API Approach](https://www.airpair.com/rest/posts/a-well-designed-api-approach)
-----------------
[Index](index.md)