Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Receiving unhandled error DialogContextError: Cannot find command: {0} on after Auth Prompt #12253

Closed
NWH-SAmin5 opened this issue Aug 20, 2024 · 5 comments
Assignees
Labels
investigating needs more info Need user to provide more info no recent activity The issue labeled needs more info gets no reply from issue owner in time TA:Auth Team Area: Auth

Comments

@NWH-SAmin5
Copy link

Describe the bug
I have command bot using this version, I am receiving unhandled error DialogContextError: Cannot find command: {0} right after TeamsBotSsoPrompt or OAuthPrompt I tried both. It looks like OAuthPrompt from my custom dialog is not coming back to my custom dialog and its going to BotSsoExecutionDialog.

        "@microsoft/teamsfx": "^2.3.2",
        "botbuilder": "^4.22.3",
        "botbuilder-dialogs": "^4.22.3",

Full error

[onTurnError] unhandled error DialogContextError: Cannot find command: {0}
    at <removed>\node_modules\botbuilder-dialogs\src\dialogContext.ts:31:19
    at Generator.throw (<anonymous>)
    at rejected (<removed>\node_modules\botbuilder-dialogs\lib\dialogContext.js:6:65)
    at processTicksAndRejections (node:internal/process/task_queues:95:5) {
  error: ErrorWithCode.CannotFindCommand: Cannot find command: {0}
      at BotSsoExecutionDialog.<anonymous> (<removed>\node_modules\@microsoft\teamsfx\src\conversation\sso\botSsoExecutionDialog.ts:216:11)
      at Generator.next (<anonymous>)
      at <removed>\node_modules\@microsoft\teamsfx\dist\index.node.cjs.js:206:71
      at new Promise (<anonymous>)
      at __awaiter (<removed>\node_modules\@microsoft\teamsfx\dist\index.node.cjs.js:202:12)
      at BotSsoExecutionDialog.commandRouteStep (<removed>\node_modules\@microsoft\teamsfx\dist\index.node.cjs.js:3471:16)
      at WaterfallDialog.<anonymous> (<removed>\node_modules\botbuilder-dialogs\src\waterfallDialog.ts:247:44)
      at Generator.next (<anonymous>)
      at <removed>\node_modules\botbuilder-dialogs\lib\waterfallDialog.js:8:71
      at new Promise (<anonymous>) {
    code: 'CannotFindCommand'
  },
  dialogContext: {
    activeDialog: 'CommandRouteDialog',
    parent: 'BotSsoExecutionDialog',
    stack: [ [Object] ]
  }
}

Method 1

/LoginCmd.ts (TeamsBotSsoPrompt)

export class LoginCmd implements TeamsFxBotCommandHandler {
    triggerPatterns: TriggerPatterns = [
        /^login?:?\s?(.*?)$/i
    ];

    private _conversationState: ConversationState;
    private _dialogState: StatePropertyAccessor<DialogState>;
    private readonly DIALOG_STATE = "DialogState";
    private _dialogs: DialogSet;

    constructor() {
        this._conversationState = new ConversationState(new MemoryStorage());
        this._dialogState = this._conversationState.createProperty(this.DIALOG_STATE);
        this._dialogs = new DialogSet(this._dialogState);

        const TeamsBotSsoPromptId = "TEAMS_BOT_SSO_PROMPT";

        const settings: TeamsBotSsoPromptSettings = {
            scopes: ["User.Read"],
            timeout: 900000,
            endOnInvalidMessage: true,
        };

        const authConfig: OnBehalfOfCredentialAuthConfig = oboAuthConfig;
        const loginUrl = process.env.INITIATE_LOGIN_ENDPOINT;
        const ssoPrompt = new TeamsBotSsoPrompt(authConfig, loginUrl, TeamsBotSsoPromptId, settings);
        this._dialogs.add(ssoPrompt);
        this._dialogs.add(
            new WaterfallDialog("taskNeedingLogin", [
                async (step) => {
                  return await step.beginDialog(TeamsBotSsoPromptId);
                },
                async (step) => {
                  const token = step.result;
                  if (token) {
                    // ... continue with task needing access token ...
                    await step.context.sendActivity(`Here is your token ${token.token}`);
                    return await step.endDialog();
                  } else {
                    await step.context.sendActivity(`Sorry... We couldn't log you in. Try again later.`);
                    return await step.endDialog();
                  }
                },
              ])
        );
    }

    async handleCommandReceived(context: TurnContext, message: CommandMessage): Promise<string | Partial<Activity> | void> {
        console.log(`App received message: ${message.text}`);
        await context.sendActivities([{ type: ActivityTypes.Typing }]);

        try {
            const dc = await this._dialogs.createContext(context);
            await dc.beginDialog("taskNeedingLogin");
        } catch (error) {
            console.log(error);
        }
    }
}

Method 2

/MainDialog.ts

const MAIN_DIALOG = 'MainDialog';
const OAUTH_PROMPT = 'OAuthPrompt';
const CONFIRM_PROMPT = 'ConfirmPrompt';
const MAIN_WATERFALL_DIALOG = 'MainWaterfallDialog';

