omniauth-microsoft_business_central 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +55 -0
- data/LICENSE.txt +25 -0
- data/README.md +195 -0
- data/lib/omniauth/strategies/microsoft_business_central.rb +117 -0
- data/lib/omniauth-microsoft_business_central/version.rb +7 -0
- data/lib/omniauth-microsoft_business_central.rb +4 -0
- data/omniauth-microsoft_business_central.gemspec +39 -0
- metadata +82 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a38f823b02c38e3b8db8913c56926d15a3cade2183884e2d922c29d1fb814764
|
|
4
|
+
data.tar.gz: b0c0a1dfea3a7a14d262537a0a4809d656abd7dce164a0116ae9b0416c19f78c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e18b93493ca79fd49738448e6b245c7a40a6b51b4dc1a8e501c5615465a111c30dd78bece29e255f0a10619751ee37d3353800e25aa4641ccee05bc07dbb1cd3
|
|
7
|
+
data.tar.gz: e5eec82f7a78d05b8edaa6cdc4eb7b2d671ee375adbbc0022e1bae1fc920b4c614d667142a7c6007d045e0d727bc7b4fa17aa03c0057574c0b1941e65766c100
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-08-27
|
|
6
|
+
|
|
7
|
+
Initial release. Replaces the [dropstream fork of omniauth-microsoft_graph](https://github.com/dropstream/omniauth-microsoft_graph),
|
|
8
|
+
which had repointed the `omniauth-microsoft_graph` strategy at Business Central while
|
|
9
|
+
keeping the Graph gem name, module names and provider key.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `OmniAuth::Strategies::MicrosoftBusinessCentral`, registered as
|
|
14
|
+
`:microsoft_business_central` (callback path
|
|
15
|
+
`/auth/microsoft_business_central/callback`).
|
|
16
|
+
- A `tenant_id` option (default `common`) that derives the Microsoft identity
|
|
17
|
+
platform v2.0 authorize/token endpoints; explicit `client_options` URLs still win.
|
|
18
|
+
- `uid`, `info` (name/email) and `extra` (`tenant_id`, `claims`), decoded from the
|
|
19
|
+
`id_token` when an OpenID scope was granted, otherwise from the access token JWT.
|
|
20
|
+
- Scope normalization carried over from the fork: bare scope names are qualified
|
|
21
|
+
against `https://api.businesscentral.dynamics.com/`, OpenID scopes and absolute
|
|
22
|
+
URLs pass through, comma- and space-separated lists are accepted. The default
|
|
23
|
+
scope remains `https://api.businesscentral.dynamics.com/user_impersonation offline_access`.
|
|
24
|
+
- Per-request passthrough of the whitelisted authorize options (`scope`, `prompt`,
|
|
25
|
+
`login_hint`, `domain_hint`, `response_mode`).
|
|
26
|
+
- A `callback_url` override that drops the callback's query string (the Microsoft
|
|
27
|
+
identity platform requires an exact `redirect_uri` match) and honors a configured
|
|
28
|
+
`:callback_url`.
|
|
29
|
+
- Integration specs covering the request phase, tenant selection, scope
|
|
30
|
+
normalization, the token exchange, and the auth hash; CI on Ruby 3.2–3.4 with a
|
|
31
|
+
weekly `bundle-audit`, and releases via the GitHub Releases UI with RubyGems
|
|
32
|
+
trusted publishing (OIDC, no stored API key).
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **Breaking (relative to the fork):** requires `omniauth ~> 2.0` and
|
|
37
|
+
`omniauth-oauth2 ~> 1.9` (the fork pinned `omniauth ~> 1.1`, which carries
|
|
38
|
+
CVE-2015-9284). Host applications must upgrade to OmniAuth 2.x; Rails apps also
|
|
39
|
+
need [`omniauth-rails_csrf_protection`](https://github.com/cookpad/omniauth-rails_csrf_protection)
|
|
40
|
+
because OmniAuth 2 only accepts `POST` for the request phase.
|
|
41
|
+
- Pinned the token endpoint to `auth_scheme: :request_body` (`client_secret_post`),
|
|
42
|
+
matching what the Microsoft identity platform expects; `oauth2` 2.x would default
|
|
43
|
+
to HTTP basic auth.
|
|
44
|
+
- Requires Ruby >= 3.2.
|
|
45
|
+
|
|
46
|
+
### Removed
|
|
47
|
+
|
|
48
|
+
- The Graph-era code inherited via omniauth-google-oauth2: the custom
|
|
49
|
+
`build_access_token` (AJAX/JSON-body code paths) and `verify_token`, which called
|
|
50
|
+
the Microsoft Graph `/me` endpoint and could not work with a Business Central
|
|
51
|
+
token. The strategy is now a plain OAuth2 authorization-code flow.
|
|
52
|
+
- The Google-specific `access_type`, `display`, `score` and `auth_type` authorize
|
|
53
|
+
options, and the ability to override `state` or `redirect_uri` from request params.
|
|
54
|
+
|
|
55
|
+
[0.1.0]: https://github.com/dropstream/omniauth-microsoft_business_central/releases/tag/v0.1.0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dropstream
|
|
4
|
+
|
|
5
|
+
Portions derived from omniauth-microsoft_graph
|
|
6
|
+
(https://github.com/synth/omniauth-microsoft_graph),
|
|
7
|
+
Copyright (c) 2014 Josef Šimánek and contributors.
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in
|
|
17
|
+
all copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
25
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Omniauth::MicrosoftBusinessCentral
|
|
2
|
+
|
|
3
|
+
This is an OmniAuth strategy for authenticating to
|
|
4
|
+
[Microsoft Dynamics 365 Business Central](https://learn.microsoft.com/en-us/dynamics365/business-central/)
|
|
5
|
+
through the Microsoft identity platform (Entra ID, formerly Azure AD). It obtains an
|
|
6
|
+
access token (and refresh token) for the Business Central API on behalf of the
|
|
7
|
+
signed-in user.
|
|
8
|
+
|
|
9
|
+
Requires Ruby >= 3.2, OmniAuth 2.x and `omniauth-oauth2` 1.9.x.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add this line to your application's Gemfile:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
gem 'omniauth-microsoft_business_central'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
And then execute:
|
|
20
|
+
|
|
21
|
+
$ bundle
|
|
22
|
+
|
|
23
|
+
Or install it yourself as:
|
|
24
|
+
|
|
25
|
+
$ gem install omniauth-microsoft_business_central
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
OmniAuth 2 only accepts `POST` for the request phase. In a Rails app, add
|
|
30
|
+
[`omniauth-rails_csrf_protection`](https://github.com/cookpad/omniauth-rails_csrf_protection)
|
|
31
|
+
to your Gemfile and link to the provider with `button_to` or `link_to ..., method: :post`:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
gem 'omniauth-rails_csrf_protection'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
|
39
|
+
provider :microsoft_business_central,
|
|
40
|
+
ENV['AZURE_APPLICATION_CLIENT_ID'],
|
|
41
|
+
ENV['AZURE_APPLICATION_CLIENT_SECRET']
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The auth hash carries the tokens under `credentials` and the user's identity —
|
|
46
|
+
decoded from the Entra ID token claims — under `uid`/`info`:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
auth = request.env['omniauth.auth']
|
|
50
|
+
auth['credentials']['token'] # Business Central access token
|
|
51
|
+
auth['credentials']['refresh_token'] # present because of the default offline_access scope
|
|
52
|
+
auth['uid'] # the Entra object id (oid claim)
|
|
53
|
+
auth['info']['name']
|
|
54
|
+
auth['info']['email']
|
|
55
|
+
auth['extra']['tenant_id'] # the Entra tenant the user signed in from
|
|
56
|
+
auth['extra']['claims'] # all decoded token claims
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuring
|
|
60
|
+
|
|
61
|
+
### Scopes
|
|
62
|
+
|
|
63
|
+
The default scope is:
|
|
64
|
+
|
|
65
|
+
https://api.businesscentral.dynamics.com/user_impersonation offline_access
|
|
66
|
+
|
|
67
|
+
`user_impersonation` grants delegated access to the Business Central API, and
|
|
68
|
+
`offline_access` makes Microsoft return a refresh token. Bare scope names are
|
|
69
|
+
qualified against `https://api.businesscentral.dynamics.com/`; OpenID Connect scopes
|
|
70
|
+
(`openid`, `email`, `profile`, `offline_access`) and absolute URLs pass through
|
|
71
|
+
untouched:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
provider :microsoft_business_central, client_id, client_secret,
|
|
75
|
+
:scope => 'user_impersonation offline_access openid email profile'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Requesting `openid` makes Microsoft return an `id_token`, which the strategy then
|
|
79
|
+
prefers over the access token when populating `uid`/`info`.
|
|
80
|
+
|
|
81
|
+
### Restricting sign-in to one tenant
|
|
82
|
+
|
|
83
|
+
`tenant_id` defaults to `common`, which lets users from any Entra tenant sign in
|
|
84
|
+
(what a multi-tenant app registration needs). Set it to a tenant GUID or domain to
|
|
85
|
+
pin the strategy to a single directory:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
provider :microsoft_business_central, client_id, client_secret,
|
|
89
|
+
:tenant_id => 'contoso.onmicrosoft.com'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Per-request authorize params
|
|
93
|
+
|
|
94
|
+
`login_hint`, `domain_hint`, `prompt`, `response_mode` and `scope` may also be
|
|
95
|
+
supplied per request and are passed through to Microsoft's authorize endpoint:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
/auth/microsoft_business_central?login_hint=user@example.com
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
(With OmniAuth 2 the request phase is a `POST`; the params can be in the query
|
|
102
|
+
string or the form body.)
|
|
103
|
+
|
|
104
|
+
### Configure the callback URL
|
|
105
|
+
|
|
106
|
+
The strategy overrides `callback_url` to drop the query string that OmniAuth would
|
|
107
|
+
otherwise append, because the Microsoft identity platform rejects a `redirect_uri`
|
|
108
|
+
that does not match the registered one exactly. Pass `callback_url` to override it
|
|
109
|
+
outright:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
:callback_url => 'https://app.example.com/users/auth/microsoft_business_central/callback'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Otherwise it is derived from the request, including the mount prefix — a strategy
|
|
116
|
+
mounted under Devise's `/users` produces
|
|
117
|
+
`https://app.example.com/users/auth/microsoft_business_central/callback`.
|
|
118
|
+
|
|
119
|
+
### Client credentials in the token request
|
|
120
|
+
|
|
121
|
+
`client_options[:auth_scheme]` is pinned to `:request_body`, because the Microsoft
|
|
122
|
+
identity platform expects `client_id` and `client_secret` in the token request body
|
|
123
|
+
(`client_secret_post`). This was `oauth2` 1.x's default; `oauth2` 2.x defaults to
|
|
124
|
+
`:basic_auth`.
|
|
125
|
+
|
|
126
|
+
## Migrating from the dropstream fork of omniauth-microsoft_graph
|
|
127
|
+
|
|
128
|
+
This gem replaces [dropstream/omniauth-microsoft_graph](https://github.com/dropstream/omniauth-microsoft_graph),
|
|
129
|
+
a fork that pointed the `omniauth-microsoft_graph` gem's strategy at Business
|
|
130
|
+
Central while keeping the Graph name. What changes:
|
|
131
|
+
|
|
132
|
+
- The provider is `:microsoft_business_central`, so routes move from
|
|
133
|
+
`/auth/microsoft_graph` to `/auth/microsoft_business_central`. Update the
|
|
134
|
+
redirect URI on the Azure app registration to the new callback path.
|
|
135
|
+
- Requires OmniAuth 2.x: the request phase is `POST`-only, and Rails apps need
|
|
136
|
+
`omniauth-rails_csrf_protection`.
|
|
137
|
+
- The auth hash now has `uid`, `info` and `extra['claims']`, decoded from the
|
|
138
|
+
token claims. `extra['params']` (the raw token response) is still there.
|
|
139
|
+
- The Graph-era `verify_token`/`access_token`-in-params flows and the JSON-body
|
|
140
|
+
code path were dropped; the strategy is a plain OAuth2 authorization-code flow.
|
|
141
|
+
|
|
142
|
+
## Development
|
|
143
|
+
|
|
144
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
145
|
+
`bundle exec rake spec` to run the tests. You can also run `bin/console` for an
|
|
146
|
+
interactive prompt that will allow you to experiment.
|
|
147
|
+
|
|
148
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
149
|
+
|
|
150
|
+
Run `bundle exec bundle-audit check --update` to check the locked dependency tree
|
|
151
|
+
against the advisory database; CI runs the same check weekly.
|
|
152
|
+
|
|
153
|
+
## Releasing
|
|
154
|
+
|
|
155
|
+
Releases are cut from the [GitHub Releases UI](https://github.com/dropstream/omniauth-microsoft_business_central/releases/new);
|
|
156
|
+
GitHub Actions builds and publishes the gem to [rubygems.org](https://rubygems.org) via
|
|
157
|
+
[RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/), so no API
|
|
158
|
+
key is stored in the repo and the gemspec can keep `rubygems_mfa_required`.
|
|
159
|
+
|
|
160
|
+
1. Bump `VERSION` in `lib/omniauth-microsoft_business_central/version.rb` and add a
|
|
161
|
+
`CHANGELOG.md` section for it, along with a link definition for the new heading at
|
|
162
|
+
the bottom of that file. Commit both on `main`.
|
|
163
|
+
2. Draft a new release with the target set to `main` and the tag set to the version
|
|
164
|
+
prefixed with `v` — `v0.1.0`, not `0.1.0`. The prefix is stripped before comparing
|
|
165
|
+
against `version.rb`; the gem version itself carries no `v`.
|
|
166
|
+
3. Click **Generate release notes** — the preferred method — then publish. The generated
|
|
167
|
+
notes list the merged pull requests; `CHANGELOG.md` carries the curated prose and
|
|
168
|
+
links back to each release.
|
|
169
|
+
|
|
170
|
+
Publishing creates the tag, which fires the `Release` workflow: it reruns the full test
|
|
171
|
+
matrix, refuses to continue if the tag and `version.rb` disagree, and pushes the gem.
|
|
172
|
+
Note that RubyGems versions are immutable — a bad release can be yanked, never replaced,
|
|
173
|
+
so the version bump has to be committed before the release is published.
|
|
174
|
+
|
|
175
|
+
### One-time RubyGems setup
|
|
176
|
+
|
|
177
|
+
Needed before the first release from CI, and again only if the gem is renamed or moved.
|
|
178
|
+
Because the gem does not exist on rubygems.org yet, register a
|
|
179
|
+
[pending trusted publisher](https://guides.rubygems.org/trusted-publishing/pushing-a-new-gem/):
|
|
180
|
+
on rubygems.org open your profile -> **Trusted publishers** -> **Create**, with gem name
|
|
181
|
+
`omniauth-microsoft_business_central`, owner `dropstream`, repository
|
|
182
|
+
`omniauth-microsoft_business_central`, workflow `release.yml`, and environment `release`.
|
|
183
|
+
Pending publishers expire after 12 hours, so do this shortly before publishing the first
|
|
184
|
+
release. The repo also needs an environment named `release`.
|
|
185
|
+
|
|
186
|
+
## Contributing
|
|
187
|
+
|
|
188
|
+
Bug reports and pull requests are welcome on GitHub at
|
|
189
|
+
https://github.com/dropstream/omniauth-microsoft_business_central.
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
Available as open source under the terms of the [MIT License](LICENSE.txt). Portions
|
|
194
|
+
derived from [omniauth-microsoft_graph](https://github.com/synth/omniauth-microsoft_graph)
|
|
195
|
+
(MIT).
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'omniauth-oauth2'
|
|
5
|
+
|
|
6
|
+
module OmniAuth
|
|
7
|
+
module Strategies
|
|
8
|
+
class MicrosoftBusinessCentral < OmniAuth::Strategies::OAuth2
|
|
9
|
+
# The Business Central API audience; bare scope names are qualified against it.
|
|
10
|
+
SCOPE_PREFIX = 'https://api.businesscentral.dynamics.com/'
|
|
11
|
+
DEFAULT_SCOPE = "#{SCOPE_PREFIX}user_impersonation offline_access"
|
|
12
|
+
# OpenID Connect scopes that must be passed through unqualified.
|
|
13
|
+
OPENID_SCOPES = %w[openid email profile offline_access].freeze
|
|
14
|
+
|
|
15
|
+
option :name, 'microsoft_business_central'
|
|
16
|
+
|
|
17
|
+
# 'common' lets any Microsoft Entra tenant sign in, which is what a
|
|
18
|
+
# multi-tenant app registration needs. Set it to a tenant id (GUID) or
|
|
19
|
+
# domain to restrict sign-in to a single directory.
|
|
20
|
+
option :tenant_id, 'common'
|
|
21
|
+
|
|
22
|
+
option :client_options, {
|
|
23
|
+
site: 'https://login.microsoftonline.com/',
|
|
24
|
+
# The Microsoft identity platform expects the client credentials in the
|
|
25
|
+
# token request body (client_secret_post); oauth2 2.x defaults to
|
|
26
|
+
# HTTP basic auth.
|
|
27
|
+
auth_scheme: :request_body
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
option :authorize_options, %i[scope prompt login_hint domain_hint response_mode]
|
|
31
|
+
|
|
32
|
+
option :scope, DEFAULT_SCOPE
|
|
33
|
+
|
|
34
|
+
uid { user_claims['oid'] }
|
|
35
|
+
|
|
36
|
+
info do
|
|
37
|
+
{
|
|
38
|
+
name: user_claims['name'],
|
|
39
|
+
email: user_claims['email'] || user_claims['preferred_username'] || user_claims['upn']
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
extra do
|
|
44
|
+
{
|
|
45
|
+
'params' => access_token.params,
|
|
46
|
+
'tenant_id' => user_claims['tid'],
|
|
47
|
+
'claims' => user_claims
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def client
|
|
52
|
+
client_options = deep_symbolize(options.client_options)
|
|
53
|
+
tenant = options.tenant_id || 'common'
|
|
54
|
+
client_options[:authorize_url] ||= "#{tenant}/oauth2/v2.0/authorize"
|
|
55
|
+
client_options[:token_url] ||= "#{tenant}/oauth2/v2.0/token"
|
|
56
|
+
::OAuth2::Client.new(options.client_id, options.client_secret, client_options)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def authorize_params
|
|
60
|
+
super.tap do |params|
|
|
61
|
+
# Any whitelisted authorize option can also be supplied per request:
|
|
62
|
+
# /auth/microsoft_business_central?login_hint=user@example.com
|
|
63
|
+
options[:authorize_options].each do |key|
|
|
64
|
+
value = request.params[key.to_s]
|
|
65
|
+
params[key] = value unless value.nil? || value == ''
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
params[:scope] = normalize_scope(params[:scope] || DEFAULT_SCOPE)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# OmniAuth::Strategy#callback_url appends the callback request's own query
|
|
73
|
+
# string, and the Microsoft identity platform requires redirect_uri to match
|
|
74
|
+
# the registered value exactly. omniauth 2's `callback_path` already carries
|
|
75
|
+
# SCRIPT_NAME, so mount prefixes must not be added again.
|
|
76
|
+
def callback_url
|
|
77
|
+
options[:callback_url] || (full_host + callback_path)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
# Qualifies bare scope names ('user_impersonation') against the Business
|
|
83
|
+
# Central API audience, leaving OpenID scopes and absolute URLs alone.
|
|
84
|
+
# Accepts space- and/or comma-separated lists.
|
|
85
|
+
def normalize_scope(raw_scope)
|
|
86
|
+
raw_scope.split(/[,\s]+/).map do |scope|
|
|
87
|
+
next scope if scope.match?(%r{\Ahttps?://}) || OPENID_SCOPES.include?(scope)
|
|
88
|
+
|
|
89
|
+
"#{SCOPE_PREFIX}#{scope}"
|
|
90
|
+
end.join(' ')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Claims for the signed-in user: from the id_token when an OpenID scope was
|
|
94
|
+
# granted, otherwise from the access token itself (Business Central access
|
|
95
|
+
# tokens are Entra ID JWTs). The payload is read without verifying the
|
|
96
|
+
# signature, which is acceptable only because the token arrives directly
|
|
97
|
+
# from login.microsoftonline.com over TLS, never from the browser.
|
|
98
|
+
def user_claims
|
|
99
|
+
@user_claims ||= decode_jwt_claims(access_token.params['id_token']) ||
|
|
100
|
+
decode_jwt_claims(access_token.token) ||
|
|
101
|
+
{}
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def decode_jwt_claims(jwt)
|
|
105
|
+
payload = jwt.to_s.split('.')[1]
|
|
106
|
+
return nil if payload.nil? || payload.empty?
|
|
107
|
+
|
|
108
|
+
# Base64url-decode by hand; the base64 gem left Ruby's default gems in 3.4.
|
|
109
|
+
json = (payload.tr('-_', '+/') + '=' * (-payload.length % 4)).unpack1('m')
|
|
110
|
+
claims = JSON.parse(json)
|
|
111
|
+
claims.is_a?(Hash) ? claims : nil
|
|
112
|
+
rescue ArgumentError, JSON::ParserError, EncodingError
|
|
113
|
+
nil
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require "omniauth-microsoft_business_central/version"
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = "omniauth-microsoft_business_central"
|
|
9
|
+
spec.version = Omniauth::MicrosoftBusinessCentral::VERSION
|
|
10
|
+
spec.authors = ["Karl Falconer"]
|
|
11
|
+
spec.email = ["karl.falconer@getdropstream.com"]
|
|
12
|
+
|
|
13
|
+
spec.summary = "OmniAuth strategy for Microsoft Dynamics 365 Business Central"
|
|
14
|
+
spec.description = "An OmniAuth OAuth2 strategy that authenticates against the " \
|
|
15
|
+
"Microsoft identity platform (Entra ID) and obtains tokens for " \
|
|
16
|
+
"the Dynamics 365 Business Central API."
|
|
17
|
+
spec.homepage = "https://github.com/dropstream/omniauth-microsoft_business_central"
|
|
18
|
+
spec.license = "MIT"
|
|
19
|
+
|
|
20
|
+
spec.required_ruby_version = ">= 3.2"
|
|
21
|
+
|
|
22
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
23
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
24
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
25
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
26
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
27
|
+
|
|
28
|
+
# Ship only the library itself; development scaffolding stays out of the gem.
|
|
29
|
+
spec.files = Dir.chdir(__dir__) do
|
|
30
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
31
|
+
f.match(%r{\A(?:spec|bin|pkg|\.github)/}) ||
|
|
32
|
+
f.match(%r{\A(?:\.gitignore|\.rspec|\.ruby-version|\.ruby-gemset|Gemfile|Gemfile\.lock|Rakefile)\z})
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
spec.require_paths = ["lib"]
|
|
36
|
+
|
|
37
|
+
spec.add_dependency "omniauth", "~> 2.0"
|
|
38
|
+
spec.add_dependency "omniauth-oauth2", "~> 1.9"
|
|
39
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: omniauth-microsoft_business_central
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Karl Falconer
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: omniauth
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: omniauth-oauth2
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.9'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.9'
|
|
40
|
+
description: An OmniAuth OAuth2 strategy that authenticates against the Microsoft
|
|
41
|
+
identity platform (Entra ID) and obtains tokens for the Dynamics 365 Business Central
|
|
42
|
+
API.
|
|
43
|
+
email:
|
|
44
|
+
- karl.falconer@getdropstream.com
|
|
45
|
+
executables: []
|
|
46
|
+
extensions: []
|
|
47
|
+
extra_rdoc_files: []
|
|
48
|
+
files:
|
|
49
|
+
- CHANGELOG.md
|
|
50
|
+
- LICENSE.txt
|
|
51
|
+
- README.md
|
|
52
|
+
- lib/omniauth-microsoft_business_central.rb
|
|
53
|
+
- lib/omniauth-microsoft_business_central/version.rb
|
|
54
|
+
- lib/omniauth/strategies/microsoft_business_central.rb
|
|
55
|
+
- omniauth-microsoft_business_central.gemspec
|
|
56
|
+
homepage: https://github.com/dropstream/omniauth-microsoft_business_central
|
|
57
|
+
licenses:
|
|
58
|
+
- MIT
|
|
59
|
+
metadata:
|
|
60
|
+
allowed_push_host: https://rubygems.org
|
|
61
|
+
homepage_uri: https://github.com/dropstream/omniauth-microsoft_business_central
|
|
62
|
+
source_code_uri: https://github.com/dropstream/omniauth-microsoft_business_central
|
|
63
|
+
changelog_uri: https://github.com/dropstream/omniauth-microsoft_business_central/blob/main/CHANGELOG.md
|
|
64
|
+
rubygems_mfa_required: 'true'
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '3.2'
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 3.6.9
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: OmniAuth strategy for Microsoft Dynamics 365 Business Central
|
|
82
|
+
test_files: []
|