Files
yaak/plugins/auth-ntlm/src/index.ts
Gregory Schier b4a1c418bb Run oxfmt across repo, add format script and docs
Add .oxfmtignore to skip generated bindings and wasm-pack output.
Add npm format script, update DEVELOPMENT.md for Vite+ toolchain,
and format all non-generated files with oxfmt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:15:49 -07:00

95 lines
2.6 KiB
TypeScript

import type { PluginDefinition } from "@yaakapp/api";
import { ntlm } from "httpntlm";
function extractNtlmChallenge(headers: Array<{ name: string; value: string }>): string | null {
const authValues = headers
.filter((h) => h.name.toLowerCase() === "www-authenticate")
.flatMap((h) => h.value.split(","))
.map((v) => v.trim())
.filter(Boolean);
return authValues.find((v) => /^NTLM\s+\S+/i.test(v)) ?? null;
}
export const plugin: PluginDefinition = {
authentication: {
name: "windows",
label: "NTLM Auth",
shortLabel: "NTLM",
args: [
{
type: "banner",
color: "info",
inputs: [
{
type: "markdown",
content:
"NTLM is still in beta. Please submit any issues to [Feedback](https://yaak.app/feedback).",
},
],
},
{
type: "text",
name: "username",
label: "Username",
optional: true,
},
{
type: "text",
name: "password",
label: "Password",
optional: true,
password: true,
},
{
type: "accordion",
label: "Advanced",
inputs: [
{ name: "domain", label: "Domain", type: "text", optional: true },
{ name: "workstation", label: "Workstation", type: "text", optional: true },
],
},
],
async onApply(ctx, { values, method, url }) {
const username = values.username ? String(values.username) : undefined;
const password = values.password ? String(values.password) : undefined;
const domain = values.domain ? String(values.domain) : undefined;
const workstation = values.workstation ? String(values.workstation) : undefined;
const options = {
url,
username,
password,
workstation,
domain,
};
const type1 = ntlm.createType1Message(options);
const negotiateResponse = await ctx.httpRequest.send({
httpRequest: {
method,
url,
headers: [
{ name: "Authorization", value: type1 },
{ name: "Connection", value: "keep-alive" },
],
},
});
const ntlmChallenge = extractNtlmChallenge(negotiateResponse.headers);
if (ntlmChallenge == null) {
throw new Error("Unable to find NTLM challenge in WWW-Authenticate response headers");
}
const type2 = ntlm.parseType2Message(ntlmChallenge, (err: Error | null) => {
if (err != null) throw err;
});
const type3 = ntlm.createType3Message(type2, options);
return { setHeaders: [{ name: "Authorization", value: type3 }] };
},
},
};