export class MainDialog extends MainInnerDialog {
    constructor() {
        super(MAIN_DIALOG, "local-oauthconnection");

        this.addDialog(new OAuthPrompt(OAUTH_PROMPT, {
            connectionName: "local-oauthconnection",
            text: 'Please Sign In to your another account',
            title: 'Sign In',
            timeout: 300000
        }));
        this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT));
        this.addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [
            this.loginPromptStep.bind(this),
            this.loginCompleteStep.bind(this),
            this.ensureOAuth.bind(this),
            this.displayToken.bind(this)
        ]));

        this.initialDialogId = MAIN_WATERFALL_DIALOG;
    }

    /**
     * The run method handles the incoming activity (in the form of a DialogContext) and passes it through the dialog system.
     * If no dialog is active, it will start the default dialog.
     * @param context 
     * @param accessor 
     */
    public async run(context: TurnContext, accessor: StatePropertyAccessor) {
        console.log("run");

        const dialogSet = new DialogSet(accessor);
        dialogSet.add(this);

        const dialogContext = await dialogSet.createContext(context);
        const results = await dialogContext.continueDialog();
        if (results.status === DialogTurnStatus.empty) {
            console.log(this.id);
            await dialogContext.beginDialog(this.id);
        }
    }

    private async loginPromptStep(stepContext: WaterfallStepContext) {
        console.log("loginPromptStep");
        const result = await stepContext.beginDialog(OAUTH_PROMPT);
        return result
    }

    private async loginCompleteStep(stepContext: WaterfallStepContext) {
        console.log("loginCompleteStep");

        // Get the token from the previous step. Note that we could also have gotten the
        // token directly from the prompt itself. There is an example of this in the next method.
        const tokenResponse = stepContext.result;
        if (tokenResponse) {
            await stepContext.context.sendActivity('You are now logged in.');
            return await stepContext.prompt(CONFIRM_PROMPT, 'Would you like to view your token?');
        } else {
            return await stepContext.context.sendActivity('Login was not successful please try again.');
        }
    }
    
    async ensureOAuth(stepContext: WaterfallStepContext) {
        await stepContext.context.sendActivity('Thank you.');

        const result = stepContext.result;
        if (result) {
            // Call the prompt again because we need the token. The reasons for this are:
            // 1. If the user is already logged in we do not need to store the token locally in the bot and worry
            // about refreshing it. We can always just call the prompt again to get the token.
            // 2. We never know how long it will take a user to respond. By the time the
            // user responds the token may have expired. The user would then be prompted to login again.
            //
            // There is no reason to store the token locally in the bot because we can always just call
            // the OAuth prompt to get the token or get a new token if needed.
            return await stepContext.beginDialog(OAUTH_PROMPT);
        }
        return await stepContext.endDialog();
    }

    private async displayToken(stepContext: WaterfallStepContext) {
        console.log("displayToken");
        const tokenResponse = stepContext.result;
        if (tokenResponse) {
            await stepContext.context.sendActivity(`Here is your token ${ tokenResponse.token }`);
        }
        return await stepContext.endDialog();
    }
@microsoft-github-policy-service microsoft-github-policy-service bot added the needs attention This issue needs the attention of a contributor. label Aug 20, 2024
@adashen adashen added investigating TA:Auth Team Area: Auth labels Aug 21, 2024
@SLdragon
Copy link
Contributor

Hi, @NWH-SAmin5 , the command bot is used to receive user's command, and then send back message to user, you cannot start a dialog from command handler.
If you just want to get the token, and then start a new dialog, I suggest you refer this sample project, which uses ssoDialog to achieve this:

https://github.com/OfficeDev/teams-toolkit-samples/tree/dev/bot-sso

@adashen adashen added needs more info Need user to provide more info and removed needs attention This issue needs the attention of a contributor. labels Aug 26, 2024
@WaseekSenju
Copy link

image
image

I am getting this error on the sample project as well.

@microsoft-github-policy-service microsoft-github-policy-service bot added needs attention This issue needs the attention of a contributor. and removed needs more info Need user to provide more info labels Aug 28, 2024
@SLdragon
Copy link
Contributor

Hi @WaseekSenju , which sample project are you using? Please share more details so that we can take a look, thank you~

@adashen adashen added needs more info Need user to provide more info and removed needs attention This issue needs the attention of a contributor. labels Aug 29, 2024
Copy link
Contributor

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 7 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.

@microsoft-github-policy-service microsoft-github-policy-service bot added the no recent activity The issue labeled needs more info gets no reply from issue owner in time label Sep 6, 2024
Copy link
Contributor

Due to lack of details for further investigation, we will archive the issue for now. In case you still have following-up questions on this issue, please always feel free to reopen the issue by clicking ‘reopen issue’ button below the comment box. We will get back to you as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
investigating needs more info Need user to provide more info no recent activity The issue labeled needs more info gets no reply from issue owner in time TA:Auth Team Area: Auth
Projects
None yet
Development

No branches or pull requests

4 participants