diff --git a/src/common/auth/channel.ts b/src/common/auth/channel.ts new file mode 100644 index 0000000000..a43fca430e --- /dev/null +++ b/src/common/auth/channel.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token"; + +// This channel retreives the value needed to grant authentication to requests +export const lensAuthenticationChannel: RequestChannel = { + id: "lens-authentication-channel", +}; diff --git a/src/common/vars/auth-header.ts b/src/common/vars/auth-header.ts new file mode 100644 index 0000000000..ba945a4c52 --- /dev/null +++ b/src/common/vars/auth-header.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +/** + * This is the header name that we use for request authentication + */ +export const lensAuthenticationHeader = "LENS-AUTHENTICATION"; diff --git a/src/main/lens-proxy/auth-header-request-listener.injnectable.ts b/src/main/lens-proxy/auth-header-request-listener.injnectable.ts new file mode 100644 index 0000000000..9736fefb8f --- /dev/null +++ b/src/main/lens-proxy/auth-header-request-listener.injnectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { lensAuthenticationChannel } from "../../common/auth/channel"; +import { getRequestChannelListenerInjectable } from "../utils/channel/channel-listeners/listener-tokens"; +import authHeaderValueInjectable from "./auth-header-value.injectable"; + +const lensAuthenticationRequestListener = getRequestChannelListenerInjectable({ + channel: lensAuthenticationChannel, + handler: (di) => { + const authHeaderValue = di.inject(authHeaderValueInjectable); + + return () => authHeaderValue; + }, +}); + +export default lensAuthenticationRequestListener; diff --git a/src/main/lens-proxy/auth-header-value.injectable.ts b/src/main/lens-proxy/auth-header-value.injectable.ts new file mode 100644 index 0000000000..1506b98fe5 --- /dev/null +++ b/src/main/lens-proxy/auth-header-value.injectable.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import * as uuid from "uuid"; + +const authHeaderValueInjectable = getInjectable({ + id: "auth-header-value", + instantiate: () => uuid.v4(), +}); + +export default authHeaderValueInjectable; diff --git a/src/main/lens-proxy/lens-proxy.injectable.ts b/src/main/lens-proxy/lens-proxy.injectable.ts index 1c6444ccaa..47ea180d18 100644 --- a/src/main/lens-proxy/lens-proxy.injectable.ts +++ b/src/main/lens-proxy/lens-proxy.injectable.ts @@ -5,7 +5,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { LensProxy } from "./lens-proxy"; import { kubeApiUpgradeRequest } from "./proxy-functions"; -import routerInjectable from "../router/router.injectable"; +import routeRequestInjectable from "../router/router.injectable"; import httpProxy from "http-proxy"; import clusterManagerInjectable from "../cluster/manager.injectable"; import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable"; @@ -13,12 +13,13 @@ import lensProxyPortInjectable from "./lens-proxy-port.injectable"; import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable"; import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable"; import loggerInjectable from "../../common/logger.injectable"; +import authHeaderValueInjectable from "./auth-header-value.injectable"; const lensProxyInjectable = getInjectable({ id: "lens-proxy", instantiate: (di) => new LensProxy({ - router: di.inject(routerInjectable), + routeRequest: di.inject(routeRequestInjectable), proxy: httpProxy.createProxy(), kubeApiUpgradeRequest, shellApiRequest: di.inject(shellApiRequestInjectable), @@ -27,6 +28,7 @@ const lensProxyInjectable = getInjectable({ contentSecurityPolicy: di.inject(contentSecurityPolicyInjectable), emitAppEvent: di.inject(emitAppEventInjectable), logger: di.inject(loggerInjectable), + authHeaderValue: di.inject(authHeaderValueInjectable), }), }); diff --git a/src/main/lens-proxy/lens-proxy.ts b/src/main/lens-proxy/lens-proxy.ts index 5dc2794299..f53f717967 100644 --- a/src/main/lens-proxy/lens-proxy.ts +++ b/src/main/lens-proxy/lens-proxy.ts @@ -7,7 +7,6 @@ import net from "net"; import http from "http"; import type httpProxy from "http-proxy"; import { apiPrefix, apiKubePrefix } from "../../common/vars"; -import type { Router } from "../router/router"; import type { ClusterContextHandler } from "../context-handler/context-handler"; import type { Cluster } from "../../common/cluster/cluster"; import type { ProxyApiRequestArgs } from "./proxy-functions"; @@ -16,6 +15,10 @@ import assert from "assert"; import type { SetRequired } from "type-fest"; import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable"; import type { Logger } from "../../common/logger"; +import type { RouteRequest } from "../router/router.injectable"; +import { lensAuthenticationHeader } from "../../common/vars/auth-header"; +import { contentTypes } from "../router/router-content-types"; +import { writeServerResponseFor } from "../router/write-server-response"; type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined; @@ -26,11 +29,12 @@ interface Dependencies { shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise; kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise; emitAppEvent: EmitAppEvent; - readonly router: Router; + routeRequest: RouteRequest; readonly proxy: httpProxy; readonly lensProxyPort: { set: (portNumber: number) => void }; readonly contentSecurityPolicy: string; readonly logger: Logger; + readonly authHeaderValue: string; } const watchParam = "watch"; @@ -61,9 +65,9 @@ const disallowedPorts = new Set([ ]); export class LensProxy { - protected proxyServer: http.Server; + protected readonly proxyServer: http.Server; protected closed = false; - protected retryCounters = new Map(); + protected readonly retryCounters = new Map(); constructor(private readonly dependencies: Dependencies) { this.configureProxy(dependencies.proxy); @@ -75,6 +79,13 @@ export class LensProxy { this.proxyServer .on("upgrade", (req: ServerIncomingMessage, socket: net.Socket, head: Buffer) => { const cluster = dependencies.getClusterForRequest(req); + const authHeader = req.headers[lensAuthenticationHeader.toLowerCase()]; + + if (authHeader !== this.dependencies.authHeaderValue) { + socket.destroy(new Error("Missing authorization")); + + return; + } if (!cluster) { this.dependencies.logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`); @@ -210,13 +221,15 @@ export class LensProxy { return proxy; } - protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ClusterContextHandler): Promise { + protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ClusterContextHandler): Promise { if (req.url?.startsWith(apiKubePrefix)) { delete req.headers.authorization; req.url = req.url.replace(apiKubePrefix, ""); return contextHandler.getApiTarget(isLongRunningRequest(req.url)); } + + return undefined; } protected getRequestId(req: http.IncomingMessage): string { @@ -227,16 +240,28 @@ export class LensProxy { protected async handleRequest(req: ServerIncomingMessage, res: http.ServerResponse) { const cluster = this.dependencies.getClusterForRequest(req); + const writeServerResponse = writeServerResponseFor(res); if (cluster) { const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler); if (proxyTarget) { + const authHeader = req.headers[lensAuthenticationHeader.toLowerCase()]; + + if (authHeader !== this.dependencies.authHeaderValue) { + writeServerResponse(contentTypes.txt.resultMapper({ + statusCode: 401, + response: "Missing authorization", + })); + + return; + } + return this.dependencies.proxy.web(req, res, proxyTarget); } } res.setHeader("Content-Security-Policy", this.dependencies.contentSecurityPolicy); - await this.dependencies.router.route(cluster, req, res); + await this.dependencies.routeRequest(cluster, req, res); } } diff --git a/src/main/router/create-handler-for-route.injectable.ts b/src/main/router/create-handler-for-route.injectable.ts index 60f62fd282..a4bade4b76 100644 --- a/src/main/router/create-handler-for-route.injectable.ts +++ b/src/main/router/create-handler-for-route.injectable.ts @@ -5,73 +5,56 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { ServerResponse } from "http"; import loggerInjectable from "../../common/logger.injectable"; -import { object } from "../../common/utils"; +import { lensAuthenticationHeader } from "../../common/vars/auth-header"; +import authHeaderValueInjectable from "../lens-proxy/auth-header-value.injectable"; import type { LensApiRequest, Route } from "./route"; import { contentTypes } from "./router-content-types"; +import { writeServerResponseFor } from "./write-server-response"; export type RouteHandler = (request: LensApiRequest, response: ServerResponse) => Promise; export type CreateHandlerForRoute = (route: Route) => RouteHandler; -interface LensServerResponse { - statusCode: number; - content: any; - headers: { - [name: string]: string; - }; -} - -const writeServerResponseFor = (serverResponse: ServerResponse) => ({ - statusCode, - content, - headers, -}: LensServerResponse) => { - serverResponse.statusCode = statusCode; - - for (const [name, value] of object.entries(headers)) { - serverResponse.setHeader(name, value); - } - - if (content instanceof Buffer) { - serverResponse.write(content); - serverResponse.end(); - } else if (content) { - serverResponse.end(content); - } else { - serverResponse.end(); - } -}; - const createHandlerForRouteInjectable = getInjectable({ id: "create-handler-for-route", instantiate: (di): CreateHandlerForRoute => { const logger = di.inject(loggerInjectable); + const authHeaderValue = di.inject(authHeaderValueInjectable); return (route) => async (request, response) => { const writeServerResponse = writeServerResponseFor(response); + if (route.requireAuthentication) { + const authHeader = request.getHeader(lensAuthenticationHeader); + + if (authHeader !== authHeaderValue) { + writeServerResponse(contentTypes.txt.resultMapper({ + statusCode: 401, + response: "Missing authorization", + })); + + return; + } + } + try { const result = await route.handler(request); if (!result) { - const mappedResult = contentTypes.txt.resultMapper({ + writeServerResponse(contentTypes.txt.resultMapper({ statusCode: 204, response: undefined, - }); - - writeServerResponse(mappedResult); + })); } else if (!result.proxy) { const contentType = result.contentType || contentTypes.json; writeServerResponse(contentType.resultMapper(result)); } } catch(error) { - const mappedResult = contentTypes.txt.resultMapper({ + logger.error(`[ROUTER]: route ${route.path}, called with ${request.path}, threw an error`, error); + writeServerResponse(contentTypes.txt.resultMapper({ statusCode: 500, error: error ? String(error) : "unknown error", - }); - - logger.error(`[ROUTER]: route ${route.path}, called with ${request.path}, threw an error`, error); - writeServerResponse(mappedResult); + })); } }; }, diff --git a/src/main/router/route.ts b/src/main/router/route.ts index 90b0210746..395a1c75fc 100644 --- a/src/main/router/route.ts +++ b/src/main/router/route.ts @@ -35,6 +35,7 @@ export interface LensApiRequest { params: InferParamFromPath; cluster: Cluster | undefined; query: URLSearchParams; + getHeader: (key: string) => string | string[] | undefined; raw: { req: http.IncomingMessage; res: http.ServerResponse; @@ -65,6 +66,7 @@ export interface RouteHandler{ export interface BaseRoutePaths { path: Path; method: "get" | "post" | "put" | "patch" | "delete"; + requireAuthentication?: boolean; } export interface PayloadValidator { @@ -77,15 +79,20 @@ export interface ValidatorBaseRoutePaths extends B export interface Route extends BaseRoutePaths { handler: RouteHandler; + requireAuthentication: boolean; } export interface BindHandler { (handler: RouteHandler): Route; } -export function route(parts: BaseRoutePaths): BindHandler { +export function route({ + requireAuthentication = true, + ...parts +}: BaseRoutePaths): BindHandler { return (handler) => ({ ...parts, + requireAuthentication, handler, }); } @@ -98,8 +105,12 @@ export interface BindClusterHandler { (handler: ClusterRouteHandler): Route; } -export function clusterRoute(parts: BaseRoutePaths): BindClusterHandler { +export function clusterRoute({ + requireAuthentication = true, + ...parts +}: BaseRoutePaths): BindClusterHandler { return (handler) => ({ + requireAuthentication, ...parts, handler: ({ cluster, ...rest }) => { if (!cluster) { diff --git a/src/main/router/router.injectable.ts b/src/main/router/router.injectable.ts index 896f875332..3a3dd9d12d 100644 --- a/src/main/router/router.injectable.ts +++ b/src/main/router/router.injectable.ts @@ -2,12 +2,16 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { Injectable, InjectionToken } from "@ogre-tools/injectable"; +import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable"; import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; -import { Router } from "./router"; import parseRequestInjectable from "./parse-request.injectable"; -import type { Route } from "./route"; +import type { Route, LensApiRequest } from "./route"; import createHandlerForRouteInjectable from "./create-handler-for-route.injectable"; +import Call from "@hapi/call"; +import type http from "http"; +import type { Cluster } from "../../common/cluster/cluster"; +import type { ServerIncomingMessage } from "../lens-proxy/lens-proxy"; +import type { RouteHandler } from "./create-handler-for-route.injectable"; export const routeInjectionToken = getInjectionToken>({ id: "route-injection-token", @@ -22,14 +26,75 @@ export function getRouteInjectable( }); } -const routerInjectable = getInjectable({ - id: "router", +export type RouteRequest = (cluster: Cluster | undefined, req: ServerIncomingMessage, res: http.ServerResponse) => Promise; - instantiate: (di) => new Router({ - parseRequest: di.inject(parseRequestInjectable), - routes: di.injectMany(routeInjectionToken), - createHandlerForRoute: di.inject(createHandlerForRouteInjectable), - }), +const createRouter = (di: DiContainerForInjection) => { + const routes = di.injectMany(routeInjectionToken); + const createHandlerForRoute = di.inject(createHandlerForRouteInjectable); + const router = new Call.Router(); + + for (const route of routes) { + router.add({ method: route.method, path: route.path }, createHandlerForRoute(route)); + } + + return router; +}; + +interface RouterRequestOpts { + req: http.IncomingMessage; + res: http.ServerResponse; + cluster: Cluster | undefined; + params: Partial>; + url: URL; +} + +const getRequestWith = (di: DiContainerForInjection) => { + const parseRequest = di.inject(parseRequestInjectable); + + return async (opts: RouterRequestOpts): Promise> => { + const { req, res, url, cluster, params } = opts; + const { payload } = await parseRequest(req, null, { + parse: true, + output: "data", + }); + + return { + cluster, + path: url.pathname, + raw: { + req, res, + }, + query: url.searchParams, + payload, + params, + getHeader: (key) => req.headers[key.toLowerCase()], + }; + }; +}; + +const routeRequestInjectable = getInjectable({ + id: "route-request", + instantiate: (di): RouteRequest => { + const router = createRouter(di); + const getRequest = getRequestWith(di); + + return async (cluster, req, res) => { + const url = new URL(req.url, "http://localhost"); + const path = url.pathname; + const method = req.method.toLowerCase(); + const matchingRoute = router.route(method, path); + + if (matchingRoute instanceof Error) { + return false; + } + + const request = await getRequest({ req, res, cluster, url, params: matchingRoute.params }); + + await matchingRoute.route(request, res); + + return true; + }; + }, }); -export default routerInjectable; +export default routeRequestInjectable; diff --git a/src/main/router/router.test.ts b/src/main/router/router.test.ts index a557eaa6c1..21088f97f0 100644 --- a/src/main/router/router.test.ts +++ b/src/main/router/router.test.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import routerInjectable, { routeInjectionToken } from "./router.injectable"; +import type { RouteRequest } from "./router.injectable"; +import routeRequestInjectable, { routeInjectionToken } from "./router.injectable"; import { getDiForUnitTesting } from "../getDiForUnitTesting"; -import type { Router } from "./router"; import type { Cluster } from "../../common/cluster/cluster"; import { Request } from "mock-http"; import { getInjectable } from "@ogre-tools/injectable"; @@ -24,7 +24,7 @@ import fsInjectable from "../../common/fs/fs.injectable"; import { runInAction } from "mobx"; describe("router", () => { - let router: Router; + let routeRequest: RouteRequest; let routeHandlerMock: AsyncFnMock<() => any>; beforeEach(async () => { @@ -51,6 +51,7 @@ describe("router", () => { method: "get", path: "/some-path", handler: routeHandlerMock, + requireAuthentication: false, } as Route), injectionToken: routeInjectionToken, @@ -60,7 +61,7 @@ describe("router", () => { di.register(injectable); }); - router = di.inject(routerInjectable); + routeRequest = di.inject(routeRequestInjectable); }); afterEach(() => { @@ -86,7 +87,7 @@ describe("router", () => { clusterStub = {} as Cluster; - actualPromise = router.route(clusterStub, requestStub, responseStub); + actualPromise = routeRequest(clusterStub, requestStub, responseStub); }); it("calls handler with the request", () => { diff --git a/src/main/router/router.ts b/src/main/router/router.ts deleted file mode 100644 index 9cb070ed1d..0000000000 --- a/src/main/router/router.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import Call from "@hapi/call"; -import type http from "http"; -import type { Cluster } from "../../common/cluster/cluster"; -import type { LensApiRequest, Route } from "./route"; -import type { ServerIncomingMessage } from "../lens-proxy/lens-proxy"; -import type { ParseRequest } from "./parse-request.injectable"; -import type { CreateHandlerForRoute, RouteHandler } from "./create-handler-for-route.injectable"; - -export interface RouterRequestOpts { - req: http.IncomingMessage; - res: http.ServerResponse; - cluster: Cluster | undefined; - params: Partial>; - url: URL; -} - -interface Dependencies { - parseRequest: ParseRequest; - createHandlerForRoute: CreateHandlerForRoute; - readonly routes: Route[]; -} - -export class Router { - private readonly router = new Call.Router(); - - constructor(private readonly dependencies: Dependencies) { - for (const route of this.dependencies.routes) { - this.router.add({ method: route.method, path: route.path }, this.dependencies.createHandlerForRoute(route)); - } - } - - public async route(cluster: Cluster | undefined, req: ServerIncomingMessage, res: http.ServerResponse): Promise { - const url = new URL(req.url, "http://localhost"); - const path = url.pathname; - const method = req.method.toLowerCase(); - const matchingRoute = this.router.route(method, path); - - if (matchingRoute instanceof Error) { - return false; - } - - const request = await this.getRequest({ req, res, cluster, url, params: matchingRoute.params }); - - await matchingRoute.route(request, res); - - return true; - } - - protected async getRequest(opts: RouterRequestOpts): Promise> { - const { req, res, url, cluster, params } = opts; - const { payload } = await this.dependencies.parseRequest(req, null, { - parse: true, - output: "data", - }); - - return { - cluster, - path: url.pathname, - raw: { - req, res, - }, - query: url.searchParams, - payload, - params, - }; - } -} diff --git a/src/main/router/write-server-response.ts b/src/main/router/write-server-response.ts new file mode 100644 index 0000000000..f483f6b188 --- /dev/null +++ b/src/main/router/write-server-response.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { ServerResponse } from "http"; +import { object } from "../../common/utils"; + +export interface LensServerResponse { + statusCode: number; + content: any; + headers: { + [name: string]: string; + }; +} + +export const writeServerResponseFor = (serverResponse: ServerResponse) => ({ + statusCode, + content, + headers, +}: LensServerResponse) => { + serverResponse.statusCode = statusCode; + + for (const [name, value] of object.entries(headers)) { + serverResponse.setHeader(name, value); + } + + if (content instanceof Buffer) { + serverResponse.write(content); + serverResponse.end(); + } else if (content) { + serverResponse.end(content); + } else { + serverResponse.end(); + } +}; diff --git a/src/main/routes/files/static-file-route.injectable.ts b/src/main/routes/files/static-file-route.injectable.ts index 0268ac2051..39b2c79412 100644 --- a/src/main/routes/files/static-file-route.injectable.ts +++ b/src/main/routes/files/static-file-route.injectable.ts @@ -17,6 +17,7 @@ const staticFileRouteInjectable = getRouteInjectable({ return route({ method: "get", path: `/{path*}`, + requireAuthentication: false, })( isDevelopment ? di.inject(devStaticFileRouteHandlerInjectable) diff --git a/src/renderer/auth/auth-header-state.injectable.ts b/src/renderer/auth/auth-header-state.injectable.ts new file mode 100644 index 0000000000..c5ced2d7c0 --- /dev/null +++ b/src/renderer/auth/auth-header-state.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const authHeaderValueStateInjectable = getInjectable({ + id: "auth-header-value-state", + instantiate: () => { + let state: string | undefined = undefined; + + return { + get: () =>{ + if (!state) { + throw new Error("Tried to get auth header value before it was initialized"); + } + + return state; + }, + + set: (newState: string) => { + if (state) { + throw new Error("Tried to overwrite existing state of auth header value"); + } + + state = newState; + }, + }; + }, +}); + +export default authHeaderValueStateInjectable; + diff --git a/src/renderer/auth/auth-header.injectable.ts b/src/renderer/auth/auth-header.injectable.ts new file mode 100644 index 0000000000..7045bebf31 --- /dev/null +++ b/src/renderer/auth/auth-header.injectable.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import authHeaderValueStateInjectable from "./auth-header-state.injectable"; + +const authHeaderValueInjectable = getInjectable({ + id: "auth-header-value", + instantiate: (di) => di.inject(authHeaderValueStateInjectable).get(), +}); + +export default authHeaderValueInjectable; diff --git a/src/renderer/auth/init.injectable.ts b/src/renderer/auth/init.injectable.ts new file mode 100644 index 0000000000..0431e7aeb0 --- /dev/null +++ b/src/renderer/auth/init.injectable.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { lensAuthenticationChannel } from "../../common/auth/channel"; +import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token"; +import requestFromChannelInjectable from "../utils/channel/request-from-channel.injectable"; +import authHeaderValueStateInjectable from "./auth-header-state.injectable"; + +const initAuthHeaderValueStateInjectable = getInjectable({ + id: "init-auth-header-value-state", + instantiate: (di) => { + const state = di.inject(authHeaderValueStateInjectable); + const requestFromChannel = di.inject(requestFromChannelInjectable); + + return { + id: "init-auth-header-value-state", + run: async () => { + state.set(await requestFromChannel(lensAuthenticationChannel)); + }, + }; + }, + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default initAuthHeaderValueStateInjectable;