omniauth-ldap 2.3.1 → 2.3.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6521def9ea4ad73235bbf1c8147822ab3b59e211fdc5f4959dfa2178a82a5bd4
4
- data.tar.gz: 4f6f08af41228c4e0a20f6a957d9927165f1d8109835a4c239abdcc3c6f79f4f
3
+ metadata.gz: 8bcc01a9129d0db019b5530a9bf5f7b5241f9eacba974d5c5585b4620a8d140b
4
+ data.tar.gz: 5eb2878ad0b0d471d60dfb4596baddb077cde9c26499ad1d2c3f661a96f1b242
5
5
  SHA512:
6
- metadata.gz: 51fca6cf72c8331c82ecb35b89d4cca511bd6de423da5c24c659b2de9a0f7c29dd74eca283edeb45672ca0a745a1a970478d42b972f518d68fc9a9dd155fef80
7
- data.tar.gz: f08359c99200c2192abc312a80859d7cd9671061318605b680ef6bf03c14ea667b9b7d288dd4754939e062da7f5de1b7c887d33fa56846b10e6f826172bfbe76
6
+ metadata.gz: f3d489556fa774b9865a3138dcce9f9eef4d2afedbeff2d0bdb5ce239a3b18dd430bcdf2438b77135434e511750a50236fb0e6902ad00ce16e60d1021058fb41
7
+ data.tar.gz: 4d544ca2868b9c0eb8d283dbc996b305bd1ca3d8070c9b5d342dc3de6d8d98206f197926da1bfd9f9b57e03728f8d96932a824b540fce3f4c66efd7e8ae08630
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -32,6 +32,49 @@ Please file a bug if you notice a violation of semantic versioning.
32
32
 
33
33
  ### Security
34
34
 
35
+ ## [2.3.3] - 2025-11-10
36
+
37
+ - TAG: [v2.3.3][2.3.3t]
38
+ - COVERAGE: 97.61% -- 286/293 lines in 4 files
39
+ - BRANCH COVERAGE: 79.69% -- 102/128 branches in 4 files
40
+ - 94.44% documented
41
+
42
+ ### Added
43
+
44
+ - Documentation cleanup & updates
45
+ - YARD documentation covering 94% of the code
46
+
47
+ ### Changed
48
+
49
+ - kettle-dev v1.1.54
50
+
51
+ ## [2.3.2] - 2025-11-06
52
+
53
+ - TAG: [v2.3.2][2.3.2t]
54
+ - COVERAGE: 97.64% -- 290/297 lines in 4 files
55
+ - BRANCH COVERAGE: 79.69% -- 102/128 branches in 4 files
56
+ - 44.12% documented
57
+
58
+ ### Added
59
+
60
+ - Support for SCRIPT_NAME for proper URL generation
61
+ - behind certain proxies/load balancers, or
62
+ - under a subdirectory
63
+ - Password Policy for LDAP Directories
64
+ - password_policy: true|false (default: false)
65
+ - on authentication failure, if the server returns password policy controls, the info will be included in the failure message
66
+ - https://datatracker.ietf.org/doc/html/draft-behera-ldap-password-policy-11
67
+ - Support for JSON bodies
68
+ - Support custom LDAP attributes mapping
69
+ - Documentation of TLS verification options
70
+
71
+ ### Changed
72
+
73
+ - Make support for OmniAuth v1.2+ explicit
74
+ - Versions < 1.2 do not support SCRIPT_NAME properly, and may cause other issues
75
+ - Raise a distinct error when LDAP server is unreachable
76
+ - Previously raised an invalid credentials authentication failure error, which is technically incorrect
77
+
35
78
  ## [2.3.1] - 2025-11-05
36
79
 
37
80
  - TAG: [v2.3.1][2.3.1t]
@@ -197,6 +240,10 @@ Please file a bug if you notice a violation of semantic versioning.
197
240
  [1.0.0]: https://github.com/omniauth/omniauth-ldap/compare/5656da80d4193e0d0584f44bac493a87695e580f...v1.0.0
198
241
  [1.0.0t]: https://github.com/omniauth/omniauth-ldap/releases/tag/v1.0.0
