Link Search Menu Expand Document

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

Copyright © 2023 RICSystem - partner DIAGO SRL -