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

@slack/bolt App using AWSLambdaReceiver not handling events/messages/etc. #1480

Closed
5 of 10 tasks
mrowles opened this issue Jun 8, 2022 · 9 comments
Closed
5 of 10 tasks
Labels
question M-T: User needs support to use the project

Comments

@mrowles
Copy link

mrowles commented Jun 8, 2022

Description

EDIT by @seratch - This seems to be copied from #781 (comment)

I'm in this exact same boat - it's seemingly all set up correctly, but the app + awsLambdaReceiver just don't respond to any of the middleware event types. I feel as though it's exiting early. Am I doing something wrong?

import { APIGatewayProxyResult, Callback, Context } from "aws-lambda";
import { APIGatewayProxyEvent } from "aws-lambda/trigger/api-gateway-proxy";
import { App, AwsLambdaReceiver } from "@slack/bolt";

const app = (receiver: AwsLambdaReceiver) => {
  return new App({
    token: process.env.SLACK_BOT_TOKEN,
    receiver,
    processBeforeResponse: true,
  });
};

const awsLambdaReceiver = () => {
  return new AwsLambdaReceiver({
    signingSecret: process.env.SLACK_SIGNING_SECRET,
  });
};

export async function events(
  event: APIGatewayProxyEvent,
  context: Context,
  callback: Callback
): Promise<APIGatewayProxyResult> {
  try {
    console.log(event); // <-- this logs with an event
    const awsLambdaReceiverInstance = awsLambdaReceiver();
    const appInstance = app(awsLambdaReceiverInstance);

    appInstance.message("goodbye", async ({ message, say }) => {
      console.log("message goodbye"); // <-- never gets logged
      const user = "user" in message ? `<@${message.user}>` : "legend";

      await say(`See ya later, ${user} :wave:`);
    });

    const handler = await awsLambdaReceiverInstance.start();
    return handler(event, context, callback); // <--no errors, runs to completion
  } catch (err) {
    throw new Error("Could not handle Slack event");
  }
}

Note: the team has been OAuth'd separately, should I be passing in my installation store to either app or awsLambdaReceiver?

Bonus OAuthv2 manual process because Express store didn't work well:

import axios from "axios";
import { Installation } from "@slack/oauth/dist/installation";
import { installationStore } from "./slack/persistence/installation-store";

export const exchangeOauthToken = async (args: {
  workspaceId: string;
  code: string;
}): Promise<boolean> => {
  try {
    const { workspaceId, code } = args;

    const details = {
      client_id: process.env.SLACK_CLIENT_ID,
      client_secret: process.env.SLACK_CLIENT_SECRET,
      code,
      grant_type: "authorization_code",
      redirect_uri: `https://my-app.com/slack/oauth`,
    };

    const formBody = Object.entries(details)
      .map(
        ([key, value]) =>
          encodeURIComponent(key) + "=" + encodeURIComponent(value)
      )
      .join("&");

    const slackOauthV2Uri = "https://slack.com/api/oauth.v2.access";

    const result = await axios.post(slackOauthV2Uri, formBody, {
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
    });

    if (!result.data.ok) {
      throw new Error(
        result.data.error || "Bad result from Slack OAuth exchange"
      );
    }

    const {
      team,
      enterprise,
      user,
      bot,
      authed_user: authedUser,
      access_token: accessToken,
      bot_user_id: botUserId,
      incoming_webhook: incomingWebhook,
      app_id: appId,
      token_type: tokenType,
      enterprise_url: enterpriseUrl,
      is_enterprise_install: isEnterpriseInstall,
      auth_version: authVersion,
      metadata,
    } = result.data;

    const installation: Installation = {
      team,
      enterprise,
      user: {
        token: user?.token,
        refreshToken: user?.refresh_token,
        expiresAt: user?.expires_at,
        scopes: user?.scopes,
        id: user?.id,
      },
      bot: {
        token: bot?.token,
        refreshToken: bot?.refresh_token,
        expiresAt: bot?.expires_at,
        scopes: bot?.scopes,
        id: bot?.id,
        userId: bot?.user_id,
      },
      incomingWebhook: {
        channel: incomingWebhook?.channel,
        channelId: incomingWebhook?.channel_id,
        configurationUrl: incomingWebhook?.configuration_url,
        url: incomingWebhook?.url,
      },
      appId,
      tokenType,
      enterpriseUrl,
      isEnterpriseInstall,
      authVersion,
      metadata: metadata || `{"workspaceId": "${workspaceId}"}`, // <-- hack to make my installation store scalable
    };

    await installationStore.storeInstallation(installation);

    return true;
  } catch (error) {
    throw new Error("Could not exchange Slack OAuth token");
  }
};

What type of issue is this? (place an x in one of the [ ])

  • bug
  • enhancement (feature request)
  • question
  • documentation related
  • example code related
  • testing related
  • discussion

Requirements (place an x in each of the [ ])

  • I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've searched for any related issues and avoided creating a duplicate issue.

Bug Report

Filling out the following details about bugs will help us solve your issue sooner.

Reproducible in:

package version:

@slack/bolt: 3.11.0
@slack/oauth: 2.5.2

node version:
16

OS version(s):
Local: MacOS 12.4
Production: AWS Linux

Steps to reproduce:

  1. post a slack message (see above listener middleware)

Expected result:

Our AWS Lambda would respond in Slack chat

Actual result:

Nothing happens in Slack

Attachments:

Logs, screenshots, screencast, sample project, funny gif, etc.

@seratch seratch added question M-T: User needs support to use the project needs info An issue that is claimed to be a bug and hasn't been reproduced, or otherwise needs more info labels Jun 8, 2022
@seratch
Copy link
Member

seratch commented Jun 8, 2022

Hi @mrowles,

return handler(event, context, callback); // <--no errors, runs to completion

The handler is an async function. Adding await to the function call may help.

@mrowles
Copy link
Author

mrowles commented Jun 8, 2022

@seratch Thanks but unfortunately that did not fix it. Also, the docs suggest that you just return the handler as I've done originally.

Additionally, reference here as to why we probably don't need this.

I'm stumped!

@seratch
Copy link
Member

seratch commented Jun 8, 2022

@mrowles I may be able to find time to check this issue tomorrow in my timezone (+09:00) but before that, I would like to suggest the following first:

  • Add logging to see the result value from the handler function call
  • Enable debug-level logging by passing logLevel argument to the App constructor

For the first one, the following code should provide more info to you (I haven't verified if this really works - if not, please adjust the code as necessary):

    const result = await handler(event, context, callback); // <--no errors, runs to completion
    console.log(JSON.stringify(result));
    return result;

@mrowles
Copy link
Author

mrowles commented Jun 8, 2022

Thanks, I tried this and got some more information:

DEBUG [DEBUG] web-api:WebClient:0 initialized
DEBUG [DEBUG] web-api:WebClient:0 apiCall('auth.test') start
DEBUG [DEBUG] web-api:WebClient:0 will perform http request
INFO { statusCode: 401, body: '' } // <-- `result`as above

The event that I print out looks well constructed, the headers are all there and the body looks well represented in terms of data.

I'm going to look into how the Slack env vars are being populated, I'm using secrets manager to retrieve them so I assume they're fine, but let me double check.

@mrowles
Copy link
Author

mrowles commented Jun 8, 2022

One thing that I was confused about is what Bot token to use - the docs suggest using the app specific one, but it would make more sense if I'm using the team/workspace specific bot token who sent the message (the one we receive when the OAuth exchange happens).

@seratch
Copy link
Member

seratch commented Jun 8, 2022

@mrowles

INFO { statusCode: 401, body: '' } // <-- resultas above

This indicates your SLACK_SIGNING_SECRET is wrong (or you are setting a different app's one). Can you double-check the value set in the env variables for your Lambda execution?

See also: https://github.com/slackapi/bolt-js/blob/%40slack/bolt%403.11.1/src/receivers/AwsLambdaReceiver.ts#L136-L138

One thing that I was confused about is what Bot token to use

If your app serves the OAuth flow for multiple workspace installation, your InstallationStore should manage the tokens to use for each incoming request from Slack. When it comes to the Lambda use case, AwsLambdaReceiver does not support OAuth endpoints. Consider switching to ExpressReceiver if you'd like to serve the OAuth flow on AWS Lambda. For more details, you can search the past questions on the topic like this.

@seratch seratch removed the needs info An issue that is claimed to be a bug and hasn't been reproduced, or otherwise needs more info label Jun 8, 2022
@mrowles
Copy link
Author

mrowles commented Jun 8, 2022

That's weird, it's showing that it's correct. I'll regenerate, redeploy and test.

Perhaps we could make a change to the body response when signing secret doesn't work - would this be a security issue to add some more verbosity to the 401 response? It could be any number of things that needs attention :/

Thanks for your help so far btw.

@seratch
Copy link
Member

seratch commented Jun 8, 2022

@mrowles We prefer adding info-level logging rather than adding more details to the response data. I will work on it later.

I think that everything is clear so far. Let me close this issue now but if you find anything further to ask / discuss, please feel free to reopen or write in.

@mrowles
Copy link
Author

mrowles commented Jun 8, 2022

Now I am getting the following errors:

ERROR [ERROR] An unhandled error occurred while Bolt processed an event
INFO { statusCode: 500, body: 'Internal server error' }

I have exposed the issue by doing this here:

appInstance.error((errorHandler) => {
    console.error(JSON.stringify(errorHandler)); <-- was missing scope `chat:write:bot`
    throw new Error(errorHandler.message);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question M-T: User needs support to use the project
Projects
None yet
Development

No branches or pull requests

2 participants