oauth2-forwarder 1.4.0 → 1.4.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.
package/README.md CHANGED
@@ -101,7 +101,7 @@ Run your oauth2 CLI app as normal.
101
101
 
102
102
  Notes:
103
103
 
104
- - You can turn on more verbose debugging information by setting the environmental variable `OAUTH2_FORWARDER_DEBUG` to `true`. The logging on the client side is saved in `/tmp/oauth2-forwarder.log`. On the server side it is output to the console.
104
+ - You can turn on more verbose debugging information by setting the environmental variable `OAUTH2_FORWARDER_DEBUG` to `true`. See the [Debugging](#debugging) section for log file locations.
105
105
 
106
106
  ### Using a Dockerfile
107
107
 
@@ -142,8 +142,24 @@ You can enable debugging on either the server or the client by setting the envir
142
142
 
143
143
  ### Log Locations
144
144
 
145
- - **Server side**: Debug output is written to the console (stdout).
146
- - **Client side**: Debug output is saved to `/tmp/oauth2-forwarder.log`.
145
+ **Server:**
146
+ - When running in a terminal (foreground): Logs are written to the console with colored output
147
+ - When running in background (no TTY): Logs are written to a file with timestamps
148
+
149
+ **Client:**
150
+ - Always writes to a log file (since it runs in background via the browser script)
151
+
152
+ Log file locations vary by operating system:
153
+
154
+ | OS | Log Directory |
155
+ |----|---------------|
156
+ | Linux | `~/.local/state/oauth2-forwarder/` |
157
+ | macOS | `~/Library/Logs/oauth2-forwarder/` |
158
+ | Windows | `%LOCALAPPDATA%\oauth2-forwarder\logs\` |
159
+
160
+ Log files:
161
+ - Server: `o2f-server.log` (rotates on startup, keeps 5 files)
162
+ - Client: `o2f-client.log` (rotates when >5MB, keeps 3 files)
147
163
 
148
164
  ## Passthrough Mode
149
165
 
@@ -172,12 +188,19 @@ You can restrict which OAuth2 provider domains the server will accept by creatin
172
188
 
173
189
  This applies to both OAuth2 URLs and passthrough URLs (when passthrough mode is enabled).
174
190
 
175
- ### Whitelist File Location
191
+ ### Config File Location
176
192
 
177
- The whitelist is stored at:
178
- ```
179
- ~/.oauth2-forwarder/whitelist.json
180
- ```
193
+ Config file locations vary by operating system:
194
+
195
+ | OS | Config Directory |
196
+ |----|------------------|
197
+ | Linux | `~/.config/oauth2-forwarder/` |
198
+ | macOS | `~/Library/Application Support/oauth2-forwarder/` |
199
+ | Windows | `%LOCALAPPDATA%\oauth2-forwarder\` |
200
+
201
+ The whitelist file is named `whitelist.json` within the config directory.
202
+
203
+ > **Note:** The legacy location `~/.oauth2-forwarder/` is still supported for backwards compatibility, but will show a deprecation warning. Please migrate to the OS-standard location.
181
204
 
182
205
  ### File Format
183
206
 
@@ -206,11 +229,11 @@ The whitelist file is a JSON file with a `domains` array:
206
229
 
207
230
  ### Example
208
231
 
209
- To only allow Microsoft and Google OAuth:
232
+ To only allow Microsoft and Google OAuth (Linux example):
210
233
 
211
234
  ```bash
212
- mkdir -p ~/.oauth2-forwarder
213
- cat > ~/.oauth2-forwarder/whitelist.json << 'EOF'
235
+ mkdir -p ~/.config/oauth2-forwarder
236
+ cat > ~/.config/oauth2-forwarder/whitelist.json << 'EOF'
214
237
  {
215
238
  "domains": [
216
239
  "login.microsoftonline.com",
@@ -236,7 +259,7 @@ Both `o2f-server` and `o2f-client` support the following command line options:
236
259
  Example:
237
260
  ```bash
238
261
  o2f-server --version
239
- # Output: o2f-server v1.3.1
262
+ # Output: o2f-server v1.4.2
240
263
  ```
241
264
 
242
265
  ## Security
package/browser-global.sh CHANGED
@@ -1,15 +1,19 @@
1
1
  #!/usr/bin/env sh
2
2
  #
3
- # Assing this script to the BROWSER env variable via:
3
+ # Assign this script to the BROWSER env variable via:
4
4
  # export BROWSER=o2f-browser
5
5
  #
6
6
  # This will ensure requests to open a browser get forwarded
7
- # through to the proxy
7
+ # through to the proxy.
8
+ #
9
+ # Logs are written to OS-standard locations:
10
+ # Linux: ~/.local/state/oauth2-forwarder/o2f-client.log
11
+ # macOS: ~/Library/Logs/oauth2-forwarder/o2f-client.log
12
+ # Windows: %LOCALAPPDATA%\oauth2-forwarder\logs\o2f-client.log
8
13
  #
9
- LOG_FILE="/tmp/oauth2-forwarder.log"
10
14
 
11
- # fork and return 0 as some apps (e.g., az cli) expect a zero return
15
+ # fork and return 0 as some apps (e.g., az cli) expect a zero return
12
16
  # before they will launch their redirect listener
13
- o2f-client "$@" >> "$LOG_FILE" 2>&1 &
17
+ o2f-client "$@" &
14
18
 
15
19
  exit 0
package/browser.sh CHANGED
@@ -1,15 +1,19 @@
1
1
  #!/usr/bin/env sh
2
2
  #
3
- # Assing this script to the BROWSER env variable via:
3
+ # Assign this script to the BROWSER env variable via:
4
4
  # export BROWSER=~/o2f/browser.sh
5
5
  #
6
6
  # This will ensure requests to open a browser get forwarded
7
- # through to the proxy
7
+ # through to the proxy.
8
+ #
9
+ # Logs are written to OS-standard locations:
10
+ # Linux: ~/.local/state/oauth2-forwarder/o2f-client.log
11
+ # macOS: ~/Library/Logs/oauth2-forwarder/o2f-client.log
12
+ # Windows: %LOCALAPPDATA%\oauth2-forwarder\logs\o2f-client.log
8
13
  #
9
- LOG_FILE="/tmp/oauth2-forwarder.log"
10
14
 
11
- # fork and return 0 as some apps (e.g., az cli) expect a zero return
15
+ # fork and return 0 as some apps (e.g., az cli) expect a zero return
12
16
  # before they will launch their redirect listener
13
- node ~/o2f/o2f-client.js "$@" >> "$LOG_FILE" 2>&1 &
17
+ node ~/o2f/o2f-client.js "$@" &
14
18
 
15
19
  exit 0
@@ -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 o=await e.credentialForwarder(r);if(t(`Credential forwarding completed with result type: ${o.type}`),"error"===o.type)return t(`Error during credential forwarding: ${o.message}`),void e.onExit.failure();t("Exiting on success..."),e.onExit.success()}catch(r){t(`Browser helper error "${r}"`),t("Exiting on failure..."),e.onExit.failure()}}}},330:e=>{e.exports=JSON.parse('{"name":"oauth2-forwarder","version":"1.4.0","description":"utilities for forwarding oauth2 interactive flow (e.g. container to host)","main":"dist/index.js","bin":{"o2f-server":"./dist/o2f-server.js","o2f-client":"./dist/o2f-client.js","o2f-browser":"./browser-global.sh"},"scripts":{"build":"rimraf ./dist && webpack && node prepend-shebang.js","zip-release":"./release.sh","test":"jest --watch","e2e-test":"jest -c ./jest.e2e.config.js","lint":"eslint .","fix-formatting":"prettier --write --config ./prettier.config.js ./src/","check-formatting":"prettier --check --config ./prettier.config.js ./src/"},"author":"Sam Davidoff","license":"MIT","repository":{"type":"git","url":"https://github.com/sam-mfb/oauth2-forwarder.git"},"files":["dist","browser.sh","browser-global.sh"],"keywords":["oauth2","docker","container","authentication","browser"],"engines":{"node":">=24.0.0"},"devDependencies":{"@eslint/js":"^9.25.1","@types/eslint__js":"^8.42.3","@types/jest":"^30.0.0","@types/node":"^24.0.0","eslint":"^9.25.1","jest":"^30.2.0","prettier":"^3.5.3","rimraf":"^6.0.1","ts-jest":"^29.3.2","ts-loader":"^9.5.2","typescript":"^5.8.3","typescript-eslint":"^8.31.1","webpack":"^5.99.7","webpack-cli":"^6.0.1"},"dependencies":{"open":"^11.0.0"},"overrides":{"glob@>=10.2.0 <=10.4.5":"10.5.0","glob@>=11.0.0 <=11.0.3":"11.1.0","js-yaml@<3.14.2":"3.14.2","js-yaml@>=4.0.0 <4.1.1":"4.1.1"}}')},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 async t=>{r(`Starting credential forwarding for url: "${t}"`);const{redirectUrl:o,requestId:n}=await(t=>new Promise((o,n)=>{const i={path:"/",method:"POST"};i.port=e.port,i.host=e.host;const u=s.default.request(i,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}"`),n(e)}),e.on("end",()=>{const{statusCode:s,statusMessage:i}=e;if(r(`Status: ${s??"No Code"}-${i??"No message"}`),200!==s)return void n(`Http request failed: Status: ${s??"No Code"}-${i??"No message"}`);r(`Final output: "${t}"`);const u=JSON.parse(t);"url"in u?"requestId"in u?o({redirectUrl:u.url,requestId:u.requestId}):n("Response did not contain 'requestId' property"):n("Response did not contain 'url' property")}),e.on("close",()=>{r("Response closed")})});u.on("error",e=>{r(`Request error: "${e}"`),n(e)});const c={url:t},a=JSON.stringify(c);r(`Sending request body: "${a}"`),u.write(a),r("Ending request"),u.end()}))(t);r(`Received redirectUrl: "${o}", requestId: "${n}"`),r(`Performing redirect to: "${o}"`);const i=await e.redirect(o);var u;return r(`Redirect result type: "${i.type}"`),r(`Sending completion report for requestId: "${n}"`),await(u={requestId:n,result:i},new Promise((t,o)=>{const n={path:"/complete",method:"POST"};n.port=e.port,n.host=e.host;const i=s.default.request(n,e=>{e.on("error",e=>{r(`Completion response error: "${e}"`),o(e)}),e.on("end",()=>{const{statusCode:s,statusMessage:n}=e;r(`Completion status: ${s??"No Code"}-${n??"No message"}`),200===s?t():o(`Completion request failed: Status: ${s??"No Code"}-${n??"No message"}`)}),e.on("data",()=>{})});i.on("error",e=>{r(`Completion request error: "${e}"`),o(e)});const c=JSON.stringify(u);r(`Sending completion report: "${c}"`),i.write(c),i.end()})),r("Completion report sent successfully"),i}};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:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:""},o=""},541: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.getVersion=function(){return s.default.version};const s=o(t(330))},580:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.LOOPBACK_PATTERNS=void 0,r.isLoopbackUrl=function(e){return r.LOOPBACK_PATTERNS.some(r=>r.test(e))},r.convertLoopbackUrl=function(e,r){return e.replace(/^(http:\/\/)localhost(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)127\.0\.0\.1(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)\[::1\](:\d+)?/,`$1${r}$2`)},r.LOOPBACK_PATTERNS=[/^http:\/\/localhost(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/127\.0\.0\.1(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/\[::1\](?::(\d{1,5}))?(?:\/.*)?$/]},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:()=>{},t=e=>new Promise((t,o)=>{const n=setTimeout(()=>{o(new Error(`Request timed out after ${u}ms`))},u);s.default.get(e,e=>{clearTimeout(n);const r=[];e.on("data",e=>{r.push(e)}),e.on("end",()=>{const o=Buffer.concat(r).toString("utf8"),s=e.headers.location;t({statusCode:e.statusCode??0,location:s,body:o})}),e.on("error",e=>{o(e)})}).on("error",e=>{clearTimeout(n),r(`Received error "${JSON.stringify(e)}"`),o(e)})}),o=async(e,s)=>{if(r(`Making GET request to url: "${e}" (${s} redirects remaining)`),s<=0)return{type:"error",message:`Exceeded maximum number of redirects (${i})`};let u;try{u=await(async e=>{if((0,n.isLoopbackUrl)(e)){const o=(0,n.convertLoopbackUrl)(e,"127.0.0.1"),s=(0,n.convertLoopbackUrl)(e,"[::1]");r("Loopback URL detected, will try IPv4 then IPv6");try{return r(`Trying IPv4: ${o}`),await t(o)}catch(e){const o=e;if("ECONNREFUSED"===o.code){r(`IPv4 connection refused, trying IPv6: ${s}`);try{return await t(s)}catch(e){const r=e;throw new Error(`Connection refused on both IPv4 and IPv6. IPv4 error: ${o.message}, IPv6 error: ${r.message}`)}}throw e}}return await t(e)})(e)}catch(e){return{type:"error",message:e instanceof Error?e.message:String(e)}}if(r(`Received status ${u.statusCode}`),301===u.statusCode||302===u.statusCode){const e=u.location;return e?(r(`Redirect to: ${e}`),(0,n.isLoopbackUrl)(e)?(r("Following localhost redirect"),o(e,s-1)):{type:"redirect",location:e}):{type:"error",message:`Received ${u.statusCode} without Location header`}}return 200===u.statusCode?(r(`Success with body length: ${u.body.length}`),{type:"success",body:u.body||void 0}):{type:"error",message:`Request returned unexpected status: ${u.statusCode}`}};return e=>o(e,i)};const s=o(t(611)),n=t(580),i=10,u=3e4},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",LOGIN_TIMEOUT:"OAUTH2_FORWARDER_LOGIN_TIMEOUT"}}},r={};function t(o){var s=r[o];if(void 0!==s)return s.exports;var n=r[o]={exports:{}};return e[o].call(n.exports,n,n.exports,t),n.exports}var o={};return(()=>{var e=o;Object.defineProperty(e,"__esModule",{value:!0});const r=t(541),s=t(950),n=t(76),i=t(728),u=t(113),c=t(472),a=t(737),d=t(377);(process.argv.includes("--version")||process.argv.includes("-v"))&&(console.log(`o2f-client v${(0,r.getVersion)()}`),process.exit(0));const l=process.env[s.EnvKey.DEBUG],p=(0,n.buildOutputWriter)({color:"red",stream:process.stderr}),f=process.env[s.EnvKey.SERVER];f||(p(`The environmental variable ${[s.EnvKey.SERVER]} was not defined`),process.exit(1));const g=(0,d.parseServerInfo)(f);i.Result.isFailure(g)&&(p(`Invalid server info: "${g.error.message}"`),process.exit(1));const v=g.value,m=(0,a.buildRedirect)({debugger:l?(0,n.buildOutputWriter)({color:"yellow",stream:process.stderr}):void 0}),y=(0,c.buildCredentialForwarder)({host:v.host,port:v.port,redirect:m,debugger:l?(0,n.buildOutputWriter)({color:"cyan",stream:process.stderr}):void 0});(0,u.buildBrowserHelper)({onExit:{success:function(){process.exit(0)},failure:function(){process.exit(1)}},credentialForwarder:y,debugger:l?(0,n.buildOutputWriter)({color:"green",stream:process.stderr}):void 0})(process.argv[2]).catch(p)})(),o})());
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={113(e,r,t){Object.defineProperty(r,"__esModule",{value:!0}),r.buildBrowserHelper=function(e){return async r=>{const{logger:t}=e;if(!r)return t.warn("No URL argument present"),void e.onExit.failure();t.debug(`Received url "${r}"`);const n=(0,o.getDomain)(r)??"unknown";try{const o=await e.credentialForwarder(r);if(t.info(`Completed request for ${n}`),t.debug(`Result type: ${o.type}`),"error"===o.type)return t.error(`Error during credential forwarding: ${o.message}`),void e.onExit.failure();t.debug("Exiting on success"),e.onExit.success()}catch(r){t.error(`Browser helper error: ${r}`),e.onExit.failure()}}};const o=t(512)},472(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{logger:r}=e;return async t=>{r.debug(`Starting credential forwarding for URL: "${t}"`);const{redirectUrl:o,requestId:s}=await(t=>new Promise((o,s)=>{const i={path:"/",method:"POST"};i.port=e.port,i.host=e.host;const a=n.default.request(i,e=>{let t="";e.setEncoding("utf8"),e.on("data",e=>{r.debug(`Data chunk received: "${e}"`),t+=e}),e.on("error",e=>{r.error(`Response error: ${e}`),s(e)}),e.on("end",()=>{const{statusCode:n,statusMessage:i}=e;if(r.debug(`Status: ${n??"No Code"}-${i??"No message"}`),200!==n)return void s(`Http request failed: Status: ${n??"No Code"}-${i??"No message"}`);r.debug(`Final output: "${t}"`);const a=JSON.parse(t);"url"in a?"requestId"in a?o({redirectUrl:a.url,requestId:a.requestId}):s("Response did not contain 'requestId' property"):s("Response did not contain 'url' property")}),e.on("close",()=>{r.debug("Response closed")})});a.on("error",e=>{r.error(`Request error: ${e}`),s(e)});const u={url:t},c=JSON.stringify(u);r.debug(`Sending request body: "${c}"`),a.write(c),r.debug("Ending request"),a.end()}))(t);r.debug(`Received redirectUrl: "${o}", requestId: "${s}"`),r.debug(`Performing redirect to: "${o}"`);const i=await e.redirect(o);var a;return r.debug(`Redirect result type: "${i.type}"`),r.debug(`Sending completion report for requestId: "${s}"`),await(a={requestId:s,result:i},new Promise((t,o)=>{const s={path:"/complete",method:"POST"};s.port=e.port,s.host=e.host;const i=n.default.request(s,e=>{e.on("error",e=>{r.error(`Completion response error: ${e}`),o(e)}),e.on("end",()=>{const{statusCode:n,statusMessage:s}=e;r.debug(`Completion status: ${n??"No Code"}-${s??"No message"}`),200===n?t():o(`Completion request failed: Status: ${n??"No Code"}-${s??"No message"}`)}),e.on("data",()=>{})});i.on("error",e=>{r.error(`Completion request error: ${e}`),o(e)});const u=JSON.stringify(a);r.debug(`Sending completion report: "${u}"`),i.write(u),i.end()})),r.debug("Completion report sent successfully"),i}};const n=o(t(611))},737(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{logger:r}=e,t=(e,t)=>new Promise((o,s)=>{const i=setTimeout(()=>{s(new Error("Request timed out after 30000ms"))},3e4),a=new URL(e),u={hostname:a.hostname.replace(/^\[|\]$/g,""),port:a.port||80,path:a.pathname+a.search,method:"GET"};t&&(u.headers={Host:t});const c=n.default.request(u,e=>{clearTimeout(i);const r=[];e.on("data",e=>{r.push(e)}),e.on("end",()=>{const t=Buffer.concat(r).toString("utf8"),n=e.headers.location;o({statusCode:e.statusCode??0,location:n,body:t})}),e.on("error",e=>{s(e)})});c.on("error",e=>{clearTimeout(i),r.error(`Request error: ${JSON.stringify(e)}`),s(e)}),c.end()}),o=async(e,n)=>{if(r.debug(`Making GET request to url: "${e}" (${n} redirects remaining)`),n<=0)return{type:"error",message:"Exceeded maximum number of redirects (10)"};let i;try{i=await(async e=>{if((0,s.isLoopbackUrl)(e)){const o=new URL(e).host,n=(0,s.convertLoopbackUrl)(e,"127.0.0.1"),i=(0,s.convertLoopbackUrl)(e,"[::1]");r.debug("Loopback URL detected, will try IPv4 then IPv6"),r.debug(`Preserving original Host header: ${o}`);try{return r.debug(`Trying IPv4: ${n}`),await t(n,o)}catch(e){const n=e;if("ECONNREFUSED"===n.code){r.debug(`IPv4 connection refused, trying IPv6: ${i}`);try{return await t(i,o)}catch(e){const r=e;throw new Error(`Connection refused on both IPv4 and IPv6. IPv4 error: ${n.message}, IPv6 error: ${r.message}`)}}throw e}}return await t(e)})(e)}catch(e){return{type:"error",message:e instanceof Error?e.message:String(e)}}if(r.debug(`Received status ${i.statusCode}`),301===i.statusCode||302===i.statusCode){const e=i.location;return e?(r.debug(`Redirect to: ${e}`),(0,s.isLoopbackUrl)(e)?(r.debug("Following localhost redirect"),o(e,n-1)):{type:"redirect",location:e}):{type:"error",message:`Received ${i.statusCode} without Location header`}}return 200===i.statusCode?(r.debug(`Success with body length: ${i.body.length}`),{type:"success",body:i.body||void 0}):{type:"error",message:`Request returned unexpected status: ${i.statusCode}`}};return e=>o(e,10)};const n=o(t(611)),s=t(580)},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 n=parseInt(t,10);return isNaN(n)||n<0||n>65535?o.Result.failure(new Error(`Not a valid port: ${n}`)):o.Result.success({host:e,port:n})}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)},478(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.color=function(e,r){return`${t[r]}${e}`};const t={black:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:""}},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",LOGIN_TIMEOUT:"OAUTH2_FORWARDER_LOGIN_TIMEOUT"}},465(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.rotateOnLaunch=a,r.rotateIfNeeded=function(e,r=5242880,t=3){if((0,i.ensureDirectory)(s.default.dirname(e)),!n.default.existsSync(e))return!1;try{if(n.default.statSync(e).size<r)return!1}catch{return!1}return a(e,t),!0},r.createLogFileStream=function(e){return(0,i.ensureDirectory)(s.default.dirname(e)),n.default.createWriteStream(e,{flags:"a"})};const n=o(t(896)),s=o(t(928)),i=t(125);function a(e,r=5){if((0,i.ensureDirectory)(s.default.dirname(e)),!n.default.existsSync(e))return;const t=`${e}.${r}`;n.default.existsSync(t)&&n.default.unlinkSync(t);for(let t=r-1;t>=1;t--){const r=`${e}.${t}`,o=`${e}.${t+1}`;n.default.existsSync(r)&&n.default.renameSync(r,o)}n.default.renameSync(e,`${e}.1`)}},741(e,r,t){Object.defineProperty(r,"__esModule",{value:!0}),r.buildLogger=function(e){const{level:r,stream:t=process.stderr,prefix:a="",useFileFormat:u=!1}=e,c=s[r],d=(e,r)=>{if(s[e]>=c){const s=a?`[${a}] `:"",c=`[${e.toUpperCase()}]`,d=(0,n.sanitize)(r);let l;if(u)l=`${(new Date).toISOString()} ${s}${c} ${d}\n`;else{const r=i[e];l=(0,o.color)(`${s}${c} ${d}`,r)+"\n"}t.write(l)}};return{error:e=>d("error",e),warn:e=>d("warn",e),info:e=>d("info",e),debug:e=>d("debug",e)}};const o=t(478),n=t(638),s={debug:0,info:1,warn:2,error:3},i={error:"red",warn:"yellow",info:"cyan",debug:"magenta"}},580(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.LOOPBACK_PATTERNS=void 0,r.isLoopbackUrl=function(e){return r.LOOPBACK_PATTERNS.some(r=>r.test(e))},r.convertLoopbackUrl=function(e,r){return e.replace(/^(http:\/\/)localhost(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)127\.0\.0\.1(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)\[::1\](:\d+)?/,`$1${r}$2`)},r.LOOPBACK_PATTERNS=[/^http:\/\/localhost(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/127\.0\.0\.1(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/\[::1\](?::(\d{1,5}))?(?:\/.*)?$/]},125(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.ensureDirectory=function(e){n.default.existsSync(e)||n.default.mkdirSync(e,{recursive:!0})},r.getLogFilePath=function(e){const r=u();return s.default.join(r,`o2f-${e}.log`)},r.getLogDir=function(){return u()},r.getConfigDir=function(){return c()},r.getLegacyConfigDir=function(){return d()},r.resolveConfigFile=function(e){const r=s.default.join(d(),e),t=s.default.join(c(),e);return n.default.existsSync(r)?{path:r,isLegacy:!0}:{path:t,isLegacy:!1}},r.getPreferredConfigDescription=function(){switch(i.default.platform()){case"darwin":return"~/Library/Application Support/oauth2-forwarder/";case"win32":return"%LOCALAPPDATA%\\oauth2-forwarder\\";default:return"~/.config/oauth2-forwarder/ (or $XDG_CONFIG_HOME/oauth2-forwarder/)"}};const n=o(t(896)),s=o(t(928)),i=o(t(857)),a="oauth2-forwarder";function u(){const e=i.default.platform(),r=i.default.homedir();switch(e){case"darwin":return s.default.join(r,"Library","Logs",a);case"win32":{const e=process.env.LOCALAPPDATA||s.default.join(r,"AppData","Local");return s.default.join(e,a,"logs")}default:{const e=process.env.XDG_STATE_HOME||s.default.join(r,".local","state");return s.default.join(e,a)}}}function c(){const e=i.default.platform(),r=i.default.homedir();switch(e){case"darwin":return s.default.join(r,"Library","Application Support",a);case"win32":{const e=process.env.LOCALAPPDATA||s.default.join(r,"AppData","Local");return s.default.join(e,a)}default:{const e=process.env.XDG_CONFIG_HOME||s.default.join(r,".config");return s.default.join(e,a)}}}function d(){return s.default.join(i.default.homedir(),".oauth2-forwarder")}},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}},638(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.sanitize=function(e){return e.replace(/((code|code_challenge)=)([^"&\n]*)/gi,(e,r)=>`${r}********`)}},512(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.getDomain=function(e){try{return new URL(e).hostname.toLowerCase()}catch{return null}}},541(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.getVersion=function(){return n.default.version};const n=o(t(330))},896(e){e.exports=require("fs")},611(e){e.exports=require("http")},857(e){e.exports=require("os")},928(e){e.exports=require("path")},330(e){e.exports=JSON.parse('{"name":"oauth2-forwarder","version":"1.4.3","description":"utilities for forwarding oauth2 interactive flow (e.g. container to host)","main":"dist/index.js","bin":{"o2f-server":"./dist/o2f-server.js","o2f-client":"./dist/o2f-client.js","o2f-browser":"./browser-global.sh"},"scripts":{"build":"rimraf ./dist && webpack && node prepend-shebang.js","zip-release":"./release.sh","test":"vitest","e2e-test":"vitest --config vitest.e2e.config.ts","lint":"eslint .","fix-formatting":"prettier --write --config ./prettier.config.js ./src/","check-formatting":"prettier --check --config ./prettier.config.js ./src/"},"author":"Sam Davidoff","license":"MIT","repository":{"type":"git","url":"https://github.com/sam-mfb/oauth2-forwarder.git"},"files":["dist","browser.sh","browser-global.sh"],"keywords":["oauth2","docker","container","authentication","browser"],"engines":{"node":">=24.0.0"},"devDependencies":{"@eslint/js":"^9.25.1","@types/eslint__js":"^8.42.3","@types/node":"^24.0.0","eslint":"^9.25.1","prettier":"^3.5.3","rimraf":"^6.0.1","ts-loader":"^9.5.2","typescript":"^5.8.3","typescript-eslint":"^8.31.1","vitest":"^3.0.5","webpack":"^5.99.7","webpack-cli":"^6.0.1"},"dependencies":{"nanoid":"^5.1.5","open":"^11.0.0"},"overrides":{"glob@>=10.2.0 <=10.4.5":"10.5.0","glob@>=11.0.0 <=11.0.3":"11.1.0","js-yaml@<3.14.2":"3.14.2","js-yaml@>=4.0.0 <4.1.1":"4.1.1"}}')}},r={};function t(o){var n=r[o];if(void 0!==n)return n.exports;var s=r[o]={exports:{}};return e[o].call(s.exports,s,s.exports,t),s.exports}var o={};return(()=>{var e=o;Object.defineProperty(e,"__esModule",{value:!0});const r=t(541),n=t(950),s=t(741),i=t(125),a=t(465),u=t(728),c=t(512),d=t(113),l=t(472),f=t(737),p=t(377);(process.argv.includes("--version")||process.argv.includes("-v"))&&(console.log(`o2f-client v${(0,r.getVersion)()}`),process.exit(0));const g=process.env[n.EnvKey.DEBUG]?"debug":"info",h=(0,i.getLogFilePath)("client");(0,a.rotateIfNeeded)(h,5242880,3);const m=(0,a.createLogFileStream)(h),y=(0,s.buildLogger)({level:g,stream:m,prefix:"client",useFileFormat:!0}),v=process.env[n.EnvKey.SERVER];v||(y.error(`The environmental variable ${[n.EnvKey.SERVER]} was not defined`),process.exit(1));const b=(0,p.parseServerInfo)(v);u.Result.isFailure(b)&&(y.error(`Invalid server info: "${b.error.message}"`),process.exit(1));const w=b.value,$=(0,f.buildRedirect)({logger:y}),_=(0,l.buildCredentialForwarder)({host:w.host,port:w.port,redirect:$,logger:y}),R=(0,d.buildBrowserHelper)({onExit:{success:function(){process.exit(0)},failure:function(){process.exit(1)}},credentialForwarder:_,logger:y}),O=process.argv[2];y.info(`Processing request for ${O?(0,c.getDomain)(O)??"(invalid URL)":"(none)"}`),y.debug(`Full URL: ${O??"(none)"}`),R(O).catch(e=>y.error(String(e)))})(),o})());
@@ -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){let r=null;for(const t of n.LOOPBACK_PATTERNS)if(r=e.match(t),r)break;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),n=t(580)},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)},330:e=>{e.exports=JSON.parse('{"name":"oauth2-forwarder","version":"1.4.0","description":"utilities for forwarding oauth2 interactive flow (e.g. container to host)","main":"dist/index.js","bin":{"o2f-server":"./dist/o2f-server.js","o2f-client":"./dist/o2f-client.js","o2f-browser":"./browser-global.sh"},"scripts":{"build":"rimraf ./dist && webpack && node prepend-shebang.js","zip-release":"./release.sh","test":"jest --watch","e2e-test":"jest -c ./jest.e2e.config.js","lint":"eslint .","fix-formatting":"prettier --write --config ./prettier.config.js ./src/","check-formatting":"prettier --check --config ./prettier.config.js ./src/"},"author":"Sam Davidoff","license":"MIT","repository":{"type":"git","url":"https://github.com/sam-mfb/oauth2-forwarder.git"},"files":["dist","browser.sh","browser-global.sh"],"keywords":["oauth2","docker","container","authentication","browser"],"engines":{"node":">=24.0.0"},"devDependencies":{"@eslint/js":"^9.25.1","@types/eslint__js":"^8.42.3","@types/jest":"^30.0.0","@types/node":"^24.0.0","eslint":"^9.25.1","jest":"^30.2.0","prettier":"^3.5.3","rimraf":"^6.0.1","ts-jest":"^29.3.2","ts-loader":"^9.5.2","typescript":"^5.8.3","typescript-eslint":"^8.31.1","webpack":"^5.99.7","webpack-cli":"^6.0.1"},"dependencies":{"open":"^11.0.0"},"overrides":{"glob@>=10.2.0 <=10.4.5":"10.5.0","glob@>=11.0.0 <=11.0.3":"11.1.0","js-yaml@<3.14.2":"3.14.2","js-yaml@>=4.0.0 <4.1.1":"4.1.1"}}')},478:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.color=function(e,r){return`${t[r]}${e}${o}`};const t={black:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:""},o=""},515:(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))})})}},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.loginTimeoutMs??s,t=e.debugger?e.debugger:()=>{};return async(o,s)=>new Promise((a,c)=>{const l=i.default.randomUUID();let d;t(`Generated requestId: ${l}`);const u=n.default.createServer((e,r)=>{d&&(clearTimeout(d),d=void 0),t(`Received a ${e.method??"undefined"} request`),e.on("error",e=>{t(`Error: ${JSON.stringify(e)}`),t("Terminating request on error"),r.writeHead(500,JSON.stringify(e)),t("Ending response"),r.end(),t("Closing temporary redirect server on error"),u.close(),c(e)}),e.on("data",e=>{t(`Received data chunk: ${e}`)}),e.on("close",()=>{t("Redirect request closed")}),e.on("end",()=>{if(t("Request ended"),!e.headers.host){const e="Missing Host header in redirect request";return t(`Error: ${e}`),r.writeHead(400,e),r.end(),u.close(),void c(new Error(e))}const o="http://"+e.headers.host+e.url;t(`Received callback url: "${o}"`),t("Holding browser response pending completion"),t("Closing listening socket (keeping response connection open)"),u.close(),a({callbackUrl:o,requestId:l,complete:e=>{switch(t(`Completing request ${l} with result type: ${e.type}`),e.type){case"redirect":t(`Redirecting browser to: ${e.location}`),r.writeHead(302,{Location:e.location}),r.end();break;case"success":t("Sending success response to browser"),r.writeHead(200,{"Content-Type":"text/html"}),e.body?r.end(e.body):r.end("Authentication completed. You may close this page now.");break;case"error":t(`Sending error response to browser: ${e.message}`),r.writeHead(500,{"Content-Type":"text/html"}),r.end(`Authentication failed: ${e.message}`)}}})})});t(`Starting temporary redirect server on port ${s}...`),u.listen(s,"127.0.0.1",()=>{t("Temporary redirect server is listening"),t(`Setting login timeout: ${r}ms`),d=setTimeout(()=>{t(`Login timeout exceeded (${r}ms), closing server on port ${s}`),u.close(),c(new Error(`Login timeout: user did not complete OAuth2 flow within ${r}ms`))},r),t("Opening browser for interactive login..."),e.openBrowser(o)})})};const n=o(t(611)),i=o(t(982)),s=3e5},541: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.getVersion=function(){return n.default.version};const n=o(t(330))},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(541),i=t(950),s=t(76),a=t(516),c=t(802),l=t(515),d=t(564),u=o(t(667));(process.argv.includes("--version")||process.argv.includes("-v"))&&(console.log(`o2f-server v${(0,n.getVersion)()}`),process.exit(0));const p=process.env[i.EnvKey.DEBUG],m="true"===process.env[i.EnvKey.PASSTHROUGH],f="127.0.0.1";let g;const h=process.env[i.EnvKey.LOGIN_TIMEOUT];if(h){const e=parseInt(h);!isNaN(e)&&e>0&&(g=1e3*e)}const w=(0,s.buildOutputWriter)({color:"cyan",stream:process.stdout}),y=(0,s.buildOutputWriter)({color:"yellow",stream:process.stdout}),v=(0,s.buildOutputWriter)({color:"white",stream:process.stdout}),b=(0,s.buildOutputWriter)({color:"red",stream:process.stderr});let _=null;const S=process.env[i.EnvKey.PORT];if(S){const e=parseInt(S);isNaN(e)||(_=e)}const x=async e=>{await(0,u.default)(e)},E=(0,a.buildInteractiveLogin)({openBrowser:x,debugger:p?(0,s.buildOutputWriter)({color:"magenta",stream:process.stdout}):void 0,loginTimeoutMs:g});(async()=>{_&&w(`Attempting to use user specified port ${_}`);const e=_??await(0,l.findAvailablePort)(f),r=(0,d.loadWhitelist)();r.enabled?w(`URL whitelist enabled with ${r.domains.size} domain(s): ${Array.from(r.domains).join(", ")}`):w(`URL whitelist disabled (no whitelist file at ${r.configPath})`);const t=(0,c.buildCredentialProxy)({host:f,port:e,interactiveLogin:E,openBrowser:x,passthrough:m,debugger:p?(0,s.buildOutputWriter)({color:"green",stream:process.stdout}):void 0,pendingRequestTtlMs:g,whitelist:r,logger:w});w(`Starting TCP server listening on ${f}:${e}`),m&&w("Passthrough mode enabled: malformed URLs will be opened in browser"),y("Run the following command in your docker container:\n"),v(` export ${i.EnvKey.SERVER}="host.docker.internal:${e}"\n`),y("\nIn addition, you need to set the BROWSER env variable to point to the client script in the docker container.\n"),y("If you installed via npm (recommended):\n"),v(" export BROWSER=o2f-browser\n"),y("If you installed manually to the default location:\n"),v(" export BROWSER=~/o2f/browser.sh\n");try{await t(),w("Ctrl+c to stop server.")}catch(e){b(JSON.stringify(e)),process.exit(1)}})().catch(e=>{b(JSON.stringify(e)),process.exit(1)})},564: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.loadWhitelist=function(){const e=i.default.join(s.default.homedir(),a,c);if(!n.default.existsSync(e))return{enabled:!1,domains:new Set,configPath:e};try{const r=n.default.readFileSync(e,"utf-8"),t=JSON.parse(r);if(!Array.isArray(t.domains)||0===t.domains.length)return{enabled:!1,domains:new Set,configPath:e};const o=new Set(t.domains.map(e=>e.toLowerCase().trim()).filter(e=>e.length>0));return{enabled:o.size>0,domains:o,configPath:e}}catch{return{enabled:!1,domains:new Set,configPath:e}}},r.isUrlAllowed=function(e,r){if(!r.enabled)return!0;try{const t=new URL(e).hostname.toLowerCase();return r.domains.has(t)}catch{return!1}},r.getHostnameFromUrl=function(e){try{return new URL(e).hostname.toLowerCase()}catch{return null}};const n=o(t(896)),i=o(t(928)),s=o(t(857)),a=".oauth2-forwarder",c="whitelist.json"},580:(e,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.LOOPBACK_PATTERNS=void 0,r.isLoopbackUrl=function(e){return r.LOOPBACK_PATTERNS.some(r=>r.test(e))},r.convertLoopbackUrl=function(e,r){return e.replace(/^(http:\/\/)localhost(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)127\.0\.0\.1(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)\[::1\](:\d+)?/,`$1${r}$2`)},r.LOOPBACK_PATTERNS=[/^http:\/\/localhost(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/127\.0\.0\.1(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/\[::1\](?::(\d{1,5}))?(?:\/.*)?$/]},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:()=>F});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"),h=require("node:buffer"),w=(0,g.promisify)(s.execFile),y=()=>`${o.env.SYSTEMROOT||o.env.windir||String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,v=async(e,r={})=>{const{powerShellPath:t,...o}=r,n=v.encodeCommand(e);return w(t??y(),[...v.argumentsPrefix,n],{encoding:"utf8",...o})};v.argumentsPrefix=["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand"],v.encodeCommand=e=>h.Buffer.from(e,"utf16le").toString("base64"),v.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`:y;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 O=(0,g.promisify)(s.execFile),P={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(P));class $ extends Error{}const T=(0,g.promisify)(s.execFile),A=(0,g.promisify)(s.execFile);async function U(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 A("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 C=(0,g.promisify)(s.execFile);const q=Boolean(o.env.SSH_CONNECTION||o.env.SSH_CLIENT||o.env.SSH_TTY),L=Symbol("fallbackAttempt"),M=n.dirname((0,i.fileURLToPath)("file:///home/runner/work/oauth2-forwarder/oauth2-forwarder/node_modules/open/index.js")),H=n.join(M,"xdg-open"),{platform:j,arch:I}=o,B=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})[L];if(delete e[L],Array.isArray(e.app))return B(e.app,r=>N({...e,app:r,[L]:!0}));let t,{name:n,arguments:i=[]}=e.app??{};if(i=[...i],Array.isArray(n))return B(n,r=>N({...e,app:{name:r,arguments:i},[L]:!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 v(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 T("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 U(e),id:e}}if("linux"===o.platform){const{stdout:e}=await C("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=O){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 $(`Cannot find Windows browser in stdout: ${JSON.stringify(r)}`);const{id:o}=t.groups,n=P[o];if(!n)throw new $(`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()||q||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"===j)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"===j||d){t=await S(),c.push(...v.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(v.escapeArgument(n)),e.target&&i.push(e.target)):e.target&&r.push(v.escapeArgument(e.target)),i.length>0&&(i=i.map(e=>v.escapeArgument(e)),r.push("-ArgumentList",i.join(","))),e.target=v.encodeCommand(r.join(" ")),e.wait||(l.stdio="ignore")}else{if(n)t=n;else{const e=!M||"/"===M;let r=!1;try{await a.access(H,a.constants.X_OK),r=!0}catch{}t=o.versions.electron??("android"===j||e||!r)?"xdg-open":H}i.length>0&&c.push(...i),e.wait||(l.stdio="ignore",l.detached=!0)}"darwin"===j&&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)})}))},F=(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 k(e){if("string"==typeof e||Array.isArray(e))return e;const{[I]:r}=e;if(!r)throw new Error(`${I} is not supported`);return r}function W({[j]:e},{wsl:r}={}){if(r&&f)return k(r);if(!e)throw new Error(`${j} is not supported`);return k(e)}const D={browser:"browser",browserPrivate:"browserPrivate"};E(D,"chrome",()=>W({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",()=>W({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",()=>W({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",()=>W({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",()=>W({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=e.pendingRequestTtlMs??l,o=new Map,d=(t,o,n)=>{if((0,c.isUrlAllowed)(t,e.whitelist))return!1;const i=`Domain '${(0,c.getHostnameFromUrl)(t)??"unknown"}' is not in the whitelist`,s=n?"Whitelist rejection (passthrough)":"Whitelist rejection";return e.logger(`${s}: ${i}`),r(`Error: ${i}`),o.writeHead(403,i),o.end(),!0},u=n.default.createServer((e,t)=>{r(`Request received at ${e.headers.host}${e.url??"/"}`);const o=[];e.on("close",()=>{r("Request closed.")}),e.on("error",e=>{r(`Request received error "${e}"`)}),e.on("data",e=>{o.push(e)}),e.on("end",()=>{r("Request ended");const n=o.join("");r(`Received body: "${n}"`),"/complete"===(e.url??"/")?p(n,t):m(n,t)})}),p=(e,t)=>{let n;r("Handling /complete request");try{n=JSON.parse(e)}catch{const e="Invalid JSON in completion request body";return r(`Error: ${e}`),t.writeHead(400,e),void t.end()}if(!n.requestId||!n.result){const e="Completion request missing requestId or result";return r(`Error: ${e}`),t.writeHead(400,e),void t.end()}const i=o.get(n.requestId);if(!i){const e=`No pending request found for requestId: ${n.requestId}`;return r(`Error: ${e}`),t.writeHead(404,e),void t.end()}clearTimeout(i.timeoutId),o.delete(n.requestId),r(`Completing request ${n.requestId} with result type: ${n.result.type}`),i.complete(n.result),t.writeHead(200),t.end()},m=(n,c)=>{let l,u;try{l=JSON.parse(n)}catch{const e="Invalid JSON in request body";return r(`Error: ${e}`),c.writeHead(400,e),void c.end()}if(!("url"in l)){const e="Received body does not contain a 'url' property";return r(`Error: ${e}`),c.writeHead(400,e),void c.end()}{const t=(0,i.parseOauth2Url)(l.url);if(s.Result.isFailure(t)){if(r(`Error: ${t.error.message}`),e.passthrough){if(d(l.url,c,!0))return;r("Passthrough mode: opening URL in browser"),e.openBrowser(l.url).catch(e=>{r(`Failed to open browser: ${e}`)}),c.writeHead(400,`${t.error.message}; URL opened in browser (passthrough mode)`)}else c.writeHead(400,t.error.message);return void c.end()}if(u=t.value,u.code_challenge||r("OAuth2 request does not include PKCE parameters"),d(l.url,c,!1))return}const p=(0,a.extractPort)(u.redirect_uri);if(s.Result.isFailure(p)){if(r(`Error: ${p.error.message}`),e.passthrough){if(d(l.url,c,!0))return;r("Passthrough mode: opening URL in browser"),e.openBrowser(l.url).catch(e=>{r(`Failed to open browser: ${e}`)}),c.writeHead(400,`${p.error.message}; URL opened in browser (passthrough mode)`)}else c.writeHead(400,p.error.message);return void c.end()}const m=p.value??80;r(`Using port number: ${m}`),e.interactiveLogin(l.url,m).then(({callbackUrl:e,requestId:n,complete:i})=>{r(`Interactive login received callback, requestId: ${n}`);const s=setTimeout(()=>{r(`Pending request ${n} expired after ${t}ms, cleaning up`),o.delete(n)},t);o.set(n,{complete:i,timeoutId:s}),r(`Stored pending request ${n} with TTL ${t}ms`),r("Sending callback URL and requestId to client"),c.writeHead(200);const a={url:e,requestId:n};r(`Ending response with output: "${JSON.stringify(a)}"`),c.end(JSON.stringify(a))}).catch(e=>{r(`Interactive login errored: "${JSON.stringify(e)}"`),r("Sending error header"),c.writeHead(500,JSON.stringify(e)),r("Ending response"),c.end()})};return r("Starting credential proxy..."),u.listen(e.port,e.host),{close:()=>{for(const[e,t]of o)r(`Clearing pending request timeout for ${e} on shutdown`),clearTimeout(t.timeoutId);o.clear(),u.close()}}}};const n=o(t(611)),i=t(876),s=t(728),a=t(51),c=t(564),l=3e5},857:e=>{e.exports=require("os")},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"];let t;try{t=new URL(e)}catch(r){return o.Result.failure(new Error(`Invalid URL: ${e} (${r})`))}const n=Object.fromEntries(t.searchParams.entries());for(const e of r)if(!n[e])return o.Result.failure(new Error(`Missing required parameter: ${e}`));const i=Boolean(n.code_challenge);if(i!==Boolean(n.code_challenge_method))return o.Result.failure(new Error("PKCE parameters must be used together: both code_challenge and code_challenge_method are required, or neither"));if(n.code_challenge_method&&!["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 s=i?{client_id:n.client_id,response_type:n.response_type,redirect_uri:n.redirect_uri,scope:n.scope,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,code_challenge:n.code_challenge,code_challenge_method:n.code_challenge_method}:{client_id:n.client_id,response_type:n.response_type,redirect_uri:n.redirect_uri,scope:n.scope,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(s)};const o=t(728)},896:e=>{e.exports=require("fs")},928:e=>{e.exports=require("path")},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",LOGIN_TIMEOUT:"OAUTH2_FORWARDER_LOGIN_TIMEOUT"}},982:e=>{e.exports=require("crypto")}},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={478(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.color=function(e,r){return`${t[r]}${e}`};const t={black:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:""}},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",LOGIN_TIMEOUT:"OAUTH2_FORWARDER_LOGIN_TIMEOUT"}},51(e,r,t){Object.defineProperty(r,"__esModule",{value:!0}),r.extractPort=function(e){let r=null;for(const t of n.LOOPBACK_PATTERNS)if(r=e.match(t),r)break;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),n=t(580)},465(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.rotateOnLaunch=a,r.rotateIfNeeded=function(e,r=5242880,t=3){if((0,s.ensureDirectory)(i.default.dirname(e)),!n.default.existsSync(e))return!1;try{if(n.default.statSync(e).size<r)return!1}catch{return!1}return a(e,t),!0},r.createLogFileStream=function(e){return(0,s.ensureDirectory)(i.default.dirname(e)),n.default.createWriteStream(e,{flags:"a"})};const n=o(t(896)),i=o(t(928)),s=t(125);function a(e,r=5){if((0,s.ensureDirectory)(i.default.dirname(e)),!n.default.existsSync(e))return;const t=`${e}.${r}`;n.default.existsSync(t)&&n.default.unlinkSync(t);for(let t=r-1;t>=1;t--){const r=`${e}.${t}`,o=`${e}.${t+1}`;n.default.existsSync(r)&&n.default.renameSync(r,o)}n.default.renameSync(e,`${e}.1`)}},741(e,r,t){Object.defineProperty(r,"__esModule",{value:!0}),r.buildLogger=function(e){const{level:r,stream:t=process.stderr,prefix:a="",useFileFormat:c=!1}=e,l=i[r],u=(e,r)=>{if(i[e]>=l){const i=a?`[${a}] `:"",l=`[${e.toUpperCase()}]`,u=(0,n.sanitize)(r);let d;if(c)d=`${(new Date).toISOString()} ${i}${l} ${u}\n`;else{const r=s[e];d=(0,o.color)(`${i}${l} ${u}`,r)+"\n"}t.write(d)}};return{error:e=>u("error",e),warn:e=>u("warn",e),info:e=>u("info",e),debug:e=>u("debug",e)}};const o=t(478),n=t(638),i={debug:0,info:1,warn:2,error:3},s={error:"red",warn:"yellow",info:"cyan",debug:"magenta"}},580(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.LOOPBACK_PATTERNS=void 0,r.isLoopbackUrl=function(e){return r.LOOPBACK_PATTERNS.some(r=>r.test(e))},r.convertLoopbackUrl=function(e,r){return e.replace(/^(http:\/\/)localhost(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)127\.0\.0\.1(:\d+)?/,`$1${r}$2`).replace(/^(http:\/\/)\[::1\](:\d+)?/,`$1${r}$2`)},r.LOOPBACK_PATTERNS=[/^http:\/\/localhost(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/127\.0\.0\.1(?::(\d{1,5}))?(?:\/.*)?$/,/^http:\/\/\[::1\](?::(\d{1,5}))?(?:\/.*)?$/]},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)},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"];let t;try{t=new URL(e)}catch(r){return o.Result.failure(new Error(`Invalid URL: ${e} (${r})`))}const n=Object.fromEntries(t.searchParams.entries());for(const e of r)if(!n[e])return o.Result.failure(new Error(`Missing required parameter: ${e}`));const i=Boolean(n.code_challenge);if(i!==Boolean(n.code_challenge_method))return o.Result.failure(new Error("PKCE parameters must be used together: both code_challenge and code_challenge_method are required, or neither"));if(n.code_challenge_method&&!["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 s=i?{client_id:n.client_id,response_type:n.response_type,redirect_uri:n.redirect_uri,scope:n.scope,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,code_challenge:n.code_challenge,code_challenge_method:n.code_challenge_method}:{client_id:n.client_id,response_type:n.response_type,redirect_uri:n.redirect_uri,scope:n.scope,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(s)};const o=t(728)},125(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.ensureDirectory=function(e){n.default.existsSync(e)||n.default.mkdirSync(e,{recursive:!0})},r.getLogFilePath=function(e){const r=c();return i.default.join(r,`o2f-${e}.log`)},r.getLogDir=function(){return c()},r.getConfigDir=function(){return l()},r.getLegacyConfigDir=function(){return u()},r.resolveConfigFile=function(e){const r=i.default.join(u(),e),t=i.default.join(l(),e);return n.default.existsSync(r)?{path:r,isLegacy:!0}:{path:t,isLegacy:!1}},r.getPreferredConfigDescription=function(){switch(s.default.platform()){case"darwin":return"~/Library/Application Support/oauth2-forwarder/";case"win32":return"%LOCALAPPDATA%\\oauth2-forwarder\\";default:return"~/.config/oauth2-forwarder/ (or $XDG_CONFIG_HOME/oauth2-forwarder/)"}};const n=o(t(896)),i=o(t(928)),s=o(t(857)),a="oauth2-forwarder";function c(){const e=s.default.platform(),r=s.default.homedir();switch(e){case"darwin":return i.default.join(r,"Library","Logs",a);case"win32":{const e=process.env.LOCALAPPDATA||i.default.join(r,"AppData","Local");return i.default.join(e,a,"logs")}default:{const e=process.env.XDG_STATE_HOME||i.default.join(r,".local","state");return i.default.join(e,a)}}}function l(){const e=s.default.platform(),r=s.default.homedir();switch(e){case"darwin":return i.default.join(r,"Library","Application Support",a);case"win32":{const e=process.env.LOCALAPPDATA||i.default.join(r,"AppData","Local");return i.default.join(e,a)}default:{const e=process.env.XDG_CONFIG_HOME||i.default.join(r,".config");return i.default.join(e,a)}}}function u(){return i.default.join(s.default.homedir(),".oauth2-forwarder")}},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}},638(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.sanitize=function(e){return e.replace(/((code|code_challenge)=)([^"&\n]*)/gi,(e,r)=>`${r}********`)}},802(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{logger:r}=e,t=e.pendingRequestTtlMs??3e5,o=new Map,l=(t,o,n)=>{if((0,c.isUrlAllowed)(t,e.whitelist))return!1;const i=`Domain '${(0,c.getDomain)(t)??"unknown"}' is not in the whitelist`,s=n?"Whitelist rejection (passthrough)":"Whitelist rejection";return r.warn(`${s}: ${i}`),o.writeHead(403,i),o.end(),!0},u=n.default.createServer((e,t)=>{r.debug(`Request received at ${e.headers.host}${e.url??"/"}`);const o=[];e.on("close",()=>{r.debug("Request closed")}),e.on("error",e=>{r.error(`Request error: ${e}`)}),e.on("data",e=>{o.push(e)}),e.on("end",()=>{r.debug("Request ended");const n=o.join("");r.debug(`Received body: "${n}"`),"/complete"===(e.url??"/")?d(n,t):f(n,t)})}),d=(e,t)=>{let n;r.debug("Handling /complete request");try{n=JSON.parse(e)}catch{const e="Invalid JSON in completion request body";return r.warn(e),t.writeHead(400,e),void t.end()}if(!n.requestId||!n.result){const e="Completion request missing requestId or result";return r.warn(e),t.writeHead(400,e),void t.end()}const i=o.get(n.requestId);if(!i){const e=`No pending request found for requestId: ${n.requestId}`;return r.warn(e),t.writeHead(404,e),void t.end()}clearTimeout(i.timeoutId),o.delete(n.requestId),r.info(`Completed OAuth request for ${i.domain}`),r.debug(`Request ${n.requestId} completed with result: ${n.result.type}`),i.complete(n.result),t.writeHead(200),t.end()},f=(n,u)=>{let d,f;try{d=JSON.parse(n)}catch{const e="Invalid JSON in request body";return r.warn(e),u.writeHead(400,e),void u.end()}if(!("url"in d)){const e="Received body does not contain a 'url' property";return r.warn(e),u.writeHead(400,e),void u.end()}{const t=(0,i.parseOauth2Url)(d.url);if(s.Result.isFailure(t)){if(r.debug(`OAuth parse error: ${t.error.message}`),e.passthrough){if(l(d.url,u,!0))return;const o=(0,c.getDomain)(d.url)??"unknown";r.info(`Passthrough: opening ${o} in browser`),e.openBrowser(d.url).catch(e=>{r.error(`Failed to open browser: ${e}`)}),u.writeHead(400,`${t.error.message}; URL opened in browser (passthrough mode)`)}else u.writeHead(400,t.error.message);return void u.end()}if(f=t.value,f.code_challenge||r.debug("OAuth2 request does not include PKCE parameters"),l(d.url,u,!1))return}const p=(0,a.extractPort)(f.redirect_uri);if(s.Result.isFailure(p)){if(r.debug(`Port extraction error: ${p.error.message}`),e.passthrough){if(l(d.url,u,!0))return;const t=(0,c.getDomain)(d.url)??"unknown";r.info(`Passthrough: opening ${t} in browser`),e.openBrowser(d.url).catch(e=>{r.error(`Failed to open browser: ${e}`)}),u.writeHead(400,`${p.error.message}; URL opened in browser (passthrough mode)`)}else u.writeHead(400,p.error.message);return void u.end()}const m=p.value??80;r.debug(`Using port number: ${m}`);const g=(0,c.getDomain)(d.url)??"unknown";r.info(`Received OAuth request for ${g}`),e.interactiveLogin(d.url,m).then(({callbackUrl:e,requestId:n,complete:i})=>{r.debug(`Interactive login received callback, requestId: ${n}`);const s=setTimeout(()=>{r.warn(`Pending request ${n} expired after ${t}ms, cleaning up`),o.delete(n)},t);o.set(n,{complete:i,timeoutId:s,domain:g}),r.debug(`Stored pending request ${n} with TTL ${t}ms`),r.debug("Sending callback URL and requestId to client"),u.writeHead(200);const a={url:e,requestId:n};r.debug(`Ending response with output: "${JSON.stringify(a)}"`),u.end(JSON.stringify(a))}).catch(e=>{r.error(`Interactive login error: "${JSON.stringify(e)}"`),u.writeHead(500,JSON.stringify(e)),u.end()})};return r.debug("Starting credential proxy..."),u.listen(e.port,e.host),{close:()=>{for(const[e,t]of o)r.debug(`Clearing pending request timeout for ${e} on shutdown`),clearTimeout(t.timeoutId);o.clear(),u.close()}}}};const n=o(t(611)),i=t(876),s=t(728),a=t(51),c=t(564)},516(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.loginTimeoutMs??3e5,{logger:t}=e;return async(o,s)=>new Promise((a,c)=>{const l=(0,i.nanoid)();let u;t.debug(`Generated requestId: ${l}`);const d=n.default.createServer((e,r)=>{u&&(clearTimeout(u),u=void 0),t.debug(`Received a ${e.method??"undefined"} request`),e.on("error",e=>{t.error(`Request error: ${JSON.stringify(e)}`),r.writeHead(500,JSON.stringify(e)),r.end(),d.close(),c(e)}),e.on("data",e=>{t.debug(`Received data chunk: ${e}`)}),e.on("close",()=>{t.debug("Redirect request closed")}),e.on("end",()=>{if(t.debug("Request ended"),!e.headers.host){const e="Missing Host header in redirect request";return t.warn(e),r.writeHead(400,e),r.end(),d.close(),void c(new Error(e))}const o="http://"+e.headers.host+e.url;t.debug(`Received callback url: "${o}"`),t.debug("Holding browser response pending completion"),t.debug("Closing listening socket (keeping response connection open)"),d.close(),a({callbackUrl:o,requestId:l,complete:e=>{switch(t.debug(`Completing request ${l} with result type: ${e.type}`),e.type){case"redirect":t.debug(`Redirecting browser to: ${e.location}`),r.writeHead(302,{Location:e.location}),r.end();break;case"success":t.debug("Sending success response to browser"),r.writeHead(200,{"Content-Type":"text/html"}),e.body?r.end(e.body):r.end("Authentication completed. You may close this page now.");break;case"error":t.debug(`Sending error response to browser: ${e.message}`),r.writeHead(500,{"Content-Type":"text/html"}),r.end(`Authentication failed: ${e.message}`)}}})})});t.debug(`Starting temporary redirect server on port ${s}...`),d.listen(s,"127.0.0.1",()=>{t.debug("Temporary redirect server is listening"),t.debug(`Setting login timeout: ${r}ms`),u=setTimeout(()=>{t.warn(`Login timeout exceeded (${r}ms), closing server on port ${s}`),d.close(),c(new Error(`Login timeout: user did not complete OAuth2 flow within ${r}ms`))},r),t.info("Opening browser for interactive login"),e.openBrowser(o)})})};const n=o(t(611)),i=t(641)},515(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))})})}},561(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(541),i=t(950),s=t(76),a=t(741),c=t(125),l=t(465),u=t(516),d=t(802),f=t(515),p=t(564),m=o(t(667));(process.argv.includes("--version")||process.argv.includes("-v"))&&(console.log(`o2f-server v${(0,n.getVersion)()}`),process.exit(0));const g=process.env[i.EnvKey.DEBUG],h="true"===process.env[i.EnvKey.PASSTHROUGH],w="127.0.0.1";let y;const v=process.env[i.EnvKey.LOGIN_TIMEOUT];if(v){const e=parseInt(v);!isNaN(e)&&e>0&&(y=1e3*e)}const b=g?"debug":"info",_=!0===process.stdout.isTTY;let S,x;if(_)S=process.stdout,x=!1;else{const e=(0,c.getLogFilePath)("server");(0,l.rotateOnLaunch)(e,5),S=(0,l.createLogFileStream)(e),x=!0}const P=(0,a.buildLogger)({level:b,stream:S,prefix:"server",useFileFormat:x}),O=(0,s.buildOutputWriter)({color:"yellow",stream:process.stdout}),$=(0,s.buildOutputWriter)({color:"white",stream:process.stdout});let R=null;const E=process.env[i.EnvKey.PORT];if(E){const e=parseInt(E);isNaN(e)||(R=e)}const A=async e=>{await(0,m.default)(e)},L=(0,u.buildInteractiveLogin)({openBrowser:A,logger:P,loginTimeoutMs:y});(async()=>{P.info(`o2f-server v${(0,n.getVersion)()}`),R&&P.info(`Attempting to use user specified port ${R}`);const e=R??await(0,f.findAvailablePort)(w),r=(0,p.loadWhitelist)();r.enabled?(P.info(`URL whitelist enabled with ${r.domains.size} domain(s): ${Array.from(r.domains).join(", ")}`),r.usingLegacyPath&&P.warn(`Config location ~/.oauth2-forwarder/ is deprecated. Please move your config to ${r.preferredLocation}`)):P.info(`URL whitelist disabled (no whitelist file at ${r.configPath})`);const t=(0,d.buildCredentialProxy)({host:w,port:e,interactiveLogin:L,openBrowser:A,passthrough:h,pendingRequestTtlMs:y,whitelist:r,logger:P});P.info(`Starting TCP server listening on ${w}:${e}`),h&&P.info("Passthrough mode enabled: malformed URLs will be opened in browser"),_&&(O("Run the following command in your docker container:\n"),$(` export ${i.EnvKey.SERVER}="host.docker.internal:${e}"\n`),O("\nIn addition, you need to set the BROWSER env variable to point to the client script in the docker container.\n"),O("If you installed via npm (recommended):\n"),$(" export BROWSER=o2f-browser\n"),O("If you installed manually to the default location:\n"),$(" export BROWSER=~/o2f/browser.sh\n"));try{await t(),P.info("Server started. Press Ctrl+C to stop.")}catch(e){P.error(JSON.stringify(e)),process.exit(1)}})().catch(e=>{P.error(JSON.stringify(e)),process.exit(1)})},564(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.getDomain=void 0,r.loadWhitelist=function(){const{path:e,isLegacy:r}=(0,s.resolveConfigFile)("whitelist.json"),t=(0,s.getPreferredConfigDescription)();if(!n.default.existsSync(e))return{enabled:!1,domains:new Set,configPath:e,usingLegacyPath:!1,preferredLocation:t};try{const o=n.default.readFileSync(e,"utf-8"),i=JSON.parse(o);if(!Array.isArray(i.domains)||0===i.domains.length)return{enabled:!1,domains:new Set,configPath:e,usingLegacyPath:r,preferredLocation:t};const s=new Set(i.domains.map(e=>e.toLowerCase().trim()).filter(e=>e.length>0));return{enabled:s.size>0,domains:s,configPath:e,usingLegacyPath:r,preferredLocation:t}}catch{return{enabled:!1,domains:new Set,configPath:e,usingLegacyPath:r,preferredLocation:t}}},r.isUrlAllowed=function(e,r){if(!r.enabled)return!0;const t=(0,i.getDomain)(e);return!!t&&r.domains.has(t)};const n=o(t(896)),i=t(512);Object.defineProperty(r,"getDomain",{enumerable:!0,get:function(){return i.getDomain}});const s=t(125)},512(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.getDomain=function(e){try{return new URL(e).hostname.toLowerCase()}catch{return null}}},541(e,r,t){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.getVersion=function(){return n.default.version};const n=o(t(330))},896(e){e.exports=require("fs")},611(e){e.exports=require("http")},857(e){e.exports=require("os")},928(e){e.exports=require("path")},23(e){e.exports=require("util")},641(e,r,t){t.r(r),t.d(r,{customAlphabet:()=>u,customRandom:()=>l,nanoid:()=>d,random:()=>c,urlAlphabet:()=>n});const o=require("node:crypto"),n="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let i,s;function a(e){!i||i.length<e?(i=Buffer.allocUnsafe(128*e),o.webcrypto.getRandomValues(i),s=0):s+e>i.length&&(o.webcrypto.getRandomValues(i),s=0),s+=e}function c(e){return a(e|=0),i.subarray(s-e,s)}function l(e,r,t){let o=(2<<31-Math.clz32(e.length-1|1))-1,n=Math.ceil(1.6*o*r/e.length);return(i=r)=>{if(!i)return"";let s="";for(;;){let r=t(n),a=n;for(;a--;)if(s+=e[r[a]&o]||"",s.length>=i)return s}}}function u(e,r=21){return l(e,r,c)}function d(e=21){a(e|=0);let r="";for(let t=s-e;t<s;t++)r+=n[63&i[t]];return r}},667(e,r,t){t.r(r),t.d(r,{apps:()=>W,default:()=>K,openApp:()=>F});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 f(){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 p=()=>{if("linux"!==o.platform)return!1;if(c.release().toLowerCase().includes("microsoft"))return!f();try{return!!l.readFileSync("/proc/version","utf8").toLowerCase().includes("microsoft")&&!f()}catch{return!1}},m=o.env.__IS_WSL_TEST__?p:p(),g=require("node:util"),h=require("node:buffer"),w=(0,g.promisify)(s.execFile),y=()=>`${o.env.SYSTEMROOT||o.env.windir||String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,v=async(e,r={})=>{const{powerShellPath:t,...o}=r,n=v.encodeCommand(e);return w(t??y(),[...v.argumentsPrefix,n],{encoding:"utf8",...o})};v.argumentsPrefix=["-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand"],v.encodeCommand=e=>h.Buffer.from(e,"utf16le").toString("base64"),v.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=m?async()=>`${await _()}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`:y;let x;function P(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 O=(0,g.promisify)(s.execFile),$={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($));class E extends Error{}const A=(0,g.promisify)(s.execFile),L=(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 L("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 C=(0,g.promisify)(s.execFile);const M=Boolean(o.env.SSH_CONNECTION||o.env.SSH_CLIENT||o.env.SSH_TTY),j=Symbol("fallbackAttempt"),q=n.dirname((0,i.fileURLToPath)("file:///home/runner/work/oauth2-forwarder/oauth2-forwarder/node_modules/open/index.js")),U=n.join(q,"xdg-open"),{platform:H,arch:I}=o,D=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")},B=async e=>{const r=!0===(e={wait:!1,background:!1,newInstance:!1,allowNonzeroExitCode:!1,...e})[j];if(delete e[j],Array.isArray(e.app))return D(e.app,r=>B({...e,app:r,[j]:!0}));let t,{name:n,arguments:i=[]}=e.app??{};if(i=[...i],Array.isArray(n))return D(n,r=>B({...e,app:{name:r,arguments:i},[j]:!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(m){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 v(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 A("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 C("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=O){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 E(`Cannot find Windows browser in stdout: ${JSON.stringify(r)}`);const{id:o}=t.groups,n=$[o];if(!n)throw new E(`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 B({...e,app:{name:W[o],arguments:i}})}throw new Error(`${s.name} is not supported as a default browser`)}const c=[],l={};let u=!1;if(!m||f()||M||n||(u=await(async()=>(x??=(async()=>{try{const e=await S();return await a.access(e,a.constants.X_OK),!0}catch{return!1}})(),x))()),"darwin"===H)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"===H||u){t=await S(),c.push(...v.argumentsPrefix),m||(l.windowsVerbatimArguments=!0),m&&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(v.escapeArgument(n)),e.target&&i.push(e.target)):e.target&&r.push(v.escapeArgument(e.target)),i.length>0&&(i=i.map(e=>v.escapeArgument(e)),r.push("-ArgumentList",i.join(","))),e.target=v.encodeCommand(r.join(" ")),e.wait||(l.stdio="ignore")}else{if(n)t=n;else{const e=!q||"/"===q;let r=!1;try{await a.access(U,a.constants.X_OK),r=!0}catch{}t=o.versions.electron??("android"===H||e||!r)?"xdg-open":U}i.length>0&&c.push(...i),e.wait||(l.stdio="ignore",l.detached=!0)}"darwin"===H&&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)})}))},F=(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 B({...r,app:{name:e,arguments:t}})};function N(e){if("string"==typeof e||Array.isArray(e))return e;const{[I]:r}=e;if(!r)throw new Error(`${I} is not supported`);return r}function k({[H]:e},{wsl:r}={}){if(r&&m)return N(r);if(!e)throw new Error(`${H} is not supported`);return N(e)}const W={browser:"browser",browserPrivate:"browserPrivate"};P(W,"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"]}})),P(W,"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"]}})),P(W,"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"})),P(W,"edge",()=>k({darwin:"microsoft edge",win32:"msedge",linux:["microsoft-edge","microsoft-edge-dev"]},{wsl:"/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"})),P(W,"safari",()=>k({darwin:"Safari"}));const K=(e,r)=>{if("string"!=typeof e)throw new TypeError("Expected a `target`");return B({...r,target:e})}},330(e){e.exports=JSON.parse('{"name":"oauth2-forwarder","version":"1.4.3","description":"utilities for forwarding oauth2 interactive flow (e.g. container to host)","main":"dist/index.js","bin":{"o2f-server":"./dist/o2f-server.js","o2f-client":"./dist/o2f-client.js","o2f-browser":"./browser-global.sh"},"scripts":{"build":"rimraf ./dist && webpack && node prepend-shebang.js","zip-release":"./release.sh","test":"vitest","e2e-test":"vitest --config vitest.e2e.config.ts","lint":"eslint .","fix-formatting":"prettier --write --config ./prettier.config.js ./src/","check-formatting":"prettier --check --config ./prettier.config.js ./src/"},"author":"Sam Davidoff","license":"MIT","repository":{"type":"git","url":"https://github.com/sam-mfb/oauth2-forwarder.git"},"files":["dist","browser.sh","browser-global.sh"],"keywords":["oauth2","docker","container","authentication","browser"],"engines":{"node":">=24.0.0"},"devDependencies":{"@eslint/js":"^9.25.1","@types/eslint__js":"^8.42.3","@types/node":"^24.0.0","eslint":"^9.25.1","prettier":"^3.5.3","rimraf":"^6.0.1","ts-loader":"^9.5.2","typescript":"^5.8.3","typescript-eslint":"^8.31.1","vitest":"^3.0.5","webpack":"^5.99.7","webpack-cli":"^6.0.1"},"dependencies":{"nanoid":"^5.1.5","open":"^11.0.0"},"overrides":{"glob@>=10.2.0 <=10.4.5":"10.5.0","glob@>=11.0.0 <=11.0.3":"11.1.0","js-yaml@<3.14.2":"3.14.2","js-yaml@>=4.0.0 <4.1.1":"4.1.1"}}')}},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)})());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauth2-forwarder",
3
- "version": "1.4.0",
3
+ "version": "1.4.3",
4
4
  "description": "utilities for forwarding oauth2 interactive flow (e.g. container to host)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -11,8 +11,8 @@
11
11
  "scripts": {
12
12
  "build": "rimraf ./dist && webpack && node prepend-shebang.js",
13
13
  "zip-release": "./release.sh",
14
- "test": "jest --watch",
15
- "e2e-test": "jest -c ./jest.e2e.config.js",
14
+ "test": "vitest",
15
+ "e2e-test": "vitest --config vitest.e2e.config.ts",
16
16
  "lint": "eslint .",
17
17
  "fix-formatting": "prettier --write --config ./prettier.config.js ./src/",
18
18
  "check-formatting": "prettier --check --config ./prettier.config.js ./src/"
@@ -41,20 +41,19 @@
41
41
  "devDependencies": {
42
42
  "@eslint/js": "^9.25.1",
43
43
  "@types/eslint__js": "^8.42.3",
44
- "@types/jest": "^30.0.0",
45
44
  "@types/node": "^24.0.0",
46
45
  "eslint": "^9.25.1",
47
- "jest": "^30.2.0",
48
46
  "prettier": "^3.5.3",
49
47
  "rimraf": "^6.0.1",
50
- "ts-jest": "^29.3.2",
51
48
  "ts-loader": "^9.5.2",
52
49
  "typescript": "^5.8.3",
53
50
  "typescript-eslint": "^8.31.1",
51
+ "vitest": "^3.0.5",
54
52
  "webpack": "^5.99.7",
55
53
  "webpack-cli": "^6.0.1"
56
54
  },
57
55
  "dependencies": {
56
+ "nanoid": "^5.1.5",
58
57
  "open": "^11.0.0"
59
58
  },
60
59
  "overrides": {