Let's implement OAuth 2.0 for fun, learning, and appreciation of the tech
vlad@mixbook.com
Depends only on HTTP transport
Designed to fit well into distributed systems
Every respectable service implements it
Client Application
Authorization Provider
Resource Server
Resource Owner
Client
Application
Authorization
Provider
Resource
Server
Resource
Owner
$ history | grep "token"
Paiza – a tablet carried by Mongol officials and envoys to signify certain privileges and authority
Authorization != Authentication
Grant/deny access for a resource to a user
OAuth is an authorization protocol
Verify the identity of a user
OpenID Connect is an authentication protocol based on OAuth
Server hosting the protected resources. This is the API you want to access.
RSpec.describe 'Resource Server', type: :request do
specify 'resource is protected'
context 'when bearer token is supplied' do
specify 'resource is available'
context 'when token expires' do
specify 'resource is not available'
end
context "when scope doesn't match" do
specify 'resource is not available'
end
end
end
RSpec.describe 'Resource Server', type: :request do
let(:headers) { Hash.new }
before { get '/housing', headers: headers }
it 'is protected' do
expect(response).to have_http_status(:bad_request)
end
context "when bearer token is supplied" do
let(:token) { create(:access_token, scope: 'housing') }
let(:headers) { { 'Authorization' => "Bearer #{token.value}" } }
specify 'resource is available' do
expect(response).to have_http_status(:ok)
end
context 'when token expires' do
let(:token) do
create(:access_token, expires_at: 1.second.ago, scope: 'housing')
end
specify 'resource is not available' do
expect(response).to have_http_status(:forbidden)
end
end
context "when scope doesn't match" do
let(:token) { create(:access_token, scope: 'food') }
specify 'resource is not available' do
expect(response).to have_http_status(:forbidden)
end
end
end
end
class EmpireController < ApplicationController
before_action :authorize!
def housing
head :ok
end
private
def authorize!
token_header = request.headers["Authorization"]
unless token_header.present?
head :bad_request
return
end
token_value = token_header.delete_prefix("Bearer ")
token = AccessToken.find_by(value: token_value)
head :forbidden unless token&.usable?(['housing'])
end
end
class AccessToken < ApplicationRecord
def usable?(claimed_scopes)
scope_list.intersection(claimed_scopes) == claimed_scopes && !expired?
end
def expired?
expires_at < Time.current
end
def scope_list
scope.split(' ')
end
end
RSpec.describe 'Resource Owner', type: :system do
specify do
visit '/'
click_on 'Sign Up'
fill_in 'Email', with: 'kublai_khan@mongolia.mn'
fill_in 'Password', with: 'Tengri'
fill_in 'Password confirmation', with: 'Tengri'
click_on 'Sign up'
expect(page).to have_text('You have signed up successfully.')
click_on 'Sign Out'
expect(page).to have_text('Signed out successfully.')
click_on 'Sign In'
fill_in 'Email', with: 'kublai_khan@mongolia.mn'
fill_in 'Password', with: 'Tengri'
click_on 'Log in'
expect(page).to have_text('Signed in successfully.')
end
end
$ bundle add devise
$ bundle exec rails g devise:install
$ bundle exec rails g devise User
Application requesting access to a protected resource on behalf of the Resource Owner
Client Key is public
Client Secret should only be known by the auth provider and the client app. It can be transported only via Back-End channel i.e. server to server
Mobile apps and some SPAs don't have a Back-End channel
Extension to the Authorization Code flow to prevent CSRF and authorization code injection attacks
+---------------------------------------------------+ | Authz Provider | +----^--+------^-------+----------------------------+ | |Authz | | | |Code |/token |Token /authorize| | | |Grant | | | | | | | | +----+--v------+-------+----------------------------+ | | | | | | Marco Polo Karavan v | | mpolo://oauth/success | | | +---------------------------------------------------+ Mobile Device
+---------------------------------------------------+ | Authz Provider | +----^--+------^-------+--------^-------+-----------+ | |Authz | | | | | |Code |/token |Token |/token | /authorize| | | |Grant | | (PKCE) | | | | | X code_verifier | | | | | | mismatch +----+--v------+-------+--------+-------+-----------+ | | | | | v | | Marco Polo Karavan v Gang of Robbers | | mpolo://oauth/success mpolo://oauth/success | | | +---------------------------------------------------+ Mobile Device
In Code
Coming Soon: Mixbook's 2023 Tech Experts Speaker Series
← Sign up for updates for future events here
Vă mulţumesc pentru atenţie
Спасибо за внимание
Hvala na pažnji
Thank you for your attention