Creazione di un backend API - PARTE 2 -
Sicurezza API
Per mettere al sicuro le nostre API dobbiamo usare il cosidetto framework AAA che sta per :
- Authentication : sistema di autenticazione tramite username e password
- Authorization : sistema che controlla del “chi deve fare che cosa”, quindi accesso alle risorse definite
- Accounting : sistema di monitoraggio delle risorse consumate
Questo schema viene identificato come AAA (detto triple A) ed è il framework standard di sicurezza adottato come base per rendere sicure le applicazioni.
A questo framework possono essere aggiunti layer extra come 2FA Dual Factor Authentication. Oppure possiamo aggiungere un livello di Auditing che prevede sistemi di log avanzati ,notifiche push, reporting e metriche varie con business intelligence.
Nella creazione delle API come per un’applicazione tradizionale serve un login per autenticare chi deve accedere al sistema e consumare nel nostro caso le API create.
Aggiungiamo Authentication
Aprimiamo il file Gemfile e aggiungiamo le seguenti gemme:
gem 'devise'
gem 'devise-api', github: 'nejdetkadir/devise-api', branch: 'main'
salviamo e usciamo.
Dalla linea di comando:
$ bundle install
Configuriamo la parte di device come guida ufficiale: Devise gem
$ rails generate devise:install
Per la versione di sviluppo (development) modifichiamo il file config/environments/development.rb aggiungendo:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
Anche se per simulare il flusso di invio e ricezione posta elettronica consigliamo di installare : mailcatcher
Questo tool configurato opportunamente permette la simulazione completa di devise senza dover comprare servizi cloud di posta. MAILCATCHER sarà argomento di un’altra guida. Cerca l’argomento nel nostro motore di ricerca.
Invece per l’ambiente di produzione :
config.action_mailer.default_url_options = { host: 'www.miosito.com' }
Ovviamente ci saranno altri parametri da inserire per completare la configurazione SMTP e far funzionare il tutto correttamente.
Ora creiamo un model per la gestione degli utenti : User
$ rails generate devise User
Verrà creato una struttura stardard di model e una tabella sul database, ma ovviamente questo model potrebbe essere già esistente, allora devise aggiungerà solo i campi necessarie senza perdita dei dati presenti.
$ rails db:migrate
Ora configuriamo devise-api: Vedi sito ufficiale
Eseguiamo:
$ rails generate devise_api:install
$ rails db:migrate
Aprite il file app/model/user.rb e aggiungete/modificate come segue:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:api
end
Salvate e chiudete
Aprite il file config/initializers/devise.rb e aggiungete questo pezzo di codice :
# config/initializers/devise.rb
config.api.configure do |api|
# Access Token
api.access_token.expires_in = 1.hour
api.access_token.expires_in_infinite = ->(_resource_owner) { false }
api.access_token.generator = ->(_resource_owner) { Devise.friendly_token(60) }
# Refresh Token
api.refresh_token.enabled = true
api.refresh_token.expires_in = 1.week
api.refresh_token.generator = ->(_resource_owner) { Devise.friendly_token(60) }
api.refresh_token.expires_in_infinite = ->(_resource_owner) { false }
# Authorization
api.authorization.key = 'Authorization'
api.authorization.scheme = 'Bearer'
api.authorization.location = :both # :header or :params or :both
api.authorization.params_key = 'access_token'
# Base classes
api.base_token_model = 'Devise::Api::Token'
api.base_controller = '::DeviseController'
# After successful callbacks
api.after_successful_sign_in = ->(_resource_owner, _token, _request) { }
api.after_successful_sign_up = ->(_resource_owner, _token, _request) { }
api.after_successful_refresh = ->(_resource_owner, _token, _request) { }
api.after_successful_revoke = ->(_resource_owner, _token, _request) { }
# Before callbacks
api.before_sign_in = ->(_params, _request, _resource_class) { }
api.before_sign_up = ->(_params, _request, _resource_class) { }
api.before_refresh = ->(_params, _request, _resource_class) { }
api.before_revoke = ->(_params, _request, _resource_class) { }
end
Qui trovate alcuni parametri standard che potete anche personalizzare come volete esempio:
- api.access_token.expires_in
- api.authorization.scheme
- api.authorization.params_key
Pensiamo che sia abbastanza esplicativo il nome delle variabili, in caso contrario consigliamo di leggere la seguente guida ufficiale HTTP HEADERS
Ora modifichiamo il nostro ApplicationController come segue:
class ApplicationController < ActionController::API
skip_before_action :verify_authenticity_token, raise: false
before_action :authenticate_devise_api_token!
end
Ricordiamo che questa guida è per un backend API puro, quindi non sono ancora previsti accessi via broswer web e/o dashboard di controllo, ecco perchè questo controller è così bello pulito ;)
Controlliamo ora il nostro file routes.rb e modifichiamolo come segue:
Rails.application.routes.draw do
devise_for :users
mount Rswag::Ui::Engine => '/api-docs'
mount Rswag::Api::Engine => '/api-docs'
resources :posts
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
root "posts#index"
end
Controllate bene che sia come il codice riportato precedentemente! Note:
Per questa app di test abbiamo rendirizzato la home page del sito direttamente sulla pagina index dei posts:
root "posts#index"
Eseguiamo il test di funzionamento
Avviamo il server rails :
$ bundle exec rails server
Creo un utente via rails console ma può essere fatto anche via API (lascio a voi la prova)
$ rails console
> User.create(email:"admin@test.local", password:"1234567890", password_confirmation:"1234567890")
Apriamo un terminale qualsiasi e eseguiamo i test con il comando curl
PS: Se non avete curl installato sul vostro sistema, allora installatelo.
-
Eseguo il login:
curl –location –request POST ‘http://127.0.0.1:3000/users/tokens/sign_in’
–header ‘Content-Type: application/json’
–data-raw ‘{ “email”: “admin@test.local”, “password”: “1234567890” }’ -
Info sul token
curl –location –request GET ‘http://127.0.0.1:3000/users/tokens/info’
–header ‘Authorization: Bearer [copiare il token dal comando precedente]’ # esempio: curl –location –request GET ‘http://127.0.0.1:3000/users/tokens/info’
–header ‘Authorization: Bearer sa6vuL-yLQsoJyihvszvzHs8mc7eYRPv3S_BWyeT1ce-_vsYUEUjqRe73Dk3’ -
Creo un nuovo Post
curl –location –request POST ‘http://127.0.0.1:3000/posts’
–header ‘Authorization: Bearer [copia token comando precendente]’
-H “Content-Type: application/json”
–data-raw ‘{ “post”: { “title”: “acme”, “body”: “123 Carrot Street” } }’ - Update Post
-
prima prelevo un post id valido:
curl --location --request GET 'http://127.0.0.1:3000/posts' \ --header 'Authorization: Bearer sa6vuL-yLQsoJyihvszvzHs8mc7eYRPv3S_BWyeT1ce-_vsYUEUjqRe73Dk3'
-
aggiorno il post id scelto esempio: 3
curl --location --request PUT 'http://127.0.0.1:3000/posts/3' \ --header 'Authorization: Bearer [copia token id]' \ -H "Content-Type: application/json" \ --data-raw '{ "post": { "title": "acme2", "body": "No street included" } }'
controllare che il record nel DB sia stato modificato.
-
-
Elimino il post con id=3
curl --location --request DELETE 'http://127.0.0.1:3000/posts/3' \ --header 'Authorization: Bearer [copia il token]'
-
Lista i post rimasti:
curl --location --request GET 'http://127.0.0.1:3000/posts' \ --header 'Authorization: Bearer sa6vuL-yLQsoJyihvszvzHs8mc7eYRPv3S_BWyeT1ce-_vsYUEUjqRe73Dk3'
Conclusione
Adesso dovreste avere tutti gli strumenti per creare un BACKEND-API di test mangari per un app mobile.
Argomenti aggiuntivi:
- strutturare le api per un versioning di livello enterprise
- sistema di autorizazzione
- pannello di controllo per gli administrator
- 2FA sistema dual factor