199
242
 
200
- [Unreleased]: https://github.com/omniauth/omniauth-ldap/compare/v2.3.1...HEAD
243
+ [Unreleased]: https://github.com/omniauth/omniauth-ldap/compare/v2.3.3...HEAD
244
+ [2.3.3]: https://github.com/omniauth/omniauth-ldap/compare/v2.3.2...v2.3.3
245
+ [2.3.3t]: https://github.com/omniauth/omniauth-ldap/releases/tag/v2.3.3
246
+ [2.3.2]: https://github.com/omniauth/omniauth-ldap/compare/v2.3.1...v2.3.2
247
+ [2.3.2t]: https://github.com/omniauth/omniauth-ldap/releases/tag/v2.3.2
201
248
  [2.3.1]: https://github.com/omniauth/omniauth-ldap/compare/v2.0.0...v2.3.1
202
249
  [2.3.1t]: https://github.com/omniauth/omniauth-ldap/releases/tag/v2.3.1
data/CONTRIBUTING.md CHANGED
@@ -24,13 +24,13 @@ Follow these instructions:
24
24
 
25
25
  ## Executables vs Rake tasks
26
26
 
27
- Executables shipped by dependencies, such as omniauth-ldap, and stone_checksums, are available
27
+ Executables shipped by dependencies, such as kettle-dev, and stone_checksums, are available
28
28
  after running `bin/setup`. These include:
29
29
 
30
30
  - gem_checksums
31
31
  - kettle-changelog
32
32
  - kettle-commit-msg
33
- - omniauth-ldap-setup
33
+ - kettle-dev-setup
34
34
  - kettle-dvcs
35
35
  - kettle-pre-release
36
36
  - kettle-readme-backers
@@ -68,7 +68,9 @@ GitHub API and CI helpers
68
68
  Releasing and signing
69
69
  - SKIP_GEM_SIGNING: If set, skip gem signing during build/release
70
70
  - GEM_CERT_USER: Username for selecting your public cert in `certs/<USER>.pem` (defaults to $USER)
71
- - SOURCE_DATE_EPOCH: Reproducible build timestamp. `kettle-release` will set this automatically for the session.
71
+ - SOURCE_DATE_EPOCH: Reproducible build timestamp.
72
+ - `kettle-release` will set this automatically for the session.
73
+ - Not needed on bundler >= 2.7.0, as reproducible builds have become the default.
72
74
 
73
75
  Git hooks and commit message helpers (exe/kettle-commit-msg)
74
76
  - GIT_HOOK_BRANCH_VALIDATE: Branch name validation mode (e.g., `jira`) or `false` to disable
@@ -166,6 +168,7 @@ NOTE: To build without signing the gem set `SKIP_GEM_SIGNING` to any value in th
166
168
  1. Update version.rb to contain the correct version-to-be-released.
167
169
  2. Run `bundle exec kettle-changelog`.
168
170
  3. Run `bundle exec kettle-release`.
171
+ 4. Stay awake and monitor the release process for any errors, and answer any prompts.
169
172
 
170
173
  #### Manual process
171
174
 
data/FUNDING.md CHANGED
@@ -6,7 +6,7 @@ Many paths lead to being a sponsor or a backer of this project. Are you on such
6
6
 
7
7
  [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal]
8
8
 
9
- [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
9
+ [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS efforts using Patreon][🖇patreon-img]][🖇patreon]
10
10
 
