AzureJwtAuth
Easy way for Ruby applications to authenticate to Azure B2C/AD in order to access protected web resources.
Installation
Add this line to your application's Gemfile:
gem 'azure_jwt_auth'
And then execute:
$ bundle
Or install it yourself as:
$ gem install azure_jwt_auth
Usage with Rails
First of all, we add our providers into an initializer:
# config/initializers/azure.rb
require 'azure_jwt_auth/jwt_manager'
AzureJwtAuth::JwtManager.load_provider(
:b2c,
'https://login.microsoftonline.com/.../v2.0/.well-known/openid-configuration')
)
AzureJwtAuth::JwtManager.load_provider(
:ad,
'https://sts.windows.net/.../v2.0/.well-known/openid-configuration'
)
...
Then, we add Authenticable
module into ApplicationController
and define entity_from_token_payload
method.
This method is used by Authenticable
module to load current_user
.
require 'azure_jwt_auth/authenticable'
class ApplicationController < ActionController::API
include AzureJwtAuth::Authenticable
rescue_from AzureJwtAuth::NotAuthorized, with: :render_401
private
def render_401
render json: {}, status: 401
end
def entity_from_token_payload(payload)
# Returns a valid entity, `nil` or raise
# e.g.
# User.find payload['sub']
end
end
Finally, we can use authenticate!
method into ours controllers:
class ExampleController < ApplicationController
before_action :authenticate!
...
end
Providers
Provider class initializer receives the following parameters:
parameter | description |
---|---|
uid | unique provider identifier |
config_url | azure url to get config |
validations | payload fields validations which will be checked for each token: {payload_field: value_expected, ...} (optional) |
We create providers using the AzureJwtAuth::JwtManager.load_provider
method:
AzureJwtAuth::JwtManager.load_provider(
:b2c, # uid
'https://login.microsoftonline.com/.../v2.0/.well-known/openid-configuration'), # config_url
{'aud' => 'my_app_id'} # validations
)
Authenticable
This module provides us with the following methods:
-
authenticate!
Check if a token is valid for any provider and loads
current_user
. Otherwise it throws an exception.If you need other behavior you can define your custom authenticate! method like this:
def my_authenticate! begin token = JwtManager.new(request, :privider_id) unauthorize! unless token.valid? rescue unauthorize! end @current_user = User.find(token.payload['sub']) end
-
current_user
Returns current_user loaded by
authenticate!
method. -
signed_in?
Check if exists current_user.
-
unauthorize!
Throws a
AzureJwtAuth::NotAuthorized
exception.
Testing (rspec)
Require the AzureJwtAuth::Spec::Helpers helper module in rails_helper.rb
.
require 'azure_jwt_auth/spec/helpers'
...
RSpec.configure do |config|
...
config.include AzureJwtAuth::Spec::Helpers, :type => :controller
end
And then we can just call sign_in(user)
:
describe ExampleController
let(:user) { MyEntity.create(...) }
it "blocks unauthenticated access" do
get :index
expect(response).to have_http_status(401)
end
it "allows authenticated access" do
sign_in user # user will be returned by current_user method
get :index
expect(response).to have_http_status(200)
end
end
License
The gem is available as open source under the terms of the MIT License.