Create a web application for a small business by Next.js with Slack authentication
Tags: nextjs, node, slack
Published: 2023-05-27 11:56:54

Creating a web application, it is hard work that implementing a login authentication. Login authentication, reissuance forgotten password, password change and etc. are required. But using NextAuth.js and Slack, you don't need for preparing these yourself. It is very easy and recommended for small businesses.

## Create LOCAL server with HTTPS enabled

Slack requires that the application URL uses HTTPS, even for local development. In this case, local-ssl-proxy is easy and useful.

See also:

https://dev.to/cress/when-you-need-https-on-local-environment-local-ssl-proxy-is-the-best-solution-24n6

## Configure on Slack side

Follow quickstart.

https://api.slack.com/authentication/quickstart

### Create a client ID and a client secret

1. Click "Create a new Slack app" button.
2. In "Your Apps" page, click "Create New App" and choose "From scratch"
3. Enter your app name and pick a workspace to develop your app in.
4. Click "Create App" button.

Then, in "App Credentials" section of "Basic Information" page, you can see its client ID and client secret.

### Configure a redirect URL

1. In "Add features and functionality" section of "Basic Information" page, click "Permissions".
2. In Redirect URLs of "OAuth & Permissions" page, click "Add New Redirect URL" button.
3. Enter "https://localhost:3001" and click "Add" button.
4. Click "Save URLs" button.

Then you can use authentication by Slack.

## Create your application

See also:

https://next-auth.js.org/

As an example, create application named *slack-auth-example* by `create-next-app`.

```powershell
npx create-next-app slack-auth-example
```

Then, install packages from npm.

```powershell
npm install next-auth bootstrap react-bootstrap
```

### .env.development.local

Put information depending your environment into `.env.development.local` file as below:

```properties
SLACK_CLIENT_ID = "<Your client ID>"
SLACK_CLIENT_SECRET = "<Your client secret>"
NEXTAUTH_URL = "https://localhost:3001"
NEXTAUTH_SECRET = "changeit"
```

Values for `SLACK_CLIENT_ID` and `SLACK_CLIENT_SECRET` are what you get in "Create a client ID and a client secret" section.

### /api/auth/[...nextauth].ts

This is the minimal configuration.

```ts
import NextAuth from "next-auth/next"
import SlackProvider from "next-auth/providers/slack"

export const authOptions = {
  providers: [
    SlackProvider({
      clientId: <string>process.env.SLACK_CLIENT_ID,
      clientSecret: <string>process.env.SLACK_CLIENT_SECRET
    })
  ]
}

export default NextAuth(authOptions)
```

### pages/_app.tsx

Surround Component with SessionProvider.

```tsx
import '@/styles/globals.css'
import { SessionProvider } from 'next-auth/react'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps: { session, ...pageProps } }: AppProps) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  )
}
```

### Example: index.tsx

It shows login button. As you logged in, it shows your name and logout button.

```tsx
import React from "react"
import { signIn, signOut, useSession } from "next-auth/react"
import { Button } from "react-bootstrap"
import "bootstrap/dist/css/bootstrap.min.css"

export default function Home() {
  const {data: session, status} = useSession()

  if (status === "loading") {
    return <p>Loading...</p>
  }

  if (status === "authenticated") {
    return (
      <>
        <p>You are logged in as {session.user?.name}</p>
        <Button onClick={() => signOut()}>Logout</Button>
      </>
    )
  } else {
    return (
      <Button onClick={() => signIn("slack")}>Login</Button>
    )
  }
}
```

## Integration with Prisma

NextAuth.js has an adapter for using Prisma. Sessions can be managed in databases.

https://authjs.dev/reference/adapter/prisma

### Installation

```powershell
npm install next-auth @prisma/client @next-auth/prisma-adapter
npm install -D prisma
```

### Prisma setup

An example using MySQL is shown.

```powershell
npx prisma init --datasource-provider mysql
```

Then, edit `prisma.schema` file as below:

```
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mys"
  url      = env("DATABASE_URL")
}

model Account {
  id                 String  @id @default(cuid())
  userId             String
  type               String
  provider           String
  providerAccountId  String
  ok                 Boolean?
  refresh_token      String?  @db.Text
  access_token       String?  @db.Text
  expires_at         Int?
  token_type         String?
  scope              String?
  id_token           String?  @db.Text
  state              String?
  session_state      String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}
```

Edit following line in `.env.development.local` file according to your environment.

```properties
DATABASE_URL="mysql://johndoe:randompassword@localhost:3306/mydb"
```

Run a command as below:

```powershell
npx prisma generate
```

Now Prisma client has been generated.

### /api/auth/[...nextauth].ts

Add `adapter` property to `authOptions`.

```ts
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"
import NextAuth from "next-auth/next"
import SlackProvider from "next-auth/providers/slack"

const prisma = new PrismaClient()

export const authOptions = {
  adapter: PrismaAdapter(prisma),
  providers: [
    SlackProvider({
      clientId: <string>process.env.SLACK_CLIENT_ID,
      clientSecret: <string>process.env.SLACK_CLIENT_SECRET
    })
  ]
}

export default NextAuth(authOptions)
```

### Initialize database

```powershell
npx prisma migrate dev --name init
```

## For your information

Don't worry that outsiders can't log in even if they have an other domain Slack account. Because the client ID you got is tied to your Slack domain, only your Slack domain member can log in. It is not always necessary to hold user information in your database.