11
11
  [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
12
12
  [⛳liberapay]: https://liberapay.com/pboling/donate
@@ -27,11 +27,11 @@ Many paths lead to being a sponsor or a backer of this project. Are you on such
27
27
 
28
28
  <!-- RELEASE-NOTES-FOOTER-END -->
29
29
 
30
- # 🤑 Request for Help
30
+ # 🤑 A request for help
31
31
 
32
32
  Maintainers have teeth and need to pay their dentists.
33
- After getting laid off in an RIF in March and filled with many dozens of rejections,
34
- I'm now spending ~60+ hours a week building open source tools.
33
+ After getting laid off in an RIF in March, and encountering difficulty finding a new one,
34
+ I began spending most of my time building open source tools.
35
35
  I'm hoping to be able to pay for my kids' health insurance this month,
36
36
  so if you value the work I am doing, I need your support.
37
37
  Please consider sponsoring me or the project.
@@ -40,16 +40,13 @@ To join the community or get help 👇️ Join the Discord.
40
40
 
41
41
  [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite]
42
42
 
43
- To say "thanks for maintaining such a great tool" ☝️ Join the Discord or 👇️ send money.
43
+ To say "thanks!" ☝️ Join the Discord or 👇️ send money.
44
44
 
45
- [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img]
45
+ [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
46
46
 
47
47
  # Another Way to Support Open Source Software
48
48
 
49
- > How wonderful it is that nobody need wait a single moment before starting to improve the world.<br/>
50
- >—Anne Frank
51
-
52
- I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).
49
+ I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).
53
50
 
54
51
  If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
55
52
 
data/LICENSE.txt CHANGED
@@ -1,6 +1,7 @@
1
1
  MIT License
2
2
 
3
3
  Copyright (c) 2025 Peter H. Boling, and omniauth-ldap contributors
4
+ Copyright (c) 2014 David Benko
4
5
  Copyright (c) 2011 by Ping Yu and Intridea, Inc.
5
6
 
6
7
  Permission is hereby granted, free of charge, to any person obtaining
data/README.md CHANGED
@@ -38,7 +38,7 @@
38
38
 
39
39
  # 📁 OmniAuth LDAP
40
40
 
