@blakearoberts/visage 0.0.1-rc.5 → 0.0.1-rc.7
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 +19 -13
- package/dist/config.d.ts +9 -11
- package/dist/config.d.ts.map +1 -1
- package/dist/index.js +97 -72
- package/dist/types.d.ts +40 -25
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,31 +33,36 @@ Visage is configured through `visage(options?)` in `vite.config.ts`.
|
|
|
33
33
|
|
|
34
34
|
The top-level `host` and `port` configure the local Visage origin that the browser visits:
|
|
35
35
|
|
|
36
|
+
```ts
|
|
37
|
+
visage({ host: 'localhost', port: 9001 });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Services
|
|
41
|
+
|
|
42
|
+
Services are Docker Compose services managed by the Vite dev-server lifecycle.
|
|
43
|
+
Additional services automatically get a matching managed upstream with the same
|
|
44
|
+
name, host, and default `/{name}/` location.
|
|
45
|
+
|
|
36
46
|
```ts
|
|
37
47
|
visage({
|
|
38
|
-
|
|
39
|
-
port: 9001,
|
|
48
|
+
services: { whoami: { image: 'traefik/whoami' } },
|
|
40
49
|
});
|
|
41
50
|
```
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
### Upstreams
|
|
53
|
+
|
|
54
|
+
Upstreams are proxy targets that Visage routes to. A top-level upstream with no
|
|
55
|
+
matching service entry is treated as an external upstream.
|
|
44
56
|
|
|
45
57
|
```ts
|
|
46
58
|
visage({
|
|
47
|
-
services: {
|
|
48
|
-
whoami: { image: 'traefik/whoami' },
|
|
49
|
-
},
|
|
50
59
|
upstreams: {
|
|
51
|
-
|
|
52
|
-
host: 'whoami',
|
|
53
|
-
port: 80,
|
|
54
|
-
locations: { '/whoami/': {} },
|
|
55
|
-
},
|
|
60
|
+
api: { host: 'api.local.test', locations: { '/api/': {} } },
|
|
56
61
|
},
|
|
57
62
|
});
|
|
58
63
|
```
|
|
59
64
|
|
|
60
|
-
See `VisageOptions` for the full option surface.
|
|
65
|
+
See [`VisageOptions`](src/types.ts) for the full option surface.
|
|
61
66
|
|
|
62
67
|
## Expected Local URLs
|
|
63
68
|
|
|
@@ -124,6 +129,7 @@ Do not treat the managed Dex and OAuth2 Proxy defaults as production auth infras
|
|
|
124
129
|
|
|
125
130
|
## TO-DO
|
|
126
131
|
|
|
132
|
+
- [ ] Support SSR injection of identity into HTML responses as script tag elements.
|
|
127
133
|
- [ ] Support configuring [Dex connectors](https://dexidp.io/docs/connectors/).
|
|
128
|
-
- [ ] Support configuring Dex on a distinct subdomain, such as `auth.
|
|
134
|
+
- [ ] Support configuring Dex on a distinct subdomain, such as `auth.localhost`.
|
|
129
135
|
- [ ] Support optional [HTTP mode without local TLS](docs/tls-http-mode.md).
|
package/dist/config.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ type ResolvedCookiePolicy = {
|
|
|
9
9
|
readonly cookie_path: string;
|
|
10
10
|
};
|
|
11
11
|
type ResolvedIdpOption = {
|
|
12
|
-
readonly kind: 'dex';
|
|
13
12
|
readonly dex: VisageDexOptions;
|
|
14
13
|
} | VisageExternalIdpOptions;
|
|
15
14
|
type ResolvedOAuth2Client = {
|
|
@@ -18,20 +17,14 @@ type ResolvedOAuth2Client = {
|
|
|
18
17
|
readonly scopes: readonly string[];
|
|
19
18
|
readonly public: boolean;
|
|
20
19
|
};
|
|
21
|
-
type ResolvedService = VisageService & {
|
|
22
|
-
readonly image: string;
|
|
23
|
-
};
|
|
24
|
-
type ResolvedUpstream = VisageUpstream & {
|
|
25
|
-
readonly scheme: 'http' | 'https';
|
|
26
|
-
};
|
|
27
20
|
type ResolvedVisageOptions = {
|
|
28
21
|
readonly host: string;
|
|
29
22
|
readonly port: number;
|
|
30
23
|
readonly cookie: ResolvedCookiePolicy;
|
|
31
24
|
readonly idp: ResolvedIdpOption;
|
|
32
25
|
readonly oauth2: ResolvedOAuth2Client;
|
|
33
|
-
readonly services: Readonly<Record<string,
|
|
34
|
-
readonly upstreams?: Record<string,
|
|
26
|
+
readonly services: Readonly<Record<string, VisageService>>;
|
|
27
|
+
readonly upstreams?: Record<string, VisageUpstream>;
|
|
35
28
|
};
|
|
36
29
|
type ResolvedBaseIdpConfig = {
|
|
37
30
|
readonly upstream: string;
|
|
@@ -41,16 +34,21 @@ type ResolvedBaseIdpConfig = {
|
|
|
41
34
|
readonly jwks: string;
|
|
42
35
|
};
|
|
43
36
|
type ResolvedDexIdpConfig = ResolvedBaseIdpConfig & {
|
|
44
|
-
readonly kind: 'dex';
|
|
45
37
|
readonly dex: {
|
|
46
38
|
readonly expiry?: VisageDexExpiry;
|
|
47
39
|
readonly users: readonly VisageDexUser[];
|
|
48
40
|
};
|
|
49
41
|
};
|
|
50
42
|
type ResolvedExternalIdpConfig = ResolvedBaseIdpConfig & {
|
|
51
|
-
readonly
|
|
43
|
+
readonly dex?: never;
|
|
52
44
|
};
|
|
53
45
|
type ResolvedIdpConfig = ResolvedDexIdpConfig | ResolvedExternalIdpConfig;
|
|
46
|
+
type ResolvedService = Omit<VisageService, 'upstream'>;
|
|
47
|
+
type ResolvedUpstream = Omit<VisageUpstream, 'host' | 'port' | 'scheme'> & {
|
|
48
|
+
readonly host: string;
|
|
49
|
+
readonly port: number;
|
|
50
|
+
readonly scheme: 'http' | 'https';
|
|
51
|
+
};
|
|
54
52
|
export type VisageConfig = {
|
|
55
53
|
readonly host: string;
|
|
56
54
|
readonly port: number;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE3C,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,wBAAwB,EACxB,aAAa,EAEb,aAAa,EACb,cAAc,EACf,MAAM,SAAS,CAAC;AAEjB,KAAK,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;AAElD,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,KAAK,iBAAiB,GAClB;IACE,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -165,7 +165,7 @@ function renderComposeConfig(config) {
|
|
|
165
165
|
const { dex, nginx, oauth2_proxy, ...services } = config.services;
|
|
166
166
|
return stringify({
|
|
167
167
|
services: {
|
|
168
|
-
...(config.idp.
|
|
168
|
+
...(config.idp.dex !== undefined
|
|
169
169
|
? {
|
|
170
170
|
dex: {
|
|
171
171
|
...config.services.dex,
|
|
@@ -200,14 +200,15 @@ function writeDexConfig(config) {
|
|
|
200
200
|
writeFileSync(file, render, 'utf-8');
|
|
201
201
|
}
|
|
202
202
|
function renderDexConfig(config) {
|
|
203
|
-
|
|
203
|
+
const { idp } = config;
|
|
204
|
+
if (idp.dex === undefined) {
|
|
204
205
|
throw new Error('Dex config is required to render Dex');
|
|
205
206
|
}
|
|
206
207
|
const origin = `https://${config.host}:${config.port}`;
|
|
207
208
|
const redirect = `${origin}/oauth2/callback`;
|
|
208
|
-
const upstream = config.upstreams[
|
|
209
|
+
const upstream = config.upstreams[idp.upstream];
|
|
209
210
|
return stringify({
|
|
210
|
-
issuer:
|
|
211
|
+
issuer: idp.issuer,
|
|
211
212
|
storage: { type: 'memory' },
|
|
212
213
|
web: { http: `0.0.0.0:${upstream.port}` },
|
|
213
214
|
oauth2: { skipApprovalScreen: true },
|
|
@@ -222,10 +223,8 @@ function renderDexConfig(config) {
|
|
|
222
223
|
},
|
|
223
224
|
],
|
|
224
225
|
enablePasswordDB: true,
|
|
225
|
-
...(
|
|
226
|
-
|
|
227
|
-
: { expiry: config.idp.dex.expiry }),
|
|
228
|
-
staticPasswords: config.idp.dex.users.map(({ password, ...user }) => ({
|
|
226
|
+
...(idp.dex.expiry === undefined ? {} : { expiry: idp.dex.expiry }),
|
|
227
|
+
staticPasswords: idp.dex.users.map(({ password, ...user }) => ({
|
|
229
228
|
...user,
|
|
230
229
|
hash: hashSync(password, 10),
|
|
231
230
|
})),
|
|
@@ -360,7 +359,7 @@ function renderOauth2ProxyConfig(config) {
|
|
|
360
359
|
|
|
361
360
|
function render(config) {
|
|
362
361
|
writeComposeConfig(config);
|
|
363
|
-
if (config.idp.
|
|
362
|
+
if (config.idp.dex !== undefined) {
|
|
364
363
|
writeDexConfig(config);
|
|
365
364
|
}
|
|
366
365
|
writeNginxConfig(config);
|
|
@@ -410,11 +409,7 @@ const BaseOauth2ProxyUpstream = {
|
|
|
410
409
|
'/oauth2/': {
|
|
411
410
|
auth: { enabled: false },
|
|
412
411
|
headers: {
|
|
413
|
-
Cookie: '$http_cookie',
|
|
414
|
-
Host: '$host',
|
|
415
|
-
'X-Real-IP': '$remote_addr',
|
|
416
|
-
'X-Forwarded-For': '$proxy_add_x_forwarded_for',
|
|
417
|
-
'X-Forwarded-Proto': '$scheme',
|
|
412
|
+
Cookie: '$http_cookie', // Forward session cookie.
|
|
418
413
|
'X-Auth-Request-Redirect': '$request_uri',
|
|
419
414
|
},
|
|
420
415
|
},
|
|
@@ -459,18 +454,20 @@ const DefaultProxyPolicy = {
|
|
|
459
454
|
},
|
|
460
455
|
};
|
|
461
456
|
function resolveOptions(options) {
|
|
462
|
-
const { host = '
|
|
457
|
+
const { host = 'localhost', port = 9001, cookie = {}, oauth2 = {} } = options;
|
|
463
458
|
const cookieName = cookie.name ?? 'session';
|
|
464
459
|
const publicClient = oauth2.clientSecret === null;
|
|
460
|
+
const services = resolveServicesOptions(options.services);
|
|
461
|
+
const upstreams = resolveUpstreamsOptions(services, options.upstreams);
|
|
465
462
|
return {
|
|
466
463
|
host,
|
|
467
464
|
port,
|
|
468
465
|
cookie: {
|
|
469
466
|
...DefaultCookiePolicy,
|
|
470
467
|
cookie_name: cookie.domains === undefined
|
|
471
|
-
? cookieName.startsWith('
|
|
468
|
+
? cookieName.startsWith('__Host-')
|
|
472
469
|
? cookieName
|
|
473
|
-
: `
|
|
470
|
+
: `__Host-${cookieName}`
|
|
474
471
|
: cookieName,
|
|
475
472
|
...(cookie.expire === undefined ? {} : { cookie_expire: cookie.expire }),
|
|
476
473
|
...(cookie.refresh === undefined
|
|
@@ -490,43 +487,66 @@ function resolveOptions(options) {
|
|
|
490
487
|
scopes: oauth2.scopes ?? DefaultOAuth2Client.scopes,
|
|
491
488
|
public: publicClient,
|
|
492
489
|
},
|
|
493
|
-
services
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
},
|
|
490
|
+
services,
|
|
491
|
+
upstreams,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function resolveServicesOptions(services = {}) {
|
|
495
|
+
return {
|
|
496
|
+
...services,
|
|
497
|
+
nginx: {
|
|
498
|
+
...BaseServiceNginx,
|
|
499
|
+
...{
|
|
500
|
+
...(services.nginx ?? {}),
|
|
501
|
+
extra_hosts: [
|
|
502
|
+
...BaseServiceNginx.extra_hosts,
|
|
503
|
+
...(services.nginx?.extra_hosts ?? []),
|
|
504
|
+
],
|
|
504
505
|
},
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
],
|
|
513
|
-
|
|
506
|
+
},
|
|
507
|
+
oauth2_proxy: {
|
|
508
|
+
...BaseOAuth2ProxyService,
|
|
509
|
+
...{
|
|
510
|
+
...(services.oauth2_proxy ?? {}),
|
|
511
|
+
extra_hosts: [
|
|
512
|
+
...BaseOAuth2ProxyService.extra_hosts,
|
|
513
|
+
...(services.oauth2_proxy?.extra_hosts ?? []),
|
|
514
|
+
],
|
|
514
515
|
},
|
|
515
516
|
},
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function resolveUpstreamsOptions(services, upstreams = {}) {
|
|
520
|
+
function resolveUpstream(name, upstream) {
|
|
521
|
+
return {
|
|
522
|
+
...upstream,
|
|
523
|
+
scheme: upstream.scheme,
|
|
524
|
+
host: upstream.host ?? name,
|
|
525
|
+
port: upstream.port ?? (upstream.scheme === 'https' ? 443 : 80),
|
|
526
|
+
locations: upstream.locations ?? { [`/${name}/`]: {} },
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
...Object.fromEntries(Object.entries(services)
|
|
531
|
+
.filter(([name]) =>
|
|
532
|
+
// Exclude base services handled separately.
|
|
533
|
+
name !== 'dex' && name !== 'nginx' && name !== 'oauth2_proxy')
|
|
534
|
+
.map(([name, service]) => [
|
|
535
|
+
name,
|
|
536
|
+
resolveUpstream(name, { scheme: 'http', ...service.upstream }),
|
|
537
|
+
])),
|
|
538
|
+
...Object.fromEntries(Object.entries(upstreams).map(([name, upstream]) => [
|
|
539
|
+
name,
|
|
540
|
+
resolveUpstream(name, {
|
|
541
|
+
scheme: services[name] === undefined ? 'https' : 'http',
|
|
542
|
+
...upstream,
|
|
523
543
|
}),
|
|
544
|
+
])),
|
|
524
545
|
};
|
|
525
546
|
}
|
|
526
547
|
function resolveIdpOption(idp) {
|
|
527
|
-
if (idp
|
|
548
|
+
if (idp && 'issuer' in idp) {
|
|
528
549
|
return {
|
|
529
|
-
kind: 'external',
|
|
530
550
|
issuer: idp.issuer,
|
|
531
551
|
authorization: idp.authorization ?? '/auth',
|
|
532
552
|
token: idp.token ?? '/token',
|
|
@@ -534,7 +554,6 @@ function resolveIdpOption(idp) {
|
|
|
534
554
|
};
|
|
535
555
|
}
|
|
536
556
|
return {
|
|
537
|
-
kind: 'dex',
|
|
538
557
|
dex: {
|
|
539
558
|
...(idp?.expiry ? { expiry: idp.expiry } : {}),
|
|
540
559
|
users: (idp?.users ?? DefaultDexUsers).map((user) => ({
|
|
@@ -547,11 +566,10 @@ function resolveIdpOption(idp) {
|
|
|
547
566
|
};
|
|
548
567
|
}
|
|
549
568
|
function resolveIdpConfig({ host, port, idp, }) {
|
|
550
|
-
if (
|
|
569
|
+
if ('dex' in idp) {
|
|
551
570
|
const issuer = `https://${host}:${port}/dex`;
|
|
552
571
|
const upstream = `http://dex:5556/dex`;
|
|
553
572
|
return {
|
|
554
|
-
kind: 'dex',
|
|
555
573
|
upstream: 'dex',
|
|
556
574
|
issuer,
|
|
557
575
|
authorization: `${issuer}/auth`,
|
|
@@ -569,7 +587,6 @@ function resolveIdpConfig({ host, port, idp, }) {
|
|
|
569
587
|
};
|
|
570
588
|
}
|
|
571
589
|
return {
|
|
572
|
-
kind: 'external',
|
|
573
590
|
upstream: 'idp',
|
|
574
591
|
issuer: idp.issuer,
|
|
575
592
|
authorization: idp.issuer + (idp.authorization ?? '/auth'),
|
|
@@ -583,7 +600,6 @@ function resolveExternalIdpUpstream(idp) {
|
|
|
583
600
|
host: issuer.hostname,
|
|
584
601
|
scheme: issuer.protocol === 'https:' ? 'https' : 'http',
|
|
585
602
|
port: Number(issuer.port) || (issuer.protocol === 'https:' ? 443 : 80),
|
|
586
|
-
locations: { [issuer.pathname]: { auth: { enabled: false } } },
|
|
587
603
|
};
|
|
588
604
|
}
|
|
589
605
|
function resolveConfig(options, config, vitePort) {
|
|
@@ -591,9 +607,9 @@ function resolveConfig(options, config, vitePort) {
|
|
|
591
607
|
const upstreams = {
|
|
592
608
|
oauth2_proxy: BaseOauth2ProxyUpstream,
|
|
593
609
|
vite: { ...BaseViteUpstream, port: vitePort },
|
|
594
|
-
...(idp.
|
|
595
|
-
? {
|
|
596
|
-
: {
|
|
610
|
+
...(idp.dex === undefined
|
|
611
|
+
? { idp: resolveExternalIdpUpstream(idp) }
|
|
612
|
+
: { dex: BaseDexUpstream }),
|
|
597
613
|
...options.upstreams,
|
|
598
614
|
};
|
|
599
615
|
return {
|
|
@@ -605,8 +621,9 @@ function resolveConfig(options, config, vitePort) {
|
|
|
605
621
|
cache: join(config.cacheDir, 'visage'),
|
|
606
622
|
files: { ...BaseFiles },
|
|
607
623
|
services: {
|
|
608
|
-
...(idp.
|
|
609
|
-
?
|
|
624
|
+
...(idp.dex === undefined
|
|
625
|
+
? BaseServices
|
|
626
|
+
: {
|
|
610
627
|
dex: BaseDexService,
|
|
611
628
|
nginx: {
|
|
612
629
|
...BaseServices.nginx,
|
|
@@ -618,23 +635,31 @@ function resolveConfig(options, config, vitePort) {
|
|
|
618
635
|
image: BaseServices.oauth2_proxy.image,
|
|
619
636
|
depends_on: ['dex'],
|
|
620
637
|
},
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
...options.services,
|
|
638
|
+
}),
|
|
639
|
+
...Object.fromEntries(Object.entries(options.services).map(([name, { upstream: _upstream, ...service }]) => [name, service])),
|
|
624
640
|
},
|
|
625
|
-
upstreams: Object.fromEntries(Object.entries(upstreams).map(([name, upstream]) =>
|
|
626
|
-
name
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
641
|
+
upstreams: Object.fromEntries(Object.entries(upstreams).map(([name, upstream]) => {
|
|
642
|
+
const external = options.upstreams?.[name] !== undefined &&
|
|
643
|
+
options.services[name] === undefined;
|
|
644
|
+
return [
|
|
645
|
+
name,
|
|
646
|
+
{
|
|
647
|
+
...upstream,
|
|
648
|
+
locations: Object.fromEntries(Object.entries(upstream.locations ?? {}).map(([path, policy]) => [
|
|
649
|
+
path,
|
|
650
|
+
{
|
|
651
|
+
auth: { ...DefaultProxyPolicy.auth, ...policy.auth },
|
|
652
|
+
headers: {
|
|
653
|
+
...(external
|
|
654
|
+
? { ...DefaultProxyPolicy.headers, Host: upstream.host }
|
|
655
|
+
: DefaultProxyPolicy.headers),
|
|
656
|
+
...policy.headers,
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
])),
|
|
660
|
+
},
|
|
661
|
+
];
|
|
662
|
+
})),
|
|
638
663
|
};
|
|
639
664
|
}
|
|
640
665
|
|
package/dist/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type VisageOptions = {
|
|
|
5
5
|
/**
|
|
6
6
|
* Browser-facing hostname for the local Visage HTTPS origin.
|
|
7
7
|
*
|
|
8
|
-
* @defaultValue `'
|
|
8
|
+
* @defaultValue `'localhost'`
|
|
9
9
|
*/
|
|
10
10
|
readonly host?: string;
|
|
11
11
|
/**
|
|
@@ -15,7 +15,8 @@ export type VisageOptions = {
|
|
|
15
15
|
*/
|
|
16
16
|
readonly port?: number;
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* OAuth2 Proxy session cookie settings for name, browser scope, lifetime, and
|
|
19
|
+
* refresh timing.
|
|
19
20
|
*/
|
|
20
21
|
readonly cookie?: VisageCookiePolicy;
|
|
21
22
|
/**
|
|
@@ -38,37 +39,48 @@ export type VisageOptions = {
|
|
|
38
39
|
readonly upstreams?: Record<string, VisageUpstream>;
|
|
39
40
|
};
|
|
40
41
|
/**
|
|
41
|
-
*
|
|
42
|
+
* Settings for the browser session cookie managed by OAuth2 Proxy.
|
|
43
|
+
*
|
|
44
|
+
* OAuth2 Proxy keeps OIDC session state behind this cookie. The cookie lifetime
|
|
45
|
+
* caps the browser session, while the refresh interval controls when OAuth2
|
|
46
|
+
* Proxy tries to renew token state with the provider.
|
|
42
47
|
*/
|
|
43
48
|
export type VisageCookiePolicy = {
|
|
44
49
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
50
|
+
* Browser session cookie base name. When no domains are configured, Visage
|
|
51
|
+
* renders a host-only name with a `__Host-` prefix, so the default rendered
|
|
52
|
+
* name is `__Host-session`.
|
|
48
53
|
*
|
|
49
54
|
* @defaultValue `'session'`
|
|
50
55
|
*/
|
|
51
56
|
readonly name?: string;
|
|
52
57
|
/**
|
|
53
|
-
* Maximum
|
|
58
|
+
* Maximum browser session lifetime. Rendered as OAuth2 Proxy `cookie_expire`
|
|
59
|
+
* using its duration syntax. Keep this aligned with the identity provider's
|
|
60
|
+
* refresh-token lifetime to avoid sessions that can no longer be renewed
|
|
61
|
+
* silently.
|
|
54
62
|
*
|
|
55
63
|
* @defaultValue `'8h'`
|
|
56
64
|
*/
|
|
57
65
|
readonly expire?: string;
|
|
58
66
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
67
|
+
* Session age after which OAuth2 Proxy attempts silent renewal using the
|
|
68
|
+
* stored refresh token, when one is available. Rendered as `cookie_refresh`
|
|
69
|
+
* using OAuth2 Proxy duration syntax. If upstreams validate forwarded access
|
|
70
|
+
* tokens, set this below the access-token lifetime so OAuth2 Proxy refreshes
|
|
71
|
+
* before forwarding an expired bearer token.
|
|
61
72
|
*
|
|
62
73
|
* @defaultValue `'15m'`
|
|
63
74
|
*/
|
|
64
75
|
readonly refresh?: string;
|
|
65
76
|
/**
|
|
66
|
-
* Cookie domains for sharing
|
|
67
|
-
*
|
|
77
|
+
* Cookie domains for sharing one session across hostnames. Omit for a
|
|
78
|
+
* host-only cookie.
|
|
68
79
|
*/
|
|
69
80
|
readonly domains?: readonly string[];
|
|
70
81
|
/**
|
|
71
|
-
* Cookie path scope.
|
|
82
|
+
* Cookie path scope. Keep broad enough for every protected route that should
|
|
83
|
+
* send the session.
|
|
72
84
|
*
|
|
73
85
|
* @defaultValue `'/'`
|
|
74
86
|
*/
|
|
@@ -78,11 +90,6 @@ export type VisageCookiePolicy = {
|
|
|
78
90
|
* Managed Dex identity provider options.
|
|
79
91
|
*/
|
|
80
92
|
export type VisageDexOptions = {
|
|
81
|
-
/**
|
|
82
|
-
* Selects the managed Dex provider. Omit this for the default managed Dex
|
|
83
|
-
* provider.
|
|
84
|
-
*/
|
|
85
|
-
readonly kind?: 'dex';
|
|
86
93
|
/**
|
|
87
94
|
* Token expiration and rotation settings rendered into the Dex config.
|
|
88
95
|
*/
|
|
@@ -165,10 +172,6 @@ export type VisageDexUser = {
|
|
|
165
172
|
* External OpenID Connect identity provider options.
|
|
166
173
|
*/
|
|
167
174
|
export type VisageExternalIdpOptions = {
|
|
168
|
-
/**
|
|
169
|
-
* Selects the external IdP flow.
|
|
170
|
-
*/
|
|
171
|
-
readonly kind: 'external';
|
|
172
175
|
/**
|
|
173
176
|
* OIDC issuer URL used by OAuth2 Proxy.
|
|
174
177
|
*/
|
|
@@ -244,6 +247,11 @@ export type VisageService = {
|
|
|
244
247
|
* Additional host-to-IP mappings rendered into the Compose service.
|
|
245
248
|
*/
|
|
246
249
|
readonly extra_hosts?: readonly string[];
|
|
250
|
+
/**
|
|
251
|
+
* Optional upstream override for this service. Omit this to create a default
|
|
252
|
+
* upstream from the service name.
|
|
253
|
+
*/
|
|
254
|
+
readonly upstream?: VisageUpstream;
|
|
247
255
|
};
|
|
248
256
|
/**
|
|
249
257
|
* Named proxy target that NGINX routes to for one or more locations.
|
|
@@ -251,20 +259,26 @@ export type VisageService = {
|
|
|
251
259
|
export type VisageUpstream = {
|
|
252
260
|
/**
|
|
253
261
|
* Hostname or Compose service name NGINX should proxy to.
|
|
262
|
+
*
|
|
263
|
+
* @defaultValue The upstream name.
|
|
254
264
|
*/
|
|
255
|
-
readonly host
|
|
265
|
+
readonly host?: string;
|
|
256
266
|
/**
|
|
257
267
|
* URL scheme NGINX should use when proxying to this upstream.
|
|
258
268
|
*
|
|
259
|
-
* @defaultValue `'http'`
|
|
269
|
+
* @defaultValue `'https'` for external upstreams; `'http'` otherwise.
|
|
260
270
|
*/
|
|
261
271
|
readonly scheme?: 'http' | 'https';
|
|
262
272
|
/**
|
|
263
273
|
* Port NGINX should proxy to on {@link VisageUpstream.host}.
|
|
274
|
+
*
|
|
275
|
+
* @defaultValue `80`, or `443` when {@link VisageUpstream.scheme} is `'https'`.
|
|
264
276
|
*/
|
|
265
|
-
readonly port
|
|
277
|
+
readonly port?: number;
|
|
266
278
|
/**
|
|
267
279
|
* Path-location policies for this upstream, keyed by NGINX location path.
|
|
280
|
+
*
|
|
281
|
+
* @defaultValue `/{upstreamName}/`
|
|
268
282
|
*/
|
|
269
283
|
readonly locations?: {
|
|
270
284
|
readonly [path: string]: VisageProxyPolicy;
|
|
@@ -302,7 +316,8 @@ export type VisageProxyPolicy = {
|
|
|
302
316
|
* Request headers to set when proxying to the upstream. Values may include
|
|
303
317
|
* NGINX variables. These are merged with Visage's default proxy headers:
|
|
304
318
|
* `Cookie`, `Host`, `X-Real-IP`, `X-Forwarded-For`, and
|
|
305
|
-
* `X-Forwarded-Proto`.
|
|
319
|
+
* `X-Forwarded-Proto`. `Host` defaults to the upstream host for top-level
|
|
320
|
+
* upstreams with no matching service, and to `$host` otherwise.
|
|
306
321
|
*/
|
|
307
322
|
readonly headers?: {
|
|
308
323
|
readonly [key: string]: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED