Omniauth::MicrosoftBusinessCentral
This is an OmniAuth strategy for authenticating to Microsoft Dynamics 365 Business Central through the Microsoft identity platform (Entra ID, formerly Azure AD). It obtains an access token (and refresh token) for the Business Central API on behalf of the signed-in user.
Requires Ruby >= 3.2, OmniAuth 2.x and omniauth-oauth2 1.9.x.
Installation
Add this line to your application's Gemfile:
gem 'omniauth-microsoft_business_central'
And then execute:
$ bundle
Or install it yourself as:
$ gem install omniauth-microsoft_business_central
Usage
OmniAuth 2 only accepts POST for the request phase. In a Rails app, add
omniauth-rails_csrf_protection
to your Gemfile and link to the provider with button_to or link_to ..., method: :post:
gem 'omniauth-rails_csrf_protection'
Rails.application.config.middleware.use OmniAuth::Builder do
provider :microsoft_business_central,
ENV['AZURE_APPLICATION_CLIENT_ID'],
ENV['AZURE_APPLICATION_CLIENT_SECRET']
end
The auth hash carries the tokens under credentials and the user's identity —
decoded from the Entra ID token claims — under uid/info:
auth = request.env['omniauth.auth']
auth['credentials']['token'] # Business Central access token
auth['credentials']['refresh_token'] # present because of the default offline_access scope
auth['uid'] # the Entra object id (oid claim)
auth['info']['name']
auth['info']['email']
auth['extra']['tenant_id'] # the Entra tenant the user signed in from
auth['extra']['claims'] # all decoded token claims
Configuring
Scopes
The default scope is:
https://api.businesscentral.dynamics.com/user_impersonation offline_access
user_impersonation grants delegated access to the Business Central API, and
offline_access makes Microsoft return a refresh token. Bare scope names are
qualified against https://api.businesscentral.dynamics.com/; OpenID Connect scopes
(openid, email, profile, offline_access) and absolute URLs pass through
untouched:
provider :microsoft_business_central, client_id, client_secret,
:scope => 'user_impersonation offline_access openid email profile'
Requesting openid makes Microsoft return an id_token, which the strategy then
prefers over the access token when populating uid/info.
Restricting sign-in to one tenant
tenant_id defaults to common, which lets users from any Entra tenant sign in
(what a multi-tenant app registration needs). Set it to a tenant GUID or domain to
pin the strategy to a single directory:
provider :microsoft_business_central, client_id, client_secret,
:tenant_id => 'contoso.onmicrosoft.com'
Per-request authorize params
login_hint, domain_hint, prompt, response_mode and scope may also be
supplied per request and are passed through to Microsoft's authorize endpoint:
/auth/microsoft_business_central?login_hint=user@example.com
(With OmniAuth 2 the request phase is a POST; the params can be in the query
string or the form body.)
Configure the callback URL
The strategy overrides callback_url to drop the query string that OmniAuth would
otherwise append, because the Microsoft identity platform rejects a redirect_uri
that does not match the registered one exactly. Pass callback_url to override it
outright:
:callback_url => 'https://app.example.com/users/auth/microsoft_business_central/callback'
Otherwise it is derived from the request, including the mount prefix — a strategy
mounted under Devise's /users produces
https://app.example.com/users/auth/microsoft_business_central/callback.
Client credentials in the token request
client_options[:auth_scheme] is pinned to :request_body, because the Microsoft
identity platform expects client_id and client_secret in the token request body
(client_secret_post). This was oauth2 1.x's default; oauth2 2.x defaults to
:basic_auth.
Migrating from the dropstream fork of omniauth-microsoft_graph
This gem replaces dropstream/omniauth-microsoft_graph,
a fork that pointed the omniauth-microsoft_graph gem's strategy at Business
Central while keeping the Graph name. What changes:
- The provider is
:microsoft_business_central, so routes move from/auth/microsoft_graphto/auth/microsoft_business_central. Update the redirect URI on the Azure app registration to the new callback path. - Requires OmniAuth 2.x: the request phase is
POST-only, and Rails apps needomniauth-rails_csrf_protection. - The auth hash now has
uid,infoandextra['claims'], decoded from the token claims.extra['params'](the raw token response) is still there. - The Graph-era
verify_token/access_token-in-params flows and the JSON-body code path were dropped; the strategy is a plain OAuth2 authorization-code flow.
Development
After checking out the repo, run bin/setup to install dependencies. Then, run
bundle exec rake spec to run the tests. You can also run bin/console for an
interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install.
Run bundle exec bundle-audit check --update to check the locked dependency tree
against the advisory database; CI runs the same check weekly.
Releasing
Releases are cut from the GitHub Releases UI;
GitHub Actions builds and publishes the gem to rubygems.org via
RubyGems trusted publishing, so no API
key is stored in the repo and the gemspec can keep rubygems_mfa_required.
- Bump
VERSIONinlib/omniauth-microsoft_business_central/version.rband add aCHANGELOG.mdsection for it, along with a link definition for the new heading at the bottom of that file. Commit both onmain. - Draft a new release with the target set to
mainand the tag set to the version prefixed withv—v0.1.0, not0.1.0. The prefix is stripped before comparing againstversion.rb; the gem version itself carries nov. - Click Generate release notes — the preferred method — then publish. The generated
notes list the merged pull requests;
CHANGELOG.mdcarries the curated prose and links back to each release.
Publishing creates the tag, which fires the Release workflow: it reruns the full test
matrix, refuses to continue if the tag and version.rb disagree, and pushes the gem.
Note that RubyGems versions are immutable — a bad release can be yanked, never replaced,
so the version bump has to be committed before the release is published.
One-time RubyGems setup
Needed before the first release from CI, and again only if the gem is renamed or moved.
Because the gem does not exist on rubygems.org yet, register a
pending trusted publisher:
on rubygems.org open your profile -> Trusted publishers -> Create, with gem name
omniauth-microsoft_business_central, owner dropstream, repository
omniauth-microsoft_business_central, workflow release.yml, and environment release.
Pending publishers expire after 12 hours, so do this shortly before publishing the first
release. The repo also needs an environment named release.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/dropstream/omniauth-microsoft_business_central.
License
Available as open source under the terms of the MIT License. Portions derived from omniauth-microsoft_graph (MIT).