41
- [![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf] [![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf] [![CI Ancient][🚎1-an-wfi]][🚎1-an-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
41
+ [![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf] [![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf] [![CI Ancient][🚎1-an-wfi]][🚎1-an-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
42
42
 
43
43
  `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
44
44
 
@@ -63,9 +63,17 @@ use OmniAuth::Strategies::LDAP,
63
63
  name_proc: proc { |name| name.gsub(/@.*$/, "") },
64
64
  bind_dn: "default_bind_dn",
65
65
  password: "password",
66
+ # Optional timeouts (seconds)
67
+ connect_timeout: 3,
68
+ read_timeout: 7,
66
69
  tls_options: {
67
70
  ssl_version: "TLSv1_2",
68
71
  ciphers: ["AES-128-CBC", "AES-128-CBC-HMAC-SHA1", "AES-128-CBC-HMAC-SHA256"],
72
+ },
73
+ mapping: {
74
+ "name" => "cn;lang-en",
75
+ "email" => ["preferredEmail", "mail"],
76
+ "nickname" => ["uid", "userid", "sAMAccountName"],
69
77
  }
70
78
  # Or, alternatively:
71
79
  # use OmniAuth::Strategies::LDAP, filter: '(&(uid=%{username})(memberOf=cn=myapp-users,ou=groups,dc=example,dc=com))'
@@ -73,6 +81,50 @@ use OmniAuth::Strategies::LDAP,
73
81
 
74
82
  All of the listed options are required, with the exception of `:title`, `:name_proc`, `:bind_dn`, and `:password`.
75
83
 
84
+ ### TLS certificate verification
85
+
86
+ This gem enables TLS certificate verification by default when you use `encryption: "ssl"` (LDAPS / simple TLS) or `encryption: "tls"` (STARTTLS). We always pass `tls_options` to Net::LDAP based on `OpenSSL::SSL::SSLContext::DEFAULT_PARAMS`, which includes `verify_mode: OpenSSL::SSL::VERIFY_PEER` and sane defaults.
87
+
88
+ - Secure by default: you do not need to set anything extra to verify the LDAP server certificate.
89
+ - To customize trust or ciphers, supply your own `tls_options`, which are merged over the safe defaults.
90
+ - If you truly need to skip verification (not recommended), set `disable_verify_certificates: true`.
91
+
92
+ Examples:
93
+
94
+ ```ruby
95
+ # Verify server certs (default behavior)
96
+ use OmniAuth::Strategies::LDAP,
97
+ host: ENV["LDAP_HOST"],
98
+ port: 636,
99
+ encryption: "ssl", # or "tls"
100
+ base: "dc=example,dc=com",
101
+ uid: "uid"
102
+
103
+ # Use a private CA bundle and restrict protocol/ciphers
104
+ use OmniAuth::Strategies::LDAP,
105
+ host: ENV["LDAP_HOST"],
106
+ port: 636,
107
+ encryption: "ssl",
108
+ base: "dc=example,dc=com",
109
+ uid: "uid",
110
+ tls_options: {
111
+ ca_file: "/etc/ssl/private/my_org_ca.pem",
112
+ ssl_version: "TLSv1_2",
113
+ ciphers: ["TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256"],
114
+ }
115
+
116
+ # Opt out of verification (NOT recommended – use only in trusted test/dev scenarios)
117
+ use OmniAuth::Strategies::LDAP,
118
+ host: ENV["LDAP_HOST"],
119
+ port: 636,
120
+ encryption: "ssl",
121
+ base: "dc=example,dc=com",
122
+ uid: "uid",
123
+ disable_verify_certificates: true
124
+ ```
125
+
126
+ Note: Net::LDAP historically defaulted to no certificate validation when `tls_options` were not provided. This library mitigates that by always providing secure `tls_options` unless you explicitly disable verification.
127
+
76
128
  ## 💡 Info you can shake a stick at
77
129
 
78
130
  | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
@@ -101,7 +153,7 @@ Compatible with MRI Ruby 2.0+, and concordant releases of JRuby, and TruffleRuby
101
153
 
102
154
  Available as part of the Tidelift Subscription.
103
155
 
104
- <details>
156
+ <details markdown="1">
105
157
  <summary>Need enterprise-level guarantees?</summary>
106
158
 
107
159
  The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
@@ -136,7 +188,7 @@ gem install omniauth-ldap
136
188
 
137
189
  ### 🔒 Secure Installation
138
190
 
139
- <details>
191
+ <details markdown="1">
140
192
  <summary>For Medium or High Security Installations</summary>
141
193
 
142
194
  This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by
@@ -191,6 +243,28 @@ The following options are available for configuring the OmniAuth LDAP strategy:
191
243
  - `:sasl_mechanisms` - Array of SASL mechanisms to use (e.g., ["DIGEST-MD5", "GSS-SPNEGO"]).
192
244
  - `:allow_anonymous` - Whether to allow anonymous binding (default: false).
193
245
  - `:logger` - A logger instance for debugging (optional, for internal use).
246
+ - `:password_policy` - When true, the strategy will request the LDAP Password Policy response control (OID `1.3.6.1.4.1.42.2.27.8.5.1`) during the user bind. If the server supports it, the adaptor exposes:
247
+ - `adaptor.last_operation_result` — the last Net::LDAP operation result object.
248
+ - `adaptor.last_password_policy_response` — the matching password policy response control (implementation-specific object). This can indicate conditions such as password expired, account locked, reset required, or grace logins remaining (per the draft RFC).
249
+ - `:connect_timeout` - Maximum time in seconds to wait when establishing the TCP connection to the LDAP server. Forwarded to `Net::LDAP`.
250
+ - `:read_timeout` - Maximum time in seconds to wait for reads during LDAP operations (search/bind). Forwarded to `Net::LDAP`.
251
+ - `:mapping` - Customize how LDAP attributes map to the returned `auth.info` hash. A sensible default mapping is built into the strategy and will be merged with your overrides. See `lib/omniauth/strategies/ldap.rb` for the default keys and behavior; values can be a String (single attribute), an Array (first present attribute wins), or a Hash (string pattern with placeholders like `%0` combined from multiple attributes).
252
+
253
+ Example enabling password policy:
254
+
255
+ ```ruby
256
+ use OmniAuth::Builder do
257
+ provider :ldap,
258
+ host: "ldap.example.com",
259
+ base: "dc=example,dc=com",
260
+ uid: "uid",
261
+ bind_dn: "cn=search,dc=example,dc=com",
262
+ password: ENV["LDAP_SEARCH_PASSWORD"],
263
+ password_policy: true
264
+ end
265
+ ```
266
+
267
+ Note: This is best-effort and compatible with a range of net-ldap versions. If your server supports the control, you can inspect the response via the `adaptor` instance during/after authentication (for example in a failure handler) to tailor error messages.
194
268
 
195
269
  ### Auth Hash UID vs LDAP :uid (search attribute)
196
270
 
@@ -299,6 +373,58 @@ end
299
373
 
300
374
  Then link users to `/auth/ldap` in your app (for example, in a Devise sign-in page).
301
375
 
376
+ ### Use JSON Body
377
+
378
+ This gem is compatible with JSON-encoded POST bodies as well as traditional form-encoded.
379
+
380
+ - Set header `Content-Type` to `application/json`.
381
+ - Send a JSON object containing `username` and `password`.
382
+ - Rails automatically exposes parsed JSON params via `env["action_dispatch.request.request_parameters"]`, which this strategy reads first. In non-Rails Rack apps, ensure you use a JSON parser middleware if you post raw JSON.
383
+
384
+ Examples
385
+
386
+ - curl (JSON):
387
+
388
+ ```bash
389
+ curl -i \
390
+ -X POST \
391
+ -H 'Content-Type: application/json' \
392
+ -d '{"username":"alice","password":"secret"}' \
393
+ http://localhost:3000/auth/ldap
394
+ ```
395
+
396
+ The request phase will redirect to `/auth/ldap/callback` when both fields are present.
397
+
398
+ - curl (form-encoded, still supported):
399
+
400
+ ```bash
401
+ curl -i \
402
+ -X POST \
403
+ -H 'Content-Type: application/x-www-form-urlencoded' \
404
+ --data-urlencode 'username=alice' \
405
+ --data-urlencode 'password=secret' \
406
+ http://localhost:3000/auth/ldap
407
+ ```
408
+
409
+ - Browser (JavaScript fetch):
410
+
411
+ ```js
412
+ fetch('/auth/ldap', {
413
+ method: 'POST',
414
+ headers: { 'Content-Type': 'application/json' },
415
+ body: JSON.stringify({ username: 'alice', password: 'secret' })
416
+ }).then(res => {
417
+ if (res.redirected) {
418
+ window.location = res.url; // typically /auth/ldap/callback
419
+ }
420
+ });
421
+ ```
422
+
423
+ Notes
424
+
425
+ - You can still initiate authentication by visiting `GET /auth/ldap` to render the HTML form and then submitting it (form-encoded). JSON is an additional option, not a replacement.
426
+ - In the callback phase (`POST /auth/ldap/callback`), the strategy reads JSON credentials the same way; Rails exposes them via `action_dispatch.request.request_parameters` and non-Rails apps should use a JSON parser middleware.
427
+
302
428
  ### Using a custom filter
303
429
 
304
430
  If you need to restrict authentication to a group or use a more complex lookup, pass `:filter`. Use `%{username}` — it will be replaced with the processed username (after `:name_proc`).
@@ -401,6 +527,84 @@ provider :ldap,
401
527
 
402
528
  This trims `alice@example.com` to `alice` before searching.
403
529
 
530
+ ### Mounted under a subdirectory (SCRIPT_NAME)
531
+
532
+ If your app is served from a path prefix (for example, behind a reverse proxy at `/myapp`, or mounted via Rack::URLMap, or Rails `relative_url_root`), the OmniAuth callback must include that subdirectory. This strategy uses `callback_url` for the form action and redirects, so it automatically includes any `SCRIPT_NAME` set by Rack/Rails. In other words, you typically do not need any special configuration beyond ensuring `SCRIPT_NAME` is correct in the request environment.
533
+
534
+ - Works out-of-the-box when:
535
+ - You mount the app at a path using Rack’s `map`/`URLMap`.
536
+ - You set Rails’ `config.relative_url_root` (or `RAILS_RELATIVE_URL_ROOT`) or deploy under a prefix with a reverse proxy that sets `SCRIPT_NAME`.
537
+
538
+ Rack example (mounted at /myapp):
539
+
540
+ ```ruby
541
+ # config.ru
542
+ require "rack"
543
+ require "omniauth-ldap"
544
+
545
+ app = Rack::Builder.new do
546
+ use(Rack::Session::Cookie, secret: "change_me")
547
+ use(OmniAuth::Builder) do
548
+ provider(
549
+ :ldap,
550
+ host: "ldap.example.com",
551
+ base: "dc=example,dc=com",
552
+ uid: "uid",
553
+ title: "Example LDAP",
554
+ )
555
+ end
556
+
557
+ run(->(env) { [404, {"Content-Type" => "text/plain"}, [env.key?("omniauth.auth").to_s]] })
558
+ end
559
+
560
+ run Rack::URLMap.new(
561
+ "/myapp" => app,
562
+ )
563
+ ```
564
+
565
+ - Visiting `POST /myapp/auth/ldap` renders the login form with `action='http://host/myapp/auth/ldap/callback'`.
566
+ - Any redirects (including header-based SSO fast path) will also point to `http://host/myapp/auth/ldap/callback`.
567
+
568
+ Rails example (relative_url_root):
569
+
570
+ ```ruby
571
+ # config/environments/production.rb (or an initializer)
572
+ Rails.application.configure do
573
+ config.relative_url_root = "/myapp" # or set ENV["RAILS_RELATIVE_URL_ROOT"]
574
+ end
575
+
576
+ # config/initializers/omniauth.rb
577
+ Rails.application.config.middleware.use(OmniAuth::Builder) do
578
+ provider :ldap,
579
+ title: "Acme LDAP",
580
+ host: "ldap.acme.internal",
581
+ base: "dc=acme,dc=corp",
582
+ uid: "uid",
583
+ bind_dn: "cn=search,dc=acme,dc=corp",
584
+ password: ENV["LDAP_SEARCH_PASSWORD"],
585
+ name_proc: proc { |n| n.split("@").first }
586
+ end
587
+ ```
588
+
589
+ - With `relative_url_root` set, Rails/Rack provide `SCRIPT_NAME=/myapp`, and this strategy will issue a form with `action='.../myapp/auth/ldap/callback'` and redirect accordingly.
590
+
591
+ Behind proxies with unusual host/proto handling (optional):
592
+
593
+ OmniAuth usually derives the correct scheme/host/prefix from Rack (and standard `X-Forwarded-*` headers). If your environment produces incorrect absolute URLs, you can override the computed host and prefix by setting `OmniAuth.config.full_host`:
594
+
595
+ ```ruby
596
+ OmniAuth.config.full_host = lambda do |env|
597
+ scheme = (env["HTTP_X_FORWARDED_PROTO"] || env["rack.url_scheme"]).to_s.split(",").first
598
+ host = env["HTTP_X_FORWARDED_HOST"] || env["HTTP_HOST"] || [env["SERVER_NAME"], env["SERVER_PORT"]].compact.join(":")
599
+ script = env["SCRIPT_NAME"].to_s
600
+ "#{scheme}://#{host}#{script}"
601
+ end
602
+ ```
603
+
604
+ Note: You generally do not need this override. Prefer configuring your proxy to pass standard `X-Forwarded-Proto` and `X-Forwarded-Host` headers and let Rack/OmniAuth compute the full URL.
605
+
606
+ - Header-based SSO (`header_auth: true`) also respects `SCRIPT_NAME`; when a trusted header is present on `POST /myapp/auth/ldap`, the strategy redirects to `http://host/myapp/auth/ldap/callback`.
607
+
404
608
  ### Trusted header SSO (REMOTE_USER and friends)
405
609
 
406
610
  Some deployments terminate SSO at a reverse proxy or portal and forward the already-authenticated user identity via an HTTP header such as `REMOTE_USER`.
@@ -464,7 +668,7 @@ Security checklist:
464
668
 
465
669
  ## 🦷 FLOSS Funding
466
670
 
467
- While these tools are free software and will always be, the project would benefit immensely from some funding.
671
+ While omniauth tools are free software and will always be, the project would benefit immensely from some funding.
468
672
  Raising a monthly budget of... "dollars" would make the project more sustainable.
469
673
 
470
674
  We welcome both individual and corporate sponsors! We also offer a
@@ -474,7 +678,7 @@ Currently, [GitHub Sponsors][🖇sponsor], and [Liberapay][⛳liberapay] are our
474
678
  **If you're working in a company that's making significant use of omniauth tools we'd
475
679
  appreciate it if you suggest to your company to become a omniauth sponsor.**
476
680
 
477
- You can support me in development of OmniAuth tools via
681
+ You can support the development of omniauth tools via
478
682
  [GitHub Sponsors][🖇sponsor],
479
683
  [Liberapay][⛳liberapay],
480
684
  [PayPal][🖇paypal],
@@ -494,7 +698,7 @@ I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed
494
698
 
495
699
  **[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags**
496
700
 
497
- [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]
701
+ [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS efforts using Patreon][🖇patreon-img]][🖇patreon]
498
702
 
499
703
  ## 🔐 Security
500
704
 
@@ -520,8 +724,6 @@ See [CONTRIBUTING.md][🤝contributing].
520
724
 
521
725
  [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls]
522
726
 
523
- [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov]
524
-
525
727
  ### 🪇 Code of Conduct
526
728
 
527
729
  Everyone interacting with this project's codebases, issue trackers,
@@ -568,12 +770,11 @@ For example:
568
770
  spec.add_dependency("omniauth-ldap", "~> 1.0")
569
771
  ```
570
772
 
571
- <details>
773
+ <details markdown="1">
572
774
  <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
573
775
 
574
776
  SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms
575
- is a *breaking change* to an API.
576
- It is obvious to many, but not all, and since the spec is silent, the bike shedding is endless.
777
+ is a *breaking change* to an API, and for that reason the bike shedding is endless.
577
778
 
578
779
  To get a better understanding of how SemVer is intended to work over a project's lifetime,
579
780
  read this article from the creator of SemVer:
@@ -602,6 +803,9 @@ See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright
602
803
  </picture>
603
804
  </a>, and omniauth-ldap contributors.
604
805
  </li>
806
+ <li>
807
+ Copyright (c) 2014 David Benko
808
+ </li>
605
809
  <li>
606
810
  Copyright (c) 2011 by Ping Yu and Intridea, Inc.
607
811
  </li>
@@ -622,7 +826,7 @@ To join the community or get help 👇️ Join the Discord.
622
826
 
623
827
  To say "thanks!" ☝️ Join the Discord or 👇️ send money.
624
828
 
625
- [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img]
829
+ [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal]
626
830
 
627
831
  ### Please give the project a star ⭐ ♥.
628
832
 
@@ -705,10 +909,6 @@ Thanks for RTFM. ☺️
705
909
  [👽oss-helpi]: https://www.codetriage.com/omniauth/omniauth-ldap/badges/users.svg
706
910
  [👽version]: https://bestgems.org/gems/omniauth-ldap
707
911
  [👽versioni]: https://img.shields.io/gem/v/omniauth-ldap.svg
708
- [🏀qlty-mnt]: https://qlty.sh/gh/omniauth/projects/omniauth-ldap
709
- [🏀qlty-mnti]: https://qlty.sh/gh/omniauth/projects/omniauth-ldap/maintainability.svg
710
- [🏀qlty-cov]: https://qlty.sh/gh/omniauth/projects/omniauth-ldap/metrics/code?sort=coverageRating
711
- [🏀qlty-covi]: https://qlty.sh/gh/omniauth/projects/omniauth-ldap/coverage.svg
712
912
  [🏀codecov]: https://codecov.io/gh/omniauth/omniauth-ldap
713
913
  [🏀codecovi]: https://codecov.io/gh/omniauth/omniauth-ldap/graph/badge.svg
714
914
  [🏀coveralls]: https://coveralls.io/github/omniauth/omniauth-ldap?branch=main
@@ -790,7 +990,7 @@ Thanks for RTFM. ☺️
790
990
  [📌gitmoji]: https://gitmoji.dev
791
991
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
792
992
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
793
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.233-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
993
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.293-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
794
994
  [🔐security]: SECURITY.md
795
995
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
796
996
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year