diff --git a/web/packages/teleport/src/AppLauncher/AppLauncher.tsx b/web/packages/teleport/src/AppLauncher/AppLauncher.tsx index 72281dc4df9ea..36fba9e0248fb 100644 --- a/web/packages/teleport/src/AppLauncher/AppLauncher.tsx +++ b/web/packages/teleport/src/AppLauncher/AppLauncher.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import React, { useCallback, useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { useLocation, useParams } from 'react-router'; @@ -26,8 +26,11 @@ import { AccessDenied } from 'design/CardError'; import useAttempt from 'shared/hooks/useAttemptNext'; +import AuthnDialog from 'teleport/components/AuthnDialog'; import { UrlLauncherParams } from 'teleport/config'; +import { useMfa } from 'teleport/lib/useMfa'; import service from 'teleport/services/apps'; +import { MfaChallengeScope } from 'teleport/services/auth/auth'; export function AppLauncher() { const { attempt, setAttempt } = useAttempt('processing'); @@ -37,6 +40,20 @@ export function AppLauncher() { const queryParams = new URLSearchParams(search); const isRedirectFlow = queryParams.get('required-apps'); + const mfa = useMfa({ + req: { + scope: MfaChallengeScope.USER_SESSION, + allowReuse: false, + isMfaRequiredRequest: { + app: { + fqdn: pathParams.fqdn, + cluster_name: pathParams.clusterId, + public_addr: pathParams.publicAddr, + }, + }, + }, + }); + const createAppSession = useCallback(async (params: UrlLauncherParams) => { let fqdn = params.fqdn; const port = location.port ? `:${location.port}` : ''; @@ -101,7 +118,11 @@ export function AppLauncher() { if (params.arn) { params.arn = decodeURIComponent(params.arn); } - const session = await service.createAppSession(params); + + // Prompt for MFA if per-session MFA is required for this app. + const mfaResponse = await mfa.getChallengeResponse(); + + const session = await service.createAppSession(params, mfaResponse); // Set all the fields expected by server to validate request. const url = getXTeleportAuthUrl({ fqdn, port }); @@ -140,13 +161,18 @@ export function AppLauncher() { useEffect(() => { createAppSession(pathParams); - }, [pathParams]); - - if (attempt.status === 'failed') { - return ; - } + }, []); - return ; + return ( +
+ {attempt.status === 'failed' ? ( + + ) : ( + + )} + +
+ ); } export function AppLauncherProcessing() { diff --git a/web/packages/teleport/src/services/apps/apps.ts b/web/packages/teleport/src/services/apps/apps.ts index a8ec75c1e2eb3..aaa4316f276e2 100644 --- a/web/packages/teleport/src/services/apps/apps.ts +++ b/web/packages/teleport/src/services/apps/apps.ts @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -import api from 'teleport/services/api'; import cfg, { UrlAppParams, UrlResourcesParams } from 'teleport/config'; import { ResourcesResponse } from 'teleport/services/agents'; +import api from 'teleport/services/api'; -import auth, { MfaChallengeScope } from 'teleport/services/auth/auth'; +import { MfaChallengeResponse } from '../mfa'; import makeApp from './makeApps'; import { App } from './types'; @@ -41,27 +41,12 @@ const service = { }); }, - async createAppSession(params: UrlAppParams) { - const resolveApp = { - fqdn: params.fqdn, - cluster_name: params.clusterId, - public_addr: params.publicAddr, - }; - - // Prompt for MFA if per-session MFA is required for this app. - const challenge = await auth.getMfaChallenge({ - scope: MfaChallengeScope.USER_SESSION, - allowReuse: false, - isMfaRequiredRequest: { - app: resolveApp, - }, - }); - - const mfaResponse = await auth.getMfaChallengeResponse(challenge); - + async createAppSession( + params: UrlAppParams, + mfaResponse: MfaChallengeResponse + ) { const createAppSession = { - ...resolveApp, - arn: params.arn, + ...params, mfaResponse, // TODO(Joerger): DELETE IN v19.0.0. // We include a string version of the MFA response for backwards compatibility.