diff --git a/tunnel-server/src/app.ts b/tunnel-server/src/app.ts index 671ab3f0..e969f50f 100644 --- a/tunnel-server/src/app.ts +++ b/tunnel-server/src/app.ts @@ -4,7 +4,7 @@ import http from 'http' import { Logger } from 'pino' import { KeyObject } from 'crypto' import { SessionStore } from './session' -import { Claims, cliTokenIssuer, jwtAuthenticator, saasJWTIssuer } from './auth' +import { Claims, cliIdentityProvider, jwtAuthenticator, saasIdentityProvider } from './auth' import { ActiveTunnelStore } from './tunnel-store' import { editUrl } from './url' import { Proxy } from './proxy' @@ -22,7 +22,7 @@ export const app = ({ proxy, sessionStore, baseUrl, activeTunnelStore, log, logi saasPublicKey: KeyObject jwtSaasIssuer: string }) => { - const saasIssuer = saasJWTIssuer(jwtSaasIssuer, saasPublicKey) + const saasIdp = saasIdentityProvider(jwtSaasIssuer, saasPublicKey) return Fastify({ serverFactory: handler => { const baseHostname = baseUrl.hostname @@ -83,7 +83,7 @@ export const app = ({ proxy, sessionStore, baseUrl, activeTunnelStore, log, logi if (!session.user) { const auth = jwtAuthenticator( activeTunnel.publicKeyThumbprint, - [saasIssuer, cliTokenIssuer(activeTunnel.publicKey, activeTunnel.publicKeyThumbprint)] + [saasIdp, cliIdentityProvider(activeTunnel.publicKey, activeTunnel.publicKeyThumbprint)] ) const result = await auth(req.raw) if (!result.isAuthenticated) { @@ -108,7 +108,7 @@ export const app = ({ proxy, sessionStore, baseUrl, activeTunnelStore, log, logi const auth = jwtAuthenticator( profileId, - [saasIssuer, cliTokenIssuer(tunnels[0].publicKey, tunnels[0].publicKeyThumbprint)] + [saasIdp, cliIdentityProvider(tunnels[0].publicKey, tunnels[0].publicKeyThumbprint)] ) const result = await auth(req.raw) diff --git a/tunnel-server/src/auth.ts b/tunnel-server/src/auth.ts index dfb39765..4d0d70cb 100644 --- a/tunnel-server/src/auth.ts +++ b/tunnel-server/src/auth.ts @@ -44,7 +44,7 @@ export type BearerAuthorizationHeader = { } export type AuthorizationHeader = BasicAuthorizationHeader | BearerAuthorizationHeader -export type JWTIssuer = { +export type IdentityProvider = { issuer: string publicKey: KeyObject mapClaims: (issuer: JWTPayload, context: { pkThumbprint: string }) => Claims @@ -86,7 +86,7 @@ const extractAuthorizationHeader = (req: IncomingMessage): AuthorizationHeader | export const jwtAuthenticator = ( publicKeyThumbprint: string, - issuers: JWTIssuer[] + identityProviders: IdentityProvider[] ) : Authenticator => async req => { const authHeader = extractAuthorizationHeader(req) const jwt = match(authHeader) @@ -101,12 +101,12 @@ export const jwtAuthenticator = ( const parsedJwt = decodeJwt(jwt) if (parsedJwt.iss === undefined) throw new AuthError('Could not find issuer in JWT') - const jwtIssuer = issuers.find(x => x.issuer === parsedJwt.iss) - if (!jwtIssuer) { + const idp = identityProviders.find(x => x.issuer === parsedJwt.iss) + if (!idp) { return { isAuthenticated: false } } - const { publicKey, mapClaims } = jwtIssuer + const { publicKey, mapClaims } = idp let token: JWTVerifyResult try { @@ -124,7 +124,7 @@ export const jwtAuthenticator = ( } } -export const saasJWTIssuer = (sassIssuer:string, saasPublicKey: KeyObject): JWTIssuer => ({ +export const saasIdentityProvider = (sassIssuer:string, saasPublicKey: KeyObject): IdentityProvider => ({ issuer: sassIssuer, publicKey: saasPublicKey, mapClaims: (token, { pkThumbprint: profile }) => { @@ -149,7 +149,7 @@ export const saasJWTIssuer = (sassIssuer:string, saasPublicKey: KeyObject): JWTI }, }) -export const cliTokenIssuer = (publicKey: KeyObject, publicKeyThumbprint:string): JWTIssuer => ({ +export const cliIdentityProvider = (publicKey: KeyObject, publicKeyThumbprint:string): IdentityProvider => ({ issuer: `preevy://${publicKeyThumbprint}`, publicKey, mapClaims: (token, { pkThumbprint: profile }) => ({ @@ -160,13 +160,3 @@ export const cliTokenIssuer = (publicKey: KeyObject, publicKeyThumbprint:string) sub: `preevy-profile:${profile}`, }), }) - -/* not really in use, can be if we support non-jwt authenticators -export const combineAuthenticators = (authenticators: Authenticator[]) => - async (req: IncomingMessage):Promise => { - const authInfos = (await Promise.all(authenticators.map(authn => authn(req)))) - const found = authInfos.find(info => info.isAuthenticated) - if (found !== undefined) return found - return { isAuthenticated: false } - } -*/ diff --git a/tunnel-server/src/proxy/index.ts b/tunnel-server/src/proxy/index.ts index 27ff68f5..1a50f812 100644 --- a/tunnel-server/src/proxy/index.ts +++ b/tunnel-server/src/proxy/index.ts @@ -1,5 +1,5 @@ import httpProxy from 'http-proxy' -import { IncomingMessage, ServerResponse } from 'http' +import { IncomingMessage } from 'http' import net from 'net' import type { Logger } from 'pino' import { inspect } from 'util' @@ -7,7 +7,7 @@ import { KeyObject } from 'crypto' import stream from 'stream' import { ActiveTunnel, ActiveTunnelStore } from '../tunnel-store' import { requestsCounter } from '../metrics' -import { Claims, jwtAuthenticator, AuthenticationResult, AuthError, saasJWTIssuer } from '../auth' +import { Claims, jwtAuthenticator, AuthenticationResult, AuthError, saasIdentityProvider } from '../auth' import { SessionStore } from '../session' import { BadGatewayError, BadRequestError, BasicAuthUnauthorizedError, RedirectError, UnauthorizedError, errorHandler, errorUpgradeHandler, tryHandler, tryUpgradeHandler } from '../http-server-helpers' import { TunnelFinder, proxyRouter } from './router' @@ -47,7 +47,7 @@ export const proxy = ({ theProxy.on('proxyRes', injectScripts) const loginRedirectUrlForRequest = loginRedirectUrl(loginUrl) - const saasIssuer = saasJWTIssuer(jwtSaasIssuer, saasPublicKey) + const saasIdp = saasIdentityProvider(jwtSaasIssuer, saasPublicKey) const validatePrivateTunnelRequest = async ( req: IncomingMessage, @@ -62,7 +62,7 @@ export const proxy = ({ const authenticate = jwtAuthenticator( tunnel.publicKeyThumbprint, - [saasIssuer] + [saasIdp] ) let authResult: AuthenticationResult @@ -166,7 +166,7 @@ export const proxy = ({ const { req: mutatedReq, activeTunnel } = await validateProxyRequest( tunnelFinder, req, - pkThumbprint => sessionStore(req, new ServerResponse(req), pkThumbprint), + pkThumbprint => sessionStore(req, undefined, pkThumbprint), ) const upgrade = mutatedReq.headers.upgrade?.toLowerCase() diff --git a/tunnel-server/src/session.ts b/tunnel-server/src/session.ts index e2a11b0a..a3bf947e 100644 --- a/tunnel-server/src/session.ts +++ b/tunnel-server/src/session.ts @@ -14,10 +14,10 @@ export function cookieSessionStore(opts: {domain: string; schema: z.ZodSchema const keys = opts.keys ?? [generateInsecureSecret()] return function getSession( req: IncomingMessage, - res: ServerResponse, + res: ServerResponse | undefined, thumbprint: string ) { - const cookies = new Cookies(req, res, { + const cookies = new Cookies(req, res ?? new ServerResponse(req), { secure: true, keys, })