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 +35 -12
- package/browser-global.sh +9 -5
- package/browser.sh +9 -5
- package/dist/o2f-client.js +1 -1
- package/dist/o2f-server.js +1 -1
- package/package.json +5 -6
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`.
|
|
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
|
-
|
|
146
|
-
-
|
|
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
|
-
###
|
|
191
|
+
### Config File Location
|
|
176
192
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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.
|
|
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
|
-
#
|
|
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 "$@"
|
|
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
|
-
#
|
|
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 "$@"
|
|
17
|
+
node ~/o2f/o2f-client.js "$@" &
|
|
14
18
|
|
|
15
19
|
exit 0
|
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 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:"[30m",red:"[31m",green:"[32m",yellow:"[33m",blue:"[34m",magenta:"[35m",cyan:"[36m",white:"[37m"},o="[0m"},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}[0m`};const t={black:"[30m",red:"[31m",green:"[32m",yellow:"[33m",blue:"[34m",magenta:"[35m",cyan:"[36m",white:"[37m"}},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})());
|
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){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:"[30m",red:"[31m",green:"[32m",yellow:"[33m",blue:"[34m",magenta:"[35m",cyan:"[36m",white:"[37m"},o="[0m"},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}[0m`};const t={black:"[30m",red:"[31m",green:"[32m",yellow:"[33m",blue:"[34m",magenta:"[35m",cyan:"[36m",white:"[37m"}},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.
|
|
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": "
|
|
15
|
-
"e2e-test": "
|
|
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": {
|