mikser-io 11.10.5 → 11.11.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.
- package/package.json +1 -1
- package/src/auth.js +31 -0
package/package.json
CHANGED
package/src/auth.js
CHANGED
|
@@ -123,6 +123,37 @@ export function reachabilityOf({ auth, token, allowRemote } = {}) {
|
|
|
123
123
|
// token-gated endpoint stayed open to localhost. It exists so the api and
|
|
124
124
|
// mcp plugins can keep their documented semantics for a plain `token:`
|
|
125
125
|
// while a real verifier gets the stricter default.
|
|
126
|
+
// The exact bytes of a Basic challenge, in one place.
|
|
127
|
+
//
|
|
128
|
+
// It was written out at five call sites across two packages, which is how the
|
|
129
|
+
// `charset` parameter came to be a hardcoded decision nobody could change
|
|
130
|
+
// without a reverse-proxy rewrite.
|
|
131
|
+
//
|
|
132
|
+
// `charset="UTF-8"` is RFC 7617 §2.1, and the RFC is explicit about its
|
|
133
|
+
// weight: "This information is purely advisory." It tells a client to encode
|
|
134
|
+
// the user-pass as UTF-8 NFC; a client that ignores it is unaffected for
|
|
135
|
+
// ASCII credentials, which is nearly all of them. It buys exactly one thing —
|
|
136
|
+
// a non-ASCII password from a client that would otherwise send latin-1, which
|
|
137
|
+
// this server cannot match because it decodes the header as UTF-8.
|
|
138
|
+
//
|
|
139
|
+
// OFF by default, because the measured cost is larger than that benefit. The
|
|
140
|
+
// Windows WebDAV mini-redirector appears not to parse the parameter, and a
|
|
141
|
+
// challenge carrying it leaves a mapped drive unable to reconnect from stored
|
|
142
|
+
// credentials at all: before/after on a live deployment, with the parameter
|
|
143
|
+
// stripped at the proxy as the only variable, the client went from never
|
|
144
|
+
// sending an Authorization header to offering Basic unprompted. Interactive
|
|
145
|
+
// entry worked either way — that path sends Basic blind and never reads the
|
|
146
|
+
// challenge, which is the asymmetry that identified it.
|
|
147
|
+
//
|
|
148
|
+
// Turn it on where non-ASCII passwords matter more than Explorer does.
|
|
149
|
+
export function basicChallenge({ realm = 'mikser', charset = false } = {}) {
|
|
150
|
+
// A realm is a quoted-string, so a quote or a backslash inside one would
|
|
151
|
+
// end the field early and hand the rest to the parser as garbage. Cheap
|
|
152
|
+
// to prevent here, and impossible to prevent at five call sites.
|
|
153
|
+
const quoted = String(realm).replace(/[\\"]/g, '\\$&')
|
|
154
|
+
return charset ? `Basic realm="${quoted}", charset="UTF-8"` : `Basic realm="${quoted}"`
|
|
155
|
+
}
|
|
156
|
+
|
|
126
157
|
export async function authorize(req, verifier, { allowRemote = false, trustLoopback = false } = {}) {
|
|
127
158
|
const local = isLoopback(req.ip)
|
|
128
159
|
|