oauth2-forwarder 1.2.0 → 1.3.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/README.md +30 -5
- package/dist/o2f-client.js +1 -1
- package/dist/o2f-server.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
# oauth2-forwarder
|
|
2
2
|
|
|
3
|
-
This utility allows forwarding an
|
|
3
|
+
This utility allows forwarding an OAuth2 interactive request that is initiated inside a Docker container to the browser on the docker host and then forwarding the resulting redirect back into the Docker container. The net effect is that a command line tool inside the Docker container can perform interactive OAuth2 authentication using the browser flow on its host.
|
|
4
|
+
|
|
5
|
+
The tool also has a passthrough mode, disabled by default, which allows it to pass non-OAuth2 urls through to the host browser. This can be useful for CLI tools that do device authentication or really any situation where you want to use your hosts browser without copying in pasting. It of course opens up some additional security considerations worth thinking about, which is why it's not the default.
|
|
4
6
|
|
|
5
7
|
## Background
|
|
6
8
|
|
|
7
|
-
Imagine you have a command line tool that performs
|
|
9
|
+
Imagine you have a command line tool that performs OAuth2 interactive login. The typical flow of that application is you are running directly on your local machine is this:
|
|
8
10
|
|
|
9
11
|
1. CLI tool sends an interactive login url to the browser and, simultaneously, opens up an http service to listen for the redirect response.
|
|
10
12
|
2. The browser opens the login url and the user performs interactive authentication.
|
|
11
|
-
3. The successful interactive authentication results in a redirect GET request being made with the new authentication code.
|
|
13
|
+
3. The successful interactive authentication results in a redirect GET request being made to localhost with the new authentication code.
|
|
12
14
|
4. The http service setup by the CLI tool receives the redirect requests, extracts the code, and uses it.
|
|
13
15
|
|
|
14
|
-
This, for example, is exactly how the @azure/msal-node library works if you use the `.acquireTokenInteractive()` method on a public application.
|
|
16
|
+
This, for example, is exactly how the @azure/msal-node library works if you use the `.acquireTokenInteractive()` method on a public application, but many other CLI applications use this as well, for example the azure cli tool, Claude Code etc.
|
|
15
17
|
|
|
16
|
-
This flow breaks down if you try to use it inside a
|
|
18
|
+
This flow breaks down if you try to use it inside a Docker container (at least one without a GUI) because step 2 fails, i.e., there is no way for the CLI to launch the host's browser for interactive login. You could get around this by using a device flow, but (a) that's a little more cumbersome, and (b) often conditional access policies will block that flow if they are checking for device compliance.
|
|
17
19
|
|
|
18
20
|
If you ever tried to do this from inside a docker container managed by VS Code, you'd have seen that it magically works. This utility gives you similar functionality without needing to use VS Code.
|
|
19
21
|
|
|
@@ -125,8 +127,31 @@ Of course, replace `[VERSION]` and `[PORT]` with the actual version number and p
|
|
|
125
127
|
|
|
126
128
|
You can enable debugging on either the server or the client by setting the environmental variable `OAUTH2_FORWARDER_DEBUG` to `true`.
|
|
127
129
|
|
|
130
|
+
## Passthrough Mode
|
|
131
|
+
|
|
132
|
+
By default, the server validates incoming URLs and rejects malformed OAuth2 URLs with HTTP 400. For non-standard authentication flows (like device flows) that only need the browser opened without expecting a callback, you can enable passthrough mode.
|
|
133
|
+
|
|
134
|
+
With passthrough mode enabled:
|
|
135
|
+
- Malformed URLs still return HTTP 400 to the client
|
|
136
|
+
- BUT the URL is also opened in the browser (one-way, no callback expected)
|
|
137
|
+
- The 400 response message notes that the URL was sent to the browser
|
|
138
|
+
|
|
139
|
+
### Enable via environment variable:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
OAUTH2_FORWARDER_PASSTHROUGH=true o2f-server
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
This is useful for:
|
|
146
|
+
- Device code flows that don't use redirect URIs
|
|
147
|
+
- Custom authentication flows that just need browser opening
|
|
148
|
+
- Debugging authentication URLs that fail validation
|
|
149
|
+
- Any other situation where you want a CLI that respects the BROWSER= env variable to open your hosts browser
|
|
150
|
+
|
|
128
151
|
## Security
|
|
129
152
|
|
|
130
153
|
Since this is using all localhost tcp communications the security model is the same using this tool as it is in the non-containerized solution. In other words, in both cases the received auth code is transmitted over plaintext tcp on the localhost only. NB: you could modify this tool to send the client<-->server traffic across the network, but that would not be a good idea.
|
|
131
154
|
|
|
155
|
+
Of course in general, and particularly if you use passthrough mode, you should consider the implications of allowing a Docker container to open your host browser. If you are using a container to _prevent_ access to your browser, this would obviously circumvent that.
|
|
156
|
+
|
|
132
157
|
NB: If you believe there is a security issue with the app, please reach out to me directly via email, which is just `sam` at my company's domain.
|
package/dist/o2f-client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
!function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports["oauth-forwarder"]=r():e["oauth-forwarder"]=r()}(this,()=>(()=>{"use strict";var e={76:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.buildOutputWriter=function(e){return r=>{e.stream.write((0,o.color)((0,s.sanitize)(r),e.color)+"\n")}};const o=t(478),s=t(638)},113:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.buildBrowserHelper=function(e){return async r=>{const t=e.debugger?e.debugger:()=>{};if(!r)return t("No url argument present"),void e.onExit.failure();t(`Received url "${r}"`);try{const{redirectUrl:o}=await e.credentialForwarder(r);t(`Received redirect url ${o}`),t("Redirecting ..."),await e.redirect(o),t("Exiting on success..."),e.onExit.success()}catch(r){t(`Browser helper error "${r}"`),t("Exiting on failure..."),e.onExit.failure()}}}},377:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.parseServerInfo=function(e){const r=e.match(/^(.+):(\d+)$/);if(r){const[,e,t]=r;if(!e)return o.Result.failure(new Error("No host defined"));if(!t)return o.Result.failure(new Error("No port defined"));const s=parseInt(t,10);return isNaN(s)||s<0||s>65535?o.Result.failure(new Error(`Not a valid port: ${s}`)):o.Result.success({host:e,port:s})}return o.Result.failure(new Error("Invalid server info format, must be either a host:port or a valid socket path"))};const o=t(728)},472:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.buildCredentialForwarder=function(e){const r=e.debugger?e.debugger:()=>{};return t=>new Promise((o,u)=>{const n={path:"/",method:"POST"};n.port=e.port,n.host=e.host;const i=s.default.request(n,e=>{let t="";e.setEncoding("utf8"),e.on("data",e=>{r(`Data chunk received: "${e}"`),t+=e}),e.on("error",e=>{r(`Response received error: "${e}"`),u(e)}),e.on("end",()=>{const{statusCode:s,statusMessage:n}=e;r(`Status: ${s??"No Code"}-${n??"No message"}`),200!==s
|
|
2
|
+
!function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports["oauth-forwarder"]=r():e["oauth-forwarder"]=r()}(this,()=>(()=>{"use strict";var e={76:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.buildOutputWriter=function(e){return r=>{e.stream.write((0,o.color)((0,s.sanitize)(r),e.color)+"\n")}};const o=t(478),s=t(638)},113:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.buildBrowserHelper=function(e){return async r=>{const t=e.debugger?e.debugger:()=>{};if(!r)return t("No url argument present"),void e.onExit.failure();t(`Received url "${r}"`);try{const{redirectUrl:o}=await e.credentialForwarder(r);t(`Received redirect url ${o}`),t("Redirecting ..."),await e.redirect(o),t("Exiting on success..."),e.onExit.success()}catch(r){t(`Browser helper error "${r}"`),t("Exiting on failure..."),e.onExit.failure()}}}},377:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.parseServerInfo=function(e){const r=e.match(/^(.+):(\d+)$/);if(r){const[,e,t]=r;if(!e)return o.Result.failure(new Error("No host defined"));if(!t)return o.Result.failure(new Error("No port defined"));const s=parseInt(t,10);return isNaN(s)||s<0||s>65535?o.Result.failure(new Error(`Not a valid port: ${s}`)):o.Result.success({host:e,port:s})}return o.Result.failure(new Error("Invalid server info format, must be either a host:port or a valid socket path"))};const o=t(728)},472:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.buildCredentialForwarder=function(e){const r=e.debugger?e.debugger:()=>{};return t=>new Promise((o,u)=>{const n={path:"/",method:"POST"};n.port=e.port,n.host=e.host;const i=s.default.request(n,e=>{let t="";e.setEncoding("utf8"),e.on("data",e=>{r(`Data chunk received: "${e}"`),t+=e}),e.on("error",e=>{r(`Response received error: "${e}"`),u(e)}),e.on("end",()=>{const{statusCode:s,statusMessage:n}=e;if(r(`Status: ${s??"No Code"}-${n??"No message"}`),200!==s)return void u(`Http request failed: Status: ${s??"No Code"}-${n??"No message"}`);r(`Final output: "${t}"`);const i=JSON.parse(t);"url"in i||u("Response did not contain 'url' property"),o({redirectUrl:i.url})}),e.on("close",()=>{r("Response closed")})});i.on("error",e=>{r(`Request error: "${e}"`),u(e)});const c={url:t},d=JSON.stringify(c);r(`Sending request body: "${d}"`),i.write(d),r("Ending request"),i.end()})};const s=o(t(611))},478:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.color=function(e,r){return`${t[r]}${e}${o}`};const t={black:"[30m",red:"[31m",green:"[32m",yellow:"[33m",blue:"[34m",magenta:"[35m",cyan:"[36m",white:"[37m"},o="[0m"},611:e=>{e.exports=require("http")},638:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.sanitize=function(e){return e.replace(/((code|code_challenge)=)([^"&\n]*)/gi,(e,r)=>`${r}********`)}},728:(e,r)=>{var t;Object.defineProperty(r,"__esModule",{value:!0}),r.Result=r.ResultType=void 0,function(e){e[e.success=0]="success",e[e.failure=1]="failure"}(t||(r.ResultType=t={})),r.Result={success:e=>({type:t.success,value:e}),isSuccess:e=>e.type===t.success,failure:e=>({type:t.failure,error:e}),isFailure:e=>e.type===t.failure}},737:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.buildRedirect=function(e){const r=e.debugger?e.debugger:()=>{};return async e=>(r(`Making GET request to url: "${e}"`),new Promise((t,o)=>{s.default.get(e,e=>{200!==e.statusCode&&302!==e.statusCode?o(`Request returned unexpected status: ${e.statusCode}`):(r(`Received status ${e.statusCode}`),t())}).on("error",e=>{r(`Received error "${JSON.stringify(e)}"`),o(e.message)})}))};const s=o(t(611))},950:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.EnvKey=void 0,r.EnvKey={PORT:"OAUTH2_FORWARDER_PORT",SERVER:"OAUTH2_FORWARDER_SERVER",DEBUG:"OAUTH2_FORWARDER_DEBUG",PASSTHROUGH:"OAUTH2_FORWARDER_PASSTHROUGH"}}},r={};function t(o){var s=r[o];if(void 0!==s)return s.exports;var u=r[o]={exports:{}};return e[o].call(u.exports,u,u.exports,t),u.exports}var o={};return(()=>{var e=o;Object.defineProperty(e,"__esModule",{value:!0});const r=t(950),s=t(76),u=t(728),n=t(113),i=t(472),c=t(737),d=t(377),a=process.env[r.EnvKey.DEBUG],l=(0,s.buildOutputWriter)({color:"red",stream:process.stderr}),p=process.env[r.EnvKey.SERVER];p||(l(`The environmental variable ${[r.EnvKey.SERVER]} was not defined`),process.exit(1));const f=(0,d.parseServerInfo)(p);u.Result.isFailure(f)&&(l(`Invalid server info: "${f.error.message}"`),process.exit(1));const v=f.value,R=(0,i.buildCredentialForwarder)({host:v.host,port:v.port,debugger:a?(0,s.buildOutputWriter)({color:"cyan",stream:process.stderr}):void 0}),g=(0,c.buildRedirect)({debugger:a?(0,s.buildOutputWriter)({color:"yellow",stream:process.stderr}):void 0});(0,n.buildBrowserHelper)({onExit:{success:function(){process.exit(0)},failure:function(){process.exit(1)}},credentialForwarder:R,redirect:g,debugger:a?(0,s.buildOutputWriter)({color:"green",stream:process.stderr}):void 0})(process.argv[2]).catch(l)})(),o})());
|
package/dist/o2f-server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
!function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports["oauth-forwarder"]=r():e["oauth-forwarder"]=r()}(this,()=>(()=>{"use strict";var e={23:e=>{e.exports=require("util")},51:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.extractPort=function(e){const r=e.match(/^http:\/\/localhost(?::(\d{1,5}))?$/);if(!r)return o.Result.failure(new Error("Invalid URL format"));const t=r[1]?parseInt(r[1],10):void 0;return void 0===t?o.Result.success(void 0):isNaN(t)||t<0||t>65535?o.Result.failure(new Error(`Not a valid port: ${t}`)):o.Result.success(t)};const o=t(728)},76:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.buildOutputWriter=function(e){return r=>{e.stream.write((0,o.color)((0,n.sanitize)(r),e.color)+"\n")}};const o=t(478),n=t(638)},478:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.color=function(e,r){return`${t[r]}${e}${o}`};const t={black:"[30m",red:"[31m",green:"[32m",yellow:"[33m",blue:"[34m",magenta:"[35m",cyan:"[36m",white:"[37m"},o="[0m"},516:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.buildInteractiveLogin=function(e){const r=e.debugger?e.debugger:()=>{};return async(t,o)=>new Promise((i,s)=>{let a="";const c=n.default.createServer((e,t)=>{r(`Received a ${e.method??"undefined"} request`),e.on("error",e=>{r(`Error: ${JSON.stringify(e)}`),r("Terminating request on error"),t.writeHead(500,JSON.stringify(e)),r("Ending response"),t.end(),r("Closing temporary redirect server on error"),c.close(),s(e)}),e.on("data",e=>{r(`Received data chunk: ${e}`)}),e.on("close",()=>{r("Redirect request closed")}),e.on("end",()=>{r("Request ended"),a="http://"+e.headers.host+e.url,r(`Received request url: "${a}"`),r("Successfully terminating request"),t.writeHead(200),t.end("Authentication completed. You may close this page now."),r("Closing temporary redirect server on success"),c.close(),i(a)})});r(`Starting temporary redirect server on port ${o}...`),c.listen(o,"127.0.0.1"),r("Opening browser for interactive login..."),e.openBrowser(t)})};const n=o(t(611))},561:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0});const n=t(950),i=t(76),s=t(516),a=t(802),c=t(896),l=o(t(667)),u=process.env[n.EnvKey.DEBUG],d="127.0.0.1",p=(0,i.buildOutputWriter)({color:"cyan",stream:process.stdout}),m=(0,i.buildOutputWriter)({color:"yellow",stream:process.stdout}),f=(0,i.buildOutputWriter)({color:"white",stream:process.stdout}),g=(0,i.buildOutputWriter)({color:"red",stream:process.stderr});let w=null;const h=process.env[n.EnvKey.PORT];if(h){const e=parseInt(h);isNaN(e)||(w=e)}const v=(0,s.buildInteractiveLogin)({openBrowser:async e=>{await(0,l.default)(e)},debugger:u?(0,i.buildOutputWriter)({color:"magenta",stream:process.stdout}):void 0});(async()=>{w&&p(`Attempting to use user specified port ${w}`);const e=w??await(0,c.findAvailablePort)(d),r=(0,a.buildCredentialProxy)({host:d,port:e,interactiveLogin:v,debugger:u?(0,i.buildOutputWriter)({color:"green",stream:process.stdout}):void 0});p(`Starting TCP server listening on ${d}:${e}`),m("Run the following command in your docker container:\n"),f(` export ${n.EnvKey.SERVER}="host.docker.internal:${e}"\n`),m("\nIn addition, you need to set the BROWSER env variable to point to the client script in the docker container. If you are using the default locations, this will work:\n"),f(" export BROWSER=~/o2f/browser.sh\n");try{await r(),p("Ctrl+c to stop server.")}catch(e){g(JSON.stringify(e)),process.exit(1)}})().catch(e=>{g(JSON.stringify(e)),process.exit(1)})},611:e=>{e.exports=require("http")},638:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.sanitize=function(e){return e.replace(/((code|code_challenge)=)([^"&\n]*)/gi,(e,r)=>`${r}********`)}},667:(e,r,t)=>{t.r(r),t.d(r,{apps:()=>D,default:()=>K,openApp:()=>I});const o=require("node:process"),n=require("node:path"),i=require("node:url"),s=require("node:child_process"),a=require("node:fs/promises"),c=require("node:os"),l=require("node:fs");let u,d;function p(){return void 0===d&&(d=(()=>{try{return l.statSync("/run/.containerenv"),!0}catch{return!1}})()||(void 0===u&&(u=function(){try{return l.statSync("/.dockerenv"),!0}catch{return!1}}()||function(){try{return l.readFileSync("/proc/self/cgroup","utf8").includes("docker")}catch{return!1}}()),u)),d}const m=()=>{if("linux"!==o.platform)return!1;if(c.release().toLowerCase().includes("microsoft"))return!p();try{return!!l.readFileSync("/proc/version","utf8").toLowerCase().includes("microsoft")&&!p()}catch{return!1}},f=o.env.__IS_WSL_TEST__?m:m(),g=require("node:util"),w=require("node:buffer"),h=(0,g.promisify)(s.execFile),v=()=>`${o.env.SYSTEMROOT||o.env.windir||String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,y=async(e,r={})=>{const{powerShellPath:t,...o}=r,n=y.encodeCommand(e);return h(t??v(),[...y.argumentsPrefix,n],{encoding:"utf8",...o})};y.argumentsPrefix=["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand"],y.encodeCommand=e=>w.Buffer.from(e,"utf16le").toString("base64"),y.escapeArgument=e=>`'${String(e).replaceAll("'","''")}'`;const b=(0,g.promisify)(s.execFile),_=(()=>{const e="/mnt/";let r;return async function(){if(r)return r;const t="/etc/wsl.conf";let o=!1;try{await a.access(t,a.constants.F_OK),o=!0}catch{}if(!o)return e;const n=await a.readFile(t,{encoding:"utf8"}),i=/(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(n);return i?(r=i.groups.mountPoint.trim(),r=r.endsWith("/")?r:`${r}/`,r):e}})(),x=f?async()=>`${await _()}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`:v;let S;function E(e,r,t){const o=t=>Object.defineProperty(e,r,{value:t,enumerable:!0,writable:!0});return Object.defineProperty(e,r,{configurable:!0,enumerable:!0,get(){const e=t();return o(e),e},set(e){o(e)}}),e}const P=(0,g.promisify)(s.execFile),O={MSEdgeHTM:{name:"Edge",id:"com.microsoft.edge"},MSEdgeBHTML:{name:"Edge Beta",id:"com.microsoft.edge.beta"},MSEdgeDHTML:{name:"Edge Dev",id:"com.microsoft.edge.dev"},AppXq0fevzme2pys62n3e0fbqa7peapykr8v:{name:"Edge",id:"com.microsoft.edge.old"},ChromeHTML:{name:"Chrome",id:"com.google.chrome"},ChromeBHTML:{name:"Chrome Beta",id:"com.google.chrome.beta"},ChromeDHTML:{name:"Chrome Dev",id:"com.google.chrome.dev"},ChromiumHTM:{name:"Chromium",id:"org.chromium.Chromium"},BraveHTML:{name:"Brave",id:"com.brave.Browser"},BraveBHTML:{name:"Brave Beta",id:"com.brave.Browser.beta"},BraveDHTML:{name:"Brave Dev",id:"com.brave.Browser.dev"},BraveSSHTM:{name:"Brave Nightly",id:"com.brave.Browser.nightly"},FirefoxURL:{name:"Firefox",id:"org.mozilla.firefox"},OperaStable:{name:"Opera",id:"com.operasoftware.Opera"},VivaldiHTM:{name:"Vivaldi",id:"com.vivaldi.Vivaldi"},"IE.HTTP":{name:"Internet Explorer",id:"com.microsoft.ie"}},R=new Map(Object.entries(O));class A extends Error{}const C=(0,g.promisify)(s.execFile),$=(0,g.promisify)(s.execFile);async function M(e){return async function(e,{humanReadableOutput:r=!0,signal:t}={}){if("darwin"!==o.platform)throw new Error("macOS only");const n=r?[]:["-ss"],i={};t&&(i.signal=t);const{stdout:s}=await $("osascript",["-e",e,n],i);return s.trim()}(`tell application "Finder" to set app_path to application file id "${e}" as string\ntell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`)}const T=(0,g.promisify)(s.execFile);const B=Boolean(o.env.SSH_CONNECTION||o.env.SSH_CLIENT||o.env.SSH_TTY),F=Symbol("fallbackAttempt"),H=n.dirname((0,i.fileURLToPath)("file:///home/runner/work/oauth2-forwarder/oauth2-forwarder/node_modules/open/index.js")),L=n.join(H,"xdg-open"),{platform:U,arch:j}=o,q=async(e,r)=>{if(0===e.length)return;const t=[];for(const o of e)try{return await r(o)}catch(e){t.push(e)}throw new AggregateError(t,"Failed to open in all supported apps")},N=async e=>{const r=!0===(e={wait:!1,background:!1,newInstance:!1,allowNonzeroExitCode:!1,...e})[F];if(delete e[F],Array.isArray(e.app))return q(e.app,r=>N({...e,app:r,[F]:!0}));let t,{name:n,arguments:i=[]}=e.app??{};if(i=[...i],Array.isArray(n))return q(n,r=>N({...e,app:{name:r,arguments:i},[F]:!0}));if("browser"===n||"browserPrivate"===n){const r={"com.google.chrome":"chrome","google-chrome.desktop":"chrome","com.brave.browser":"brave","org.mozilla.firefox":"firefox","firefox.desktop":"firefox","com.microsoft.msedge":"edge","com.microsoft.edge":"edge","com.microsoft.edgemac":"edge","microsoft-edge.desktop":"edge","com.apple.safari":"safari"},t={chrome:"--incognito",brave:"--incognito",firefox:"--private-window",edge:"--inPrivate"};let s;if(f){const e=await(async()=>{const e=await x(),r=String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`,{stdout:t}=await y(r,{powerShellPath:e});return t.trim()})();s=R.get(e)??{}}else s=await async function(){if("darwin"===o.platform){const e=await async function(){if("darwin"!==o.platform)throw new Error("macOS only");const{stdout:e}=await C("defaults",["read","com.apple.LaunchServices/com.apple.launchservices.secure","LSHandlers"]),r=/LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(e),t=r?.groups.id??"com.apple.Safari";return"com.apple.safari"===t?"com.apple.Safari":t}();return{name:await M(e),id:e}}if("linux"===o.platform){const{stdout:e}=await T("xdg-mime",["query","default","x-scheme-handler/http"]),r=e.trim();return{name:r.replace(/.desktop$/,"").replace("-"," ").toLowerCase().replaceAll(/(?:^|\s|-)\S/g,e=>e.toUpperCase()),id:r}}if("win32"===o.platform)return async function(e=P){const{stdout:r}=await e("reg",["QUERY"," HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice","/v","ProgId"]),t=/ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(r);if(!t)throw new A(`Cannot find Windows browser in stdout: ${JSON.stringify(r)}`);const{id:o}=t.groups,n=O[o];if(!n)throw new A(`Unknown browser ID: ${o}`);return n}();throw new Error("Only macOS, Linux, and Windows are supported")}();if(s.id in r){const o=r[s.id.toLowerCase()];if("browserPrivate"===n){if("safari"===o)throw new Error("Safari doesn't support opening in private mode via command line");i.push(t[o])}return N({...e,app:{name:D[o],arguments:i}})}throw new Error(`${s.name} is not supported as a default browser`)}const c=[],l={};let u=!1;if(!f||p()||B||n||(u=await(async()=>(S??=(async()=>{try{const e=await x();return await a.access(e,a.constants.X_OK),!0}catch{return!1}})(),S))()),"darwin"===U)t="open",e.wait&&c.push("--wait-apps"),e.background&&c.push("--background"),e.newInstance&&c.push("--new"),n&&c.push("-a",n);else if("win32"===U||u){t=await x(),c.push(...y.argumentsPrefix),f||(l.windowsVerbatimArguments=!0),f&&e.target&&(e.target=await(async e=>{if(/^[a-z]+:\/\//i.test(e))return e;try{const{stdout:r}=await b("wslpath",["-aw",e],{encoding:"utf8"});return r.trim()}catch{return e}})(e.target));const r=["$ProgressPreference = 'SilentlyContinue';","Start"];e.wait&&r.push("-Wait"),n?(r.push(y.escapeArgument(n)),e.target&&i.push(e.target)):e.target&&r.push(y.escapeArgument(e.target)),i.length>0&&(i=i.map(e=>y.escapeArgument(e)),r.push("-ArgumentList",i.join(","))),e.target=y.encodeCommand(r.join(" ")),e.wait||(l.stdio="ignore")}else{if(n)t=n;else{const e=!H||"/"===H;let r=!1;try{await a.access(L,a.constants.X_OK),r=!0}catch{}t=o.versions.electron??("android"===U||e||!r)?"xdg-open":L}i.length>0&&c.push(...i),e.wait||(l.stdio="ignore",l.detached=!0)}"darwin"===U&&i.length>0&&c.push("--args",...i),e.target&&c.push(e.target);const d=s.spawn(t,c,l);return e.wait?new Promise((r,t)=>{d.once("error",t),d.once("close",o=>{e.allowNonzeroExitCode||0===o?r(d):t(new Error(`Exited with code ${o}`))})}):r?new Promise((e,r)=>{d.once("error",r),d.once("spawn",()=>{d.once("close",t=>{d.off("error",r),0===t?(d.unref(),e(d)):r(new Error(`Exited with code ${t}`))})})}):(d.unref(),new Promise((e,r)=>{d.once("error",r),d.once("spawn",()=>{d.off("error",r),e(d)})}))},I=(e,r)=>{if("string"!=typeof e&&!Array.isArray(e))throw new TypeError("Expected a valid `name`");const{arguments:t=[]}=r??{};if(null!=t&&!Array.isArray(t))throw new TypeError("Expected `appArguments` as Array type");return N({...r,app:{name:e,arguments:t}})};function W(e){if("string"==typeof e||Array.isArray(e))return e;const{[j]:r}=e;if(!r)throw new Error(`${j} is not supported`);return r}function k({[U]:e},{wsl:r}={}){if(r&&f)return W(r);if(!e)throw new Error(`${U} is not supported`);return W(e)}const D={browser:"browser",browserPrivate:"browserPrivate"};E(D,"chrome",()=>k({darwin:"google chrome",win32:"chrome",linux:["google-chrome","google-chrome-stable","chromium","chromium-browser"]},{wsl:{ia32:"/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",x64:["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe","/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]}})),E(D,"brave",()=>k({darwin:"brave browser",win32:"brave",linux:["brave-browser","brave"]},{wsl:{ia32:"/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",x64:["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe","/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]}})),E(D,"firefox",()=>k({darwin:"firefox",win32:String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,linux:"firefox"},{wsl:"/mnt/c/Program Files/Mozilla Firefox/firefox.exe"})),E(D,"edge",()=>k({darwin:"microsoft edge",win32:"msedge",linux:["microsoft-edge","microsoft-edge-dev"]},{wsl:"/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"})),E(D,"safari",()=>k({darwin:"Safari"}));const K=(e,r)=>{if("string"!=typeof e)throw new TypeError("Expected a `target`");return N({...r,target:e})}},728:(e,r)=>{var t;Object.defineProperty(r,"__esModule",{value:!0}),r.Result=r.ResultType=void 0,function(e){e[e.success=0]="success",e[e.failure=1]="failure"}(t||(r.ResultType=t={})),r.Result={success:e=>({type:t.success,value:e}),isSuccess:e=>e.type===t.success,failure:e=>({type:t.failure,error:e}),isFailure:e=>e.type===t.failure}},802:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.buildCredentialProxy=function(e){return async()=>{const r=e.debugger?e.debugger:()=>{},t=n.default.createServer((t,o)=>{r(`Request received at ${t.headers.host}`);const n=[];t.on("close",()=>{r("Request closed.")}),t.on("error",e=>{r(`Request received error "${e}"`)}),t.on("data",e=>{n.push(e)}),t.on("end",()=>{r("Request ended");const t=JSON.parse(n.join(""));let c;if(r(`Received body: "${n.join("")}"`),!("url"in t)){const e="Received body does not contain a 'url' property";return r(`Error: ${e}`),o.writeHead(400,e),void o.end()}{const e=(0,i.parseOauth2Url)(t.url);if(s.Result.isFailure(e))return r(`Error: ${e.error.message}`),o.writeHead(400,e.error.message),void o.end();c=e.value}const l=(0,a.extractPort)(c.redirect_uri);if(s.Result.isFailure(l))return r(`Error: ${l.error.message}`),o.writeHead(400,l.error.message),void o.end();const u=l.value??80;r(`Using port number: ${u}`),e.interactiveLogin(t.url,u).then(e=>{r("Interactive login completed"),r("Sending success header"),o.writeHead(200);const t={url:e};r(`Ending response with output: "${JSON.stringify(t)}"`),o.end(JSON.stringify(t))}).catch(e=>{r(`Interactive login errored: "${JSON.stringify(e)}"`),r("Sending error header"),o.writeHead(500,JSON.stringify(e)),r("Ending response"),o.end()})})});return r("Starting credential proxy..."),t.listen(e.port,e.host),{close:()=>t.close()}}};const n=o(t(611)),i=t(876),s=t(728),a=t(51)},876:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.parseOauth2Url=function(e){if(!e)return o.Result.failure(new Error("Url parameter was undefined"));const r=["client_id","response_type","redirect_uri","scope","code_challenge","code_challenge_method"],t=new URL(e),n=Object.fromEntries(t.searchParams.entries());for(const e of r)if(!n[e])return o.Result.failure(new Error(`Missing required parameter: ${e}`));if(!["S256","plain"].includes(n.code_challenge_method))return o.Result.failure(new Error(`${n.code_challenge_method} is not valid for "code_challenge_method" property`));if(n.response_mode&&!["query","fragment","form_post"].includes(n.response_mode))return o.Result.failure(new Error(`${n.response_mode} is not valid for "response_mode" property`));if(n.prompt&&!["login","none","consent","select_account"].includes(n.prompt))return o.Result.failure(new Error(`${n.prompt} is not valid for "prompt" property`));const i={client_id:n.client_id,response_type:n.response_type,redirect_uri:n.redirect_uri,scope:n.scope,code_challenge:n.code_challenge,code_challenge_method:n.code_challenge_method,response_mode:n.response_mode,state:n.state,prompt:n.prompt,login_hint:n.login_hint,domain_hint:n.domain_hint,"x-client-SKU":n["x-client-SKU"],"x-client-VER":n["x-client-VER"],"x-client-OS":n["x-client-OS"],"x-client-CPU":n["x-client-CPU"],client_info:n.client_info};return o.Result.success(i)};const o=t(728)},896:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.findAvailablePort=void 0;const o=t(611),n=t(23);r.findAvailablePort=async e=>{const t=Math.floor(64512*Math.random()+1024);return new Promise(i=>{const s=(0,o.createServer)();s.listen(t,()=>{s.close(async()=>{await(0,n.promisify)(setTimeout)(250),i(t)})}).on("error",()=>{i((0,r.findAvailablePort)(e))})})}},950:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.EnvKey=void 0,r.EnvKey={PORT:"OAUTH2_FORWARDER_PORT",SERVER:"OAUTH2_FORWARDER_SERVER",DEBUG:"OAUTH2_FORWARDER_DEBUG"}}},r={};function t(o){var n=r[o];if(void 0!==n)return n.exports;var i=r[o]={exports:{}};return e[o].call(i.exports,i,i.exports,t),i.exports}return t.d=(e,r)=>{for(var o in r)t.o(r,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},t.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t(561)})());
|
|
2
|
+
!function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports["oauth-forwarder"]=r():e["oauth-forwarder"]=r()}(this,()=>(()=>{"use strict";var e={23:e=>{e.exports=require("util")},51:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.extractPort=function(e){const r=e.match(/^http:\/\/localhost(?::(\d{1,5}))?(?:\/.*)?$/);if(!r)return o.Result.failure(new Error("Invalid URL format"));const t=r[1]?parseInt(r[1],10):void 0;return void 0===t?o.Result.success(void 0):isNaN(t)||t<0||t>65535?o.Result.failure(new Error(`Not a valid port: ${t}`)):o.Result.success(t)};const o=t(728)},76:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.buildOutputWriter=function(e){return r=>{e.stream.write((0,o.color)((0,n.sanitize)(r),e.color)+"\n")}};const o=t(478),n=t(638)},478:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.color=function(e,r){return`${t[r]}${e}${o}`};const t={black:"[30m",red:"[31m",green:"[32m",yellow:"[33m",blue:"[34m",magenta:"[35m",cyan:"[36m",white:"[37m"},o="[0m"},516:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.buildInteractiveLogin=function(e){const r=e.debugger?e.debugger:()=>{};return async(t,o)=>new Promise((i,s)=>{let a="";const c=n.default.createServer((e,t)=>{r(`Received a ${e.method??"undefined"} request`),e.on("error",e=>{r(`Error: ${JSON.stringify(e)}`),r("Terminating request on error"),t.writeHead(500,JSON.stringify(e)),r("Ending response"),t.end(),r("Closing temporary redirect server on error"),c.close(),s(e)}),e.on("data",e=>{r(`Received data chunk: ${e}`)}),e.on("close",()=>{r("Redirect request closed")}),e.on("end",()=>{if(r("Request ended"),!e.headers.host){const e="Missing Host header in redirect request";return r(`Error: ${e}`),t.writeHead(400,e),t.end(),c.close(),void s(new Error(e))}a="http://"+e.headers.host+e.url,r(`Received request url: "${a}"`),r("Successfully terminating request"),t.writeHead(200),t.end("Authentication completed. You may close this page now."),r("Closing temporary redirect server on success"),c.close(),i(a)})});r(`Starting temporary redirect server on port ${o}...`),c.listen(o,"127.0.0.1"),r("Opening browser for interactive login..."),e.openBrowser(t)})};const n=o(t(611))},561:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0});const n=t(950),i=t(76),s=t(516),a=t(802),c=t(896),l=o(t(667)),d=process.env[n.EnvKey.DEBUG],u="true"===process.env[n.EnvKey.PASSTHROUGH],p="127.0.0.1",m=(0,i.buildOutputWriter)({color:"cyan",stream:process.stdout}),f=(0,i.buildOutputWriter)({color:"yellow",stream:process.stdout}),g=(0,i.buildOutputWriter)({color:"white",stream:process.stdout}),w=(0,i.buildOutputWriter)({color:"red",stream:process.stderr});let h=null;const v=process.env[n.EnvKey.PORT];if(v){const e=parseInt(v);isNaN(e)||(h=e)}const y=async e=>{await(0,l.default)(e)},b=(0,s.buildInteractiveLogin)({openBrowser:y,debugger:d?(0,i.buildOutputWriter)({color:"magenta",stream:process.stdout}):void 0});(async()=>{h&&m(`Attempting to use user specified port ${h}`);const e=h??await(0,c.findAvailablePort)(p),r=(0,a.buildCredentialProxy)({host:p,port:e,interactiveLogin:b,openBrowser:y,passthrough:u,debugger:d?(0,i.buildOutputWriter)({color:"green",stream:process.stdout}):void 0});m(`Starting TCP server listening on ${p}:${e}`),u&&m("Passthrough mode enabled: malformed URLs will be opened in browser"),f("Run the following command in your docker container:\n"),g(` export ${n.EnvKey.SERVER}="host.docker.internal:${e}"\n`),f("\nIn addition, you need to set the BROWSER env variable to point to the client script in the docker container. If you are using the default locations, this will work:\n"),g(" export BROWSER=~/o2f/browser.sh\n");try{await r(),m("Ctrl+c to stop server.")}catch(e){w(JSON.stringify(e)),process.exit(1)}})().catch(e=>{w(JSON.stringify(e)),process.exit(1)})},611:e=>{e.exports=require("http")},638:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.sanitize=function(e){return e.replace(/((code|code_challenge)=)([^"&\n]*)/gi,(e,r)=>`${r}********`)}},667:(e,r,t)=>{t.r(r),t.d(r,{apps:()=>D,default:()=>K,openApp:()=>I});const o=require("node:process"),n=require("node:path"),i=require("node:url"),s=require("node:child_process"),a=require("node:fs/promises"),c=require("node:os"),l=require("node:fs");let d,u;function p(){return void 0===u&&(u=(()=>{try{return l.statSync("/run/.containerenv"),!0}catch{return!1}})()||(void 0===d&&(d=function(){try{return l.statSync("/.dockerenv"),!0}catch{return!1}}()||function(){try{return l.readFileSync("/proc/self/cgroup","utf8").includes("docker")}catch{return!1}}()),d)),u}const m=()=>{if("linux"!==o.platform)return!1;if(c.release().toLowerCase().includes("microsoft"))return!p();try{return!!l.readFileSync("/proc/version","utf8").toLowerCase().includes("microsoft")&&!p()}catch{return!1}},f=o.env.__IS_WSL_TEST__?m:m(),g=require("node:util"),w=require("node:buffer"),h=(0,g.promisify)(s.execFile),v=()=>`${o.env.SYSTEMROOT||o.env.windir||String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,y=async(e,r={})=>{const{powerShellPath:t,...o}=r,n=y.encodeCommand(e);return h(t??v(),[...y.argumentsPrefix,n],{encoding:"utf8",...o})};y.argumentsPrefix=["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand"],y.encodeCommand=e=>w.Buffer.from(e,"utf16le").toString("base64"),y.escapeArgument=e=>`'${String(e).replaceAll("'","''")}'`;const b=(0,g.promisify)(s.execFile),_=(()=>{const e="/mnt/";let r;return async function(){if(r)return r;const t="/etc/wsl.conf";let o=!1;try{await a.access(t,a.constants.F_OK),o=!0}catch{}if(!o)return e;const n=await a.readFile(t,{encoding:"utf8"}),i=/(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(n);return i?(r=i.groups.mountPoint.trim(),r=r.endsWith("/")?r:`${r}/`,r):e}})(),S=f?async()=>`${await _()}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`:v;let x;function E(e,r,t){const o=t=>Object.defineProperty(e,r,{value:t,enumerable:!0,writable:!0});return Object.defineProperty(e,r,{configurable:!0,enumerable:!0,get(){const e=t();return o(e),e},set(e){o(e)}}),e}const P=(0,g.promisify)(s.execFile),R={MSEdgeHTM:{name:"Edge",id:"com.microsoft.edge"},MSEdgeBHTML:{name:"Edge Beta",id:"com.microsoft.edge.beta"},MSEdgeDHTML:{name:"Edge Dev",id:"com.microsoft.edge.dev"},AppXq0fevzme2pys62n3e0fbqa7peapykr8v:{name:"Edge",id:"com.microsoft.edge.old"},ChromeHTML:{name:"Chrome",id:"com.google.chrome"},ChromeBHTML:{name:"Chrome Beta",id:"com.google.chrome.beta"},ChromeDHTML:{name:"Chrome Dev",id:"com.google.chrome.dev"},ChromiumHTM:{name:"Chromium",id:"org.chromium.Chromium"},BraveHTML:{name:"Brave",id:"com.brave.Browser"},BraveBHTML:{name:"Brave Beta",id:"com.brave.Browser.beta"},BraveDHTML:{name:"Brave Dev",id:"com.brave.Browser.dev"},BraveSSHTM:{name:"Brave Nightly",id:"com.brave.Browser.nightly"},FirefoxURL:{name:"Firefox",id:"org.mozilla.firefox"},OperaStable:{name:"Opera",id:"com.operasoftware.Opera"},VivaldiHTM:{name:"Vivaldi",id:"com.vivaldi.Vivaldi"},"IE.HTTP":{name:"Internet Explorer",id:"com.microsoft.ie"}},O=new Map(Object.entries(R));class A extends Error{}const $=(0,g.promisify)(s.execFile),C=(0,g.promisify)(s.execFile);async function T(e){return async function(e,{humanReadableOutput:r=!0,signal:t}={}){if("darwin"!==o.platform)throw new Error("macOS only");const n=r?[]:["-ss"],i={};t&&(i.signal=t);const{stdout:s}=await C("osascript",["-e",e,n],i);return s.trim()}(`tell application "Finder" to set app_path to application file id "${e}" as string\ntell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`)}const H=(0,g.promisify)(s.execFile);const M=Boolean(o.env.SSH_CONNECTION||o.env.SSH_CLIENT||o.env.SSH_TTY),B=Symbol("fallbackAttempt"),U=n.dirname((0,i.fileURLToPath)("file:///home/runner/work/oauth2-forwarder/oauth2-forwarder/node_modules/open/index.js")),F=n.join(U,"xdg-open"),{platform:L,arch:q}=o,j=async(e,r)=>{if(0===e.length)return;const t=[];for(const o of e)try{return await r(o)}catch(e){t.push(e)}throw new AggregateError(t,"Failed to open in all supported apps")},N=async e=>{const r=!0===(e={wait:!1,background:!1,newInstance:!1,allowNonzeroExitCode:!1,...e})[B];if(delete e[B],Array.isArray(e.app))return j(e.app,r=>N({...e,app:r,[B]:!0}));let t,{name:n,arguments:i=[]}=e.app??{};if(i=[...i],Array.isArray(n))return j(n,r=>N({...e,app:{name:r,arguments:i},[B]:!0}));if("browser"===n||"browserPrivate"===n){const r={"com.google.chrome":"chrome","google-chrome.desktop":"chrome","com.brave.browser":"brave","org.mozilla.firefox":"firefox","firefox.desktop":"firefox","com.microsoft.msedge":"edge","com.microsoft.edge":"edge","com.microsoft.edgemac":"edge","microsoft-edge.desktop":"edge","com.apple.safari":"safari"},t={chrome:"--incognito",brave:"--incognito",firefox:"--private-window",edge:"--inPrivate"};let s;if(f){const e=await(async()=>{const e=await S(),r=String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`,{stdout:t}=await y(r,{powerShellPath:e});return t.trim()})();s=O.get(e)??{}}else s=await async function(){if("darwin"===o.platform){const e=await async function(){if("darwin"!==o.platform)throw new Error("macOS only");const{stdout:e}=await $("defaults",["read","com.apple.LaunchServices/com.apple.launchservices.secure","LSHandlers"]),r=/LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(e),t=r?.groups.id??"com.apple.Safari";return"com.apple.safari"===t?"com.apple.Safari":t}();return{name:await T(e),id:e}}if("linux"===o.platform){const{stdout:e}=await H("xdg-mime",["query","default","x-scheme-handler/http"]),r=e.trim();return{name:r.replace(/.desktop$/,"").replace("-"," ").toLowerCase().replaceAll(/(?:^|\s|-)\S/g,e=>e.toUpperCase()),id:r}}if("win32"===o.platform)return async function(e=P){const{stdout:r}=await e("reg",["QUERY"," HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice","/v","ProgId"]),t=/ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(r);if(!t)throw new A(`Cannot find Windows browser in stdout: ${JSON.stringify(r)}`);const{id:o}=t.groups,n=R[o];if(!n)throw new A(`Unknown browser ID: ${o}`);return n}();throw new Error("Only macOS, Linux, and Windows are supported")}();if(s.id in r){const o=r[s.id.toLowerCase()];if("browserPrivate"===n){if("safari"===o)throw new Error("Safari doesn't support opening in private mode via command line");i.push(t[o])}return N({...e,app:{name:D[o],arguments:i}})}throw new Error(`${s.name} is not supported as a default browser`)}const c=[],l={};let d=!1;if(!f||p()||M||n||(d=await(async()=>(x??=(async()=>{try{const e=await S();return await a.access(e,a.constants.X_OK),!0}catch{return!1}})(),x))()),"darwin"===L)t="open",e.wait&&c.push("--wait-apps"),e.background&&c.push("--background"),e.newInstance&&c.push("--new"),n&&c.push("-a",n);else if("win32"===L||d){t=await S(),c.push(...y.argumentsPrefix),f||(l.windowsVerbatimArguments=!0),f&&e.target&&(e.target=await(async e=>{if(/^[a-z]+:\/\//i.test(e))return e;try{const{stdout:r}=await b("wslpath",["-aw",e],{encoding:"utf8"});return r.trim()}catch{return e}})(e.target));const r=["$ProgressPreference = 'SilentlyContinue';","Start"];e.wait&&r.push("-Wait"),n?(r.push(y.escapeArgument(n)),e.target&&i.push(e.target)):e.target&&r.push(y.escapeArgument(e.target)),i.length>0&&(i=i.map(e=>y.escapeArgument(e)),r.push("-ArgumentList",i.join(","))),e.target=y.encodeCommand(r.join(" ")),e.wait||(l.stdio="ignore")}else{if(n)t=n;else{const e=!U||"/"===U;let r=!1;try{await a.access(F,a.constants.X_OK),r=!0}catch{}t=o.versions.electron??("android"===L||e||!r)?"xdg-open":F}i.length>0&&c.push(...i),e.wait||(l.stdio="ignore",l.detached=!0)}"darwin"===L&&i.length>0&&c.push("--args",...i),e.target&&c.push(e.target);const u=s.spawn(t,c,l);return e.wait?new Promise((r,t)=>{u.once("error",t),u.once("close",o=>{e.allowNonzeroExitCode||0===o?r(u):t(new Error(`Exited with code ${o}`))})}):r?new Promise((e,r)=>{u.once("error",r),u.once("spawn",()=>{u.once("close",t=>{u.off("error",r),0===t?(u.unref(),e(u)):r(new Error(`Exited with code ${t}`))})})}):(u.unref(),new Promise((e,r)=>{u.once("error",r),u.once("spawn",()=>{u.off("error",r),e(u)})}))},I=(e,r)=>{if("string"!=typeof e&&!Array.isArray(e))throw new TypeError("Expected a valid `name`");const{arguments:t=[]}=r??{};if(null!=t&&!Array.isArray(t))throw new TypeError("Expected `appArguments` as Array type");return N({...r,app:{name:e,arguments:t}})};function W(e){if("string"==typeof e||Array.isArray(e))return e;const{[q]:r}=e;if(!r)throw new Error(`${q} is not supported`);return r}function k({[L]:e},{wsl:r}={}){if(r&&f)return W(r);if(!e)throw new Error(`${L} is not supported`);return W(e)}const D={browser:"browser",browserPrivate:"browserPrivate"};E(D,"chrome",()=>k({darwin:"google chrome",win32:"chrome",linux:["google-chrome","google-chrome-stable","chromium","chromium-browser"]},{wsl:{ia32:"/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",x64:["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe","/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]}})),E(D,"brave",()=>k({darwin:"brave browser",win32:"brave",linux:["brave-browser","brave"]},{wsl:{ia32:"/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",x64:["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe","/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]}})),E(D,"firefox",()=>k({darwin:"firefox",win32:String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,linux:"firefox"},{wsl:"/mnt/c/Program Files/Mozilla Firefox/firefox.exe"})),E(D,"edge",()=>k({darwin:"microsoft edge",win32:"msedge",linux:["microsoft-edge","microsoft-edge-dev"]},{wsl:"/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"})),E(D,"safari",()=>k({darwin:"Safari"}));const K=(e,r)=>{if("string"!=typeof e)throw new TypeError("Expected a `target`");return N({...r,target:e})}},728:(e,r)=>{var t;Object.defineProperty(r,"__esModule",{value:!0}),r.Result=r.ResultType=void 0,function(e){e[e.success=0]="success",e[e.failure=1]="failure"}(t||(r.ResultType=t={})),r.Result={success:e=>({type:t.success,value:e}),isSuccess:e=>e.type===t.success,failure:e=>({type:t.failure,error:e}),isFailure:e=>e.type===t.failure}},802:function(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.buildCredentialProxy=function(e){return async()=>{const r=e.debugger?e.debugger:()=>{},t=n.default.createServer((t,o)=>{r(`Request received at ${t.headers.host}`);const n=[];t.on("close",()=>{r("Request closed.")}),t.on("error",e=>{r(`Request received error "${e}"`)}),t.on("data",e=>{n.push(e)}),t.on("end",()=>{r("Request ended");const t=n.join("");let c,l;r(`Received body: "${t}"`);try{c=JSON.parse(t)}catch{const e="Invalid JSON in request body";return r(`Error: ${e}`),o.writeHead(400,e),void o.end()}if(!("url"in c)){const e="Received body does not contain a 'url' property";return r(`Error: ${e}`),o.writeHead(400,e),void o.end()}{const t=(0,i.parseOauth2Url)(c.url);if(s.Result.isFailure(t))return r(`Error: ${t.error.message}`),e.passthrough?(r("Passthrough mode: opening URL in browser"),e.openBrowser(c.url).catch(e=>{r(`Failed to open browser: ${e}`)}),o.writeHead(400,`${t.error.message}; URL opened in browser (passthrough mode)`)):o.writeHead(400,t.error.message),void o.end();l=t.value}const d=(0,a.extractPort)(l.redirect_uri);if(s.Result.isFailure(d))return r(`Error: ${d.error.message}`),e.passthrough?(r("Passthrough mode: opening URL in browser"),e.openBrowser(c.url).catch(e=>{r(`Failed to open browser: ${e}`)}),o.writeHead(400,`${d.error.message}; URL opened in browser (passthrough mode)`)):o.writeHead(400,d.error.message),void o.end();const u=d.value??80;r(`Using port number: ${u}`),e.interactiveLogin(c.url,u).then(e=>{r("Interactive login completed"),r("Sending success header"),o.writeHead(200);const t={url:e};r(`Ending response with output: "${JSON.stringify(t)}"`),o.end(JSON.stringify(t))}).catch(e=>{r(`Interactive login errored: "${JSON.stringify(e)}"`),r("Sending error header"),o.writeHead(500,JSON.stringify(e)),r("Ending response"),o.end()})})});return r("Starting credential proxy..."),t.listen(e.port,e.host),{close:()=>t.close()}}};const n=o(t(611)),i=t(876),s=t(728),a=t(51)},876:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.parseOauth2Url=function(e){if(!e)return o.Result.failure(new Error("Url parameter was undefined"));const r=["client_id","response_type","redirect_uri","scope","code_challenge","code_challenge_method"],t=new URL(e),n=Object.fromEntries(t.searchParams.entries());for(const e of r)if(!n[e])return o.Result.failure(new Error(`Missing required parameter: ${e}`));if(!["S256","plain"].includes(n.code_challenge_method))return o.Result.failure(new Error(`${n.code_challenge_method} is not valid for "code_challenge_method" property`));if(n.response_mode&&!["query","fragment","form_post"].includes(n.response_mode))return o.Result.failure(new Error(`${n.response_mode} is not valid for "response_mode" property`));if(n.prompt&&!["login","none","consent","select_account"].includes(n.prompt))return o.Result.failure(new Error(`${n.prompt} is not valid for "prompt" property`));const i={client_id:n.client_id,response_type:n.response_type,redirect_uri:n.redirect_uri,scope:n.scope,code_challenge:n.code_challenge,code_challenge_method:n.code_challenge_method,response_mode:n.response_mode,state:n.state,prompt:n.prompt,login_hint:n.login_hint,domain_hint:n.domain_hint,"x-client-SKU":n["x-client-SKU"],"x-client-VER":n["x-client-VER"],"x-client-OS":n["x-client-OS"],"x-client-CPU":n["x-client-CPU"],client_info:n.client_info};return o.Result.success(i)};const o=t(728)},896:(e,r,t)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.findAvailablePort=void 0;const o=t(611),n=t(23);r.findAvailablePort=async e=>{const t=Math.floor(64512*Math.random()+1024);return new Promise(i=>{const s=(0,o.createServer)();s.listen(t,()=>{s.close(async()=>{await(0,n.promisify)(setTimeout)(250),i(t)})}).on("error",()=>{i((0,r.findAvailablePort)(e))})})}},950:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.EnvKey=void 0,r.EnvKey={PORT:"OAUTH2_FORWARDER_PORT",SERVER:"OAUTH2_FORWARDER_SERVER",DEBUG:"OAUTH2_FORWARDER_DEBUG",PASSTHROUGH:"OAUTH2_FORWARDER_PASSTHROUGH"}}},r={};function t(o){var n=r[o];if(void 0!==n)return n.exports;var i=r[o]={exports:{}};return e[o].call(i.exports,i,i.exports,t),i.exports}return t.d=(e,r)=>{for(var o in r)t.o(r,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:r[o]})},t.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t(561)})());
|