Skip to content

Commit

Permalink
Merge pull request #725 from ShelterTechSF/validate-auth0-token
Browse files Browse the repository at this point in the history
Validate auth0 token
  • Loading branch information
schroerbrian committed Oct 19, 2023
2 parents f170406 + a795ed8 commit 32c56e0
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 0 deletions.
3 changes: 3 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# frozen_string_literal: true

require "jwt"
require "auth0_client"

class ApplicationController < ActionController::API
include Secured

rescue_from ActionController::ParameterMissing do
head :bad_request
end
Expand Down
46 changes: 46 additions & 0 deletions app/controllers/concerns/secured.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

# This concern is included in our ApplicationController.
# It instantiates an Auth0Client, which calls the validate_token method
# to validate Auth tokens included in requests from the front-end.
module Secured
extend ActiveSupport::Concern

REQUIRES_AUTHENTICATION = { message: 'Requires authentication' }.freeze
BAD_CREDENTIALS = { message: 'Bad credentials' }.freeze
MALFORMED_AUTHORIZATION_HEADER = {
error: 'invalid_request',
error_description: 'Authorization header value must follow this format: Bearer access-token',
message: 'Bad credentials'
}.freeze

# The authorize method can be run as a before_action within a controller to validate
# a user's token prior to allowing access to an endpoint.
def authorize
token = token_from_request

return if performed?

validation_response = Auth0Client.validate_token(token)
error = validation_response.error
return unless error

render json: { message: error.message }, status: error.status
end

private

def token_from_request
authorization_header_elements = request.headers['Authorization']&.split

render json: REQUIRES_AUTHENTICATION, status: :unauthorized and return unless authorization_header_elements

render json: MALFORMED_AUTHORIZATION_HEADER, status: :unauthorized and return unless authorization_header_elements.length == 2

scheme, token = authorization_header_elements

render json: BAD_CREDENTIALS, status: :unauthorized and return unless scheme.downcase == 'bearer'

token
end
end
1 change: 1 addition & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class Application < Rails::Application
config.x.auth0.client_id = ENV['AUTH0_CLIENT_ID']
config.x.auth0.client_secret = ENV['AUTH0_CLIENT_SECRET']
config.x.auth0.domain = ENV['AUTH0_DOMAIN']
config.x.auth0.audience = ENV['AUTH0_AUDIENCE']

config.middleware.insert_before 0, Rack::Cors do
allow do
Expand Down
41 changes: 41 additions & 0 deletions lib/auth0_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

require 'jwt'
require 'net/http'

class Auth0Client
Auth_Domain = URI::HTTPS.build(host: Rails.application.config.x.auth0.domain)
Error = Struct.new(:message, :status)
Response = Struct.new(:decoded_token, :error)

def self.decode_token(token, jwks_hash)
JWT.decode(token, nil, true, {
algorithm: 'RS256',
iss: URI.join(Auth_Domain, '/'), # Trailing slash is required to successfully validate token
verify_iss: true,
aud: Rails.application.config.x.auth0.audience,
verify_aud: true,
jwks: { keys: jwks_hash[:keys] }
})
end

def self.fetch_jwks
jwks_uri = URI.join(Auth_Domain, '.well-known/jwks.json')
Net::HTTP.get_response jwks_uri
end

def self.validate_token(token)
jwks_response = fetch_jwks

unless jwks_response.is_a? Net::HTTPSuccess
error = Error.new(message: 'Unable to verify credentials', status: :internal_server_error)
Response.new(nil, error)
end

jwks_hash = JSON.parse(jwks_response.body).deep_symbolize_keys
decoded_token = decode_token(token, jwks_hash)
Response.new(decoded_token, nil)
rescue JWT::DecodeError
Response.new(nil, Error.new('Bad credentials', :unauthorized))
end
end

0 comments on commit 32c56e0

Please sign in to comment.