How to authorize and post Slack messages from ASP.NET Core 💬

All of your favorite tools (elmah.io anyone?) integrate with online messaging tools like Slack. It's essential for moderne companies to be notified on Slack when new errors are logged, when a build fails, when customers are acquired, etc. In this post, I'll show you how to create an ASP.NET Core web app that lets the user authorize their Slack workspace, select a channel, and post messages to that channel from C#.

How to authorize and post Slack messages from ASP.NET Core

You probably already tried connecting online apps with Slack. You start by clicking an Authorize button, the application redirects you to Slack where you sign in and select a channel. Slack then redirects you back to the application and the integration is set up. The application now has access to post messages to the selected channel. Here's an example of how to set that up in an ASP.NET Core website.

Start by navigating to https://api.slack.com/apps and click the Create New App button. In the modal, select the From scratch option. Give the app a proper name and select a workspace. Finally, click the Create App button:

Name app & choose workspace

This will create a new Slack app. Keep the Basic Information page open, since that contains the app ID and secret which we will need in a moment.

Create a new ASP.NET Core website. For this example, I have chosen an MVC project, but it could be another project type as well. Include a big Authorize button on the front page:

<a href="/home/authorizeslack" class="btn btn-primary btn-lg">Authorize Slack</a>

Create the AuthorizeSlack action in the HomeController class:

public IActionResult AuthorizeSlack()
{
    return Redirect(
        "https://slack.com/oauth/v2/authorize?client_id=YOUR_CLIENT_IDscope=incoming-webhook");
}

The endpoint redirects the user to Slack's authorize page. You will need to replace YOUR_CLIENT_ID with the client ID found on the Basic Information page on your slack app. By including a scope of incoming-webhook, we ask Slack for permission to post messages to a Slack channel using a webhook URL.

Slack needs to know where to redirect the user once the authorization is given by the user. Navigate back to the Slack app and select the OAuth & Permission menu item. Beneath the Redirect URLs section, include the following URL:

Redirect URLs

localhost:44329 is the hostname and port number assigned to my local application. You'd want to replace those with values from your code. To make this run on both local machines and your various environments (staging, production, etc.) you can include additional redirect URLs in the list.

Go back to your code and implement the SlackCallback action in the HomeController class:

public async Task<IActionResult> SlackCallback(string state, string code = null, string error = null)
{
    // TODO
}

This is a GET endpoint that will be requested by Slack. To avoid someone requesting this page in the browser, we need to verify that the request came from Slack. To do this, set up a new HttpClient in the ConfigureServices method in the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient();
    // ...
}

Then inject a HttpClient in the HomeController constructor:

private readonly HttpClient httpClient;

public HomeController(HttpClient httpClient)
{
    this.httpClient = httpClient;
}

Include the following code in the SlackCallback method:

var response = await httpClient.GetFromJsonAsync<SlackResponse>(
    $"https://slack.com/api/oauth.v2.access?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&code={code}");
var ok = response.ok;
if (!ok) return Redirect("/");
var webhookUrl = response.incoming_webhook?.url;
if (string.IsNullOrWhiteSpace(webhookUrl)) return Redirect("/");

// TODO: Store webhookURL somewhere

The code uses the injected HttpClient to request the api/oauth.v2.access endpoint. The request includes your client IP, client secret, and the code provided for the SlackCallback action when requested by Slack. The client secret is found on your Slack app as well.

I'm using the System.Net.Http.Json helpers to map the JSON response directly to a small strongly typed object made exclusively for this one purpose:

private class SlackResponse
{
    public bool ok { get; set; }
    public Webhook incoming_webhook { get; set; }
}

private class Webhook
{
    public string url { get; set; }
}

The ok boolean indicates if Slack accepts the call or not. The incoming_webhook.url value includes an URL generated from Slack. You will need to store this URL somewhere to post messages to the chosen Slack channel.

To test the generated webhook URL, include the following code before returning from the SlackCallback method:

await httpClient.PostAsJsonAsync(webhookUrl, new { text = "Hello from app" });

This will use the generated webhook URL to post a message to Slack saying hello.

Here's the full code for the HomeController class:

public class HomeController : Controller
{
    private readonly HttpClient httpClient;

    public HomeController(HttpClient httpClient)
    {
        this.httpClient = httpClient;
    }

    public IActionResult Index()
    {
        return View();
    }

    public IActionResult AuthorizeSlack()
    {
        return Redirect(
            "https://slack.com/oauth/v2/authorize?client_id=YOUR_CLIENT_ID&scope=incoming-webhook");
    }

    private class SlackResponse
    {
        public bool ok { get; set; }
        public Webhook incoming_webhook { get; set; }
    }

    private class Webhook
    {
        public string url { get; set; }
    }

    public async Task<IActionResult> SlackCallback(string state, string code = null, string error = null)
    {
        var response = await httpClient.GetFromJsonAsync<SlackResponse>(
            $"https://slack.com/api/oauth.v2.access?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&code={code}");
        var ok = response.ok;
        if (!ok) return Redirect("/");
        var webhookUrl = response.incoming_webhook?.url;
        if (string.IsNullOrWhiteSpace(webhookUrl)) return Redirect("/");

        // Store webhook URL somewhere

        await httpClient.PostAsJsonAsync(webhookUrl, new { text = "Hello from app" });

        return Redirect("/");
    }
}

elmah.io: Error logging and Uptime Monitoring for your web apps

This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.

See how we can help you monitor your website for crashes Monitor your website