Previous slide Next slide Toggle fullscreen Open presenter view
Backend Authentication ด้วย Ruby on Rails
26 เมษายน 2567
Overview
Ruby on Rails
Authentication and Authorization
HTTP Basic Access Authenentication
Username with Secured Password
has_secured_password
Session
Cookie
Token
JWT Token
One Time Password
Authorization with OAuth
Ruby on Rails
MVC Web Framework
Convention over Configuration
user.rb
users/index.html
users/show.html
users_controller.rb
user_service.rb
HTTP Basic Access Authentication
RFC-7235
A HTTP/1.1 authentication flow that use Message Syntax and Routing from RFC-7230
, including the general framework previously described in RFC-2617
and the related fields and status codes previously defined in RFC-2616
.
HTTP Basic Access Authentication
sequenceDiagram
participant Client as Browser
participant Server
Client->>Server: Request to restricted URL
Server-->>Client: 401 Unauthorized with header WWW-Authenticate: Basic
Client->>Client: Pop-up login prompt
Client->>Server: Request with Authorization: Basic BASE64_ENCODED_STRING
Server-->>Client: Response
Client->>Server: Subsequent requests with Authorization: Basic BASE64_ENCODED_STRING
HTTP Basic Access Authentication
Encode Username and Password in Base64 encoded string then include that in
every HTTP requests.
HTTP Basic Access Authentication
HTTP Basic Access Authentication
https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Basic.html
class PostsController < ApplicationController
http_basic_authenticate_with name: "admin" ,
password: "password" ,
only: :edit
def index
render plain: "Everyone can see list of Posts"
end
def show
render plain: "Everyone can see Post's information"
end
def edit
render plain: "Only people who know the password can edit"
end
end
Username with Secured Password
class User < ActiveRecord::Base
has_secure_password
has_secure_password :recovery_password , validations: false
end
Username with Secured Password
ActiveModel::SecurePassword
user = User .new(name: "david" , password: "" , password_confirmation: "nomatch" )
user.save
user.password = "vr00m"
user.save
user.password_confirmation = "vr00m"
user.save
user.authenticate("notright" )
user.authenticate("vr00m" )
User .find_by(name: "david" )&.authenticate("notright" )
User .find_by(name: "david" )&.authenticate("vr00m" )
user.recovery_password = "42password"
user.recovery_password_digest
user.save
user.authenticate_recovery_password("42password" )
user.update(password: "pwn3d" , password_challenge: "" )
user.update(password: "nohack4u" , password_challenge: "vr00m" )
user.authenticate("vr00m" )
user.authenticate("nohack4u" )
Guest Account
class Account
include ActiveModel::SecurePassword
attr_accessor :is_guest , :password_digest
has_secure_password
def errors
super .tap { |errors | errors.delete(:password , :blank ) if is_guest }
end
end
account = Account .new
account.valid?
account.is_guest = true
account.valid?
Session
sequenceDiagram
participant Client
participant Server
Client->>Server: Authenticate with Server
Server->>Server: Generate session ID
Server->>Server: Keep information in session storage
Server-->>Client: Response with session ID
Client->>Server: Request with session ID
Server->>Server: Retrieve data in session storage
Server-->>Client: Response with requested data
Session
def create
session[:current_user_id ] = @user .id
end
def index
current_user = User .find_by_id(session[:current_user_id ])
posts = current_user.posts
end
Cookie
sequenceDiagram
participant Client
participant Server
Client->>Server: Authenticate with Server
Server->>Server: Generate session ID
Server->>Server: Encrypt session data
Server->>Server: Set session ID as cookie
Server-->>Client: Response with cookie
Client->>Server: Request with cookie
Server->>Server: Decrypt session data in cookie
Server-->>Client: Response with requested data
Cookie
cookies[:user_name ] = "david"
cookies[:lat_lon ] = JSON .generate([47.68 , -122.37 ])
cookies[:login ] = { value: "XJ-122" , expires: 1 .hour }
cookies[:login ] = { value: "XJ-122" , expires: Time .utc(2020 , 10 , 15 , 5 ) }
cookies.signed[:user_id ] = current_user.id
cookies.encrypted[:discount ] = 45
cookies.permanent[:login ] = "XJ-122"
cookies.signed.permanent[:login ] = "XJ-122"
JWT
JWT signatures are created using cryptographic algorithms, such as HMAC (Hash-based Message Authentication Code) or RSA (Rivest-Shamir-Adleman).
For HMAC:
A secret key is used to generate the signature.
The signature is a hash (e.g., SHA256) of the header and payload, encoded using the secret key.
For RSA:
A public/private key pair is used.
The signature is created using the private key and verified using the public key.
The signature ensures the integrity of the JWT and prevents tampering.
JWT Token
sequenceDiagram
participant Client
participant Server
Client->>Server: Request with JWT
Server->>Server: Decode JWT
Server->>Server: Verify JWT signature
Server->>Server: Extract payload
Server-->>Client: Response with requested data
JWT Token
gem 'jwt'
JWT_SECRET = 'your_secret_key_here'
JWT Token
class ApplicationController < ActionController::Base
before_action :authenticate_user!
def authenticate_user!
token = request.headers['Authorization' ]&.split(' ' )&.last
begin
payload = JWT .decode(token, JWT_SECRET , true , algorithm: 'HS256' )
@current_user_id = payload[0 ]['user_id' ]
rescue JWT : :DecodeError
render json: { error: 'Invalid token' }, status: :unauthorized
end
end
def current_user
@current_user | |= User .find(@current_user_id )
end
end
JWT Token
class UsersController < ApplicationController
def show
user = current_user
render json: { user: user }
end
end
class UsersController < ApplicationController
def index
posts = current_user.posts
render json: { posts: posts }
end
end
One Time Password
sequenceDiagram
participant User
participant Server
User->>Server: Request OTP
Server->>Server: Generate OTP and store in database
Server-->>User: Send OTP
User->>Server: Provide OTP
Server->>Server: Verify OTP
Server-->>User: Response
One Time Password
class User < ActiveRecord::Base
has_secure_password
generates_token_for :password_reset , expires_in: 15 .minutes do
password_salt&.last(10 )
end
end
user = User .first
token = user.generate_token_for(:password_reset )
User .find_by_token_for(:password_reset , token)
User .find_by_token_for(:password_reset , token)
token = user.generate_token_for(:password_reset )
User .find_by_token_for(:password_reset , token)
user.update!(password: "new password" )
User .find_by_token_for(:password_reset , token)
Authorization with OAuth
sequenceDiagram
participant User
participant Client
participant Authorization_Server
participant Resource_Server
User->>Client: Clicks "Login with OAuth"
Client->>Authorization_Server: Redirect to Authorization Endpoint
Authorization_Server->>User: Login Page
User->>Authorization_Server: Enters Credentials
Authorization_Server->>Client: Authorization Code
Client->>Authorization_Server: Requests Access Token
Authorization_Server->>Client: Sends Access Token
Client->>Resource_Server: Requests Protected Resource
Resource_Server->>Client: Sends Protected Resource
Refresh Token
sequenceDiagram
participant Client
participant Authorization_Server
Client->>Authorization_Server: Request Access Token with Refresh Token
Authorization_Server->>Client: Sends new Access Token
Overview of agenda.
Not much to cover and have to cut out many interesting things
to fit in the timebox.
How Rails stands out of other web frameworks.
A progenitor of modern full-stack web.
Talk about the approach that we will see when showing
files from Rails.
What is Authentication ?
Examples of use cases.
What is Authorization ?
Examples of use cases.
A Basic authentication that oldies will know but
probably doesn't know the definition.
Kind of useless knowledge but might be handy when we want
to have solid references.
Workflow:
Looks at the Header.
A simplfied version of earlier difinition.
Oldies will recognized this.
After being lectured on definition and workflow.
Revealing the implementation to be just One-line.
Looks at how we can have fined-grain control over different methods.
After being lectured on definition and workflow.
Revealing the implementation to be just One-line.
Looks at how we can have fined-grain control over different methods.