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.