Dev-Event-Slack-Bot 프로젝트 - slack api 활용, slack bot distribution

정현우·2022년 11월 15일
3

Project Records

목록 보기
2/5

slack bot distribution

생각보다 험난했다 ㅎ.. slack bot을 만들어서 개인 workspace 에 귀속시켜서 사용하는 것은 간단하나 public하게 distribution 하기 위해서는 OAuth를 무조건 해야한다. 그러기 위한 re-direct url (call back url) 설정과 token관련 설정들 까지! slack bot create 부터 distribution 을 살펴보자.

깃허브에서 보기 : Dev-Event-Slack-Bot

slack-bot 만들기

  • slack은 free 티어 workspace도 만들기 쉽고 간단하다. slack app에서 설정을 타고 들어가면 slack web -> https://api.slack.com/apps?new_app=1 으로 접근해서 Create New App 을 하면 된다. 그리고 From scratch 로 직접 step by step 설정을 하는게 편하다.

  • 그 이후는 다른 많은 글을 살펴보는 게 낫다. 제일 중요하게 살펴봐야 할 부분은 "Bot Token Scopes" 부분이다.

  • 깃허브 코드 를 보면 slack_token 값이 중요하다. slack sdk 또는 rest api 를 활용할땐 만든 APP의 OAuth & Permissions > OAuth Token 이 중요하다.

  • 만든 bot은 우리 workspace에 추가하는 것이 끝이 아니라, "채널에 초대까지 해야한다" 그래야 제대로 된 채팅관련 api를 사용할 수 있다. 즉 workspace에 "초대가 가능하도록 하는 것" 이 worspace에 install 하는 것이지 그 행위가 바로 channel에 접근 가능하게 해주는 것이 아니다.

  • 그리고 우리는 slack app으로 user의 scope, auth는 전혀 상관이 없다. bot의 scope와 auth가 중요하다. slack에 수많은 token과 scope 때문에 많이 해멧다.

dev-event-slack-bot scope

  • 당연한 이야기지만 우리가 어떤 API를 사용하냐에 따라 bot의 scope는 달라진다. 해당 스코프가 굉장히 세부적으로 잘 나누어져 있어서 공식 홈페이지 에서 자세하게 확인이 가능하다.

  • dev-event-slack-bot 이 사용하는 scope 들이다. slack bot이 메시징 보내는 로직은 추가한 채널 정보를 읽고, 해당 채널 정보 기반으로 우리가 chat을 write 해야한다. 생각보다 쓸모없는 scope 도 추가해줘야 했다.

  • 깃허브 slack sdk 사용하는 코드 를 보면 왜 위와 같은 scope가 필요한지 확인이 가능하다. 사실 scope를 더 줄일 수 있었으나, 추가 확장성을 위해 optional한 method를 추가한다고 추가 scope가 따라왔다.

  • 그리고 추후에 slack 에게 public 하게 인정받은 APP 이 되려면 해당 scope에 모두 각각 적절한 이유가 필요하다.


slack-bot을 public 하게

slack 에서 app distribute

생각보다 OAuth 세팅을 하는데 좀 많이 헤멧다. token값을 어떤걸 사용해야 하는지, callback url (redirect url)에서 들어오는 데이터로 어떻게 인증해야하는지 애매했다.

  • 만든 slack-bot을 distribute 해보자! 관리자에서 "Manage distribution" 을 누른다.

  • 아래 4가지 사항을 꼭 지켜야 하는데, 제일 난해한 것이 "Add OAuth Redirect URLs" 이다. 그리고 OAuth Redirect URL은 https 여야 한다. ssl은 도메인 기준으로 발급이 되어야 한다. => domain이 필요하다.

  • 이 OAuth Redirect Urls를 필수로 설정해야만 distribution이 가능하다.

https 여야 한다, 그리고 Domain Name이 필요하다.

  • 이 부분 때문에 테스트를 하기도 난해하다. 아 물론 localhost에 local전용 SSL 인증서를 만들어서 하는 방법도 있지만 번거롭다. 그래서 선택한 방법이 공짜 도메인 + [nginx + certbot] 조합이다.

  • nginx 와 certbot 조합은 알사람은 이미 많이 아는, Let’s Encrypt 를 자동으로 발급/갱신 해주는 써드파티 S/W 이다. Let’s Encrypt는 인증서 요청 -> 도메인에 대한 소유권 확인 -> 발급 의 과정을 거치고, 더 자세한 내용은 클릭해서 확인하길 바란다. 그리고 더 자세한 certbot 활용법은 여기에서 확인 하면 좋다.

  • 깃허브 repo의 nginx-certbot 디렉토리 모두가 이를 위해 존재한다. compose 파일에서 nginx image, certbot image를 사용 했고, docker compose -f docker-compose.yml -p dev-event-bot-nginx up -d 와 같은 command로 run 한 뒤에 같은 경로에 존재하는 init-letsencrypt.sh 를 실행시켜주면 된다.

  • 해당 init shellhttps://github.com/wmnnd/nginx-certbot 를 참조해서 만들어졌다. 핵심은 pem 파일 생성 후 open ssl을 활용해 유효성 검사까지 쭉 해줍니다. 해당 shell을 실행하고 나면 (경로가 같다면) nginx-certbot/data/certbot/ 하위에 confwww 디렉토리가 추가 생성된다!

  • 이제 domain name + ssl (https) 는 simple한 상태로 (엔터프라이즈 환경에서는 주의가 필요!!) test 와 간단한 운영 레벨 정도까지는 가능하다.

OAuth callback API

  • 일단 코어가 python기반 플젝인 flask로 할까 하다가, 앱 배포를 위해 example이 가장 많고 빠르게 도입할 수 있는게 무엇이 있을까 하다가 node - express 로 결정했다. 그리고 simple한 landing이 있어야 한다. (더 자세하게는 "slack이 인증한 APP이 되려면 APP scope와 landing 등 모든 것에 대한 세부 설명이 필요"하다. 그게 아니라면 공식 인증 APP이 아니라고 뜬다.)

  • 사실 landing이 필수가 아니기 때문에 위에서 사용할 "redirect url" 에 request를 받아줄 rest API 하나만 있으면 된다. 일단 위 https + ssl과 redirect url restAPI 구성을 위해 전체 디렉토리 구성은 아래와 같이 되었다.

├── core
│   ├── __init__.py
│   ├── config.py
│   ├── db
│   │   └── mongodb.py
│   ├── exception_handler.py
│   └── slack.py
├── crawler
│   ├── __init__.py
│   └── crawler.py
├── nginx-certbot
│   ├── README.md
│   ├── data
│   │   ├── certbot
│   │   └── nginx
│   ├── docker-compose.yml
│   └── init-letsencrypt.sh
├── node_modules
├── oauth
│   ├── app.js
│   ├── node_modules
│   ├── package.json
│   ├── public
│   ├── routes
│   ├── views
│   ├── www
│   └── yarn.lock
├── main.py
├── requirements.txt
├── runtime.txt
└── secrets.json
  • oauth에 저렇게 많이 주렁주렁 달려있을 필요가 없다. 실제 필요한 API도 단일이라 그냥 app.js에 다 때려박아도 된다. git hub repo에서 oauth > wwwoauth > app.js 내용을 참고하길 바란다. API만 다뤄볼 것이다.

  • 아래는 oauth > routes > index.js 파일이다.

const { WebClient } = require('@slack/web-api');
const client = new WebClient();

router.get('/auth/slack', async (_, res) => {
    const botScopes = 'calls:write,chat:write,channels:read,groups:read,mpim:read,im:read';
    const userScopes = '';
    const clientId = process.env.SLACK_CLIENT_ID;
    const oauthUrl = `https://slack.com/oauth/v2/authorize?client_id=${clientId}&scope=${botScopes}&user_scope=${userScopes}`;
    return res.render('index', { oauthUrl });
});

router.get('/auth/slack/callback', async (req, res) => {
    try {
        const response = await client.oauth.v2.access({
            client_id: process.env.SLACK_CLIENT_ID,
            client_secret: process.env.SLACK_CLIENT_SECRET,
            code: req.query.code,
        });

        console.dir(response);

        // At this point you can assume the user has logged in successfully with their account.
        return res.status(200).send(`<html><body><p>You have successfully logged in with your slack account! Here are the details:</p><p>Response: ${JSON.stringify(response)}</p></body></html>`);
    } catch (eek) {
        console.log(eek);
        return res.status(500).send(`<html><body><p>Something went wrong!</p><p>${JSON.stringify(eek)}</p>`);
    }
});
  1. /auth/slack API는 사실 필요없는 API이다. ejs view engine을 활용해서 단순하게 /auth/slack/callback 로 가는 버튼하나 있을 뿐이다. 핵심은 여기서 세팅해주는 그 버튼의 url인데, 공식 홈페이지에서 확인 가능하다.

  2. slack 엡페이지 > Settings > Basic information > App Credentials 색션 에서 확인가능한 "Client ID" 값이 clientId 이며 url에서는 bot, user scope값이 굉장히 중요하다. client id는 url로 노출되기 때문에 엄청나게 중요한 비밀값은 아니다.

  3. 그리고 bot이랑 user scope를 헷갈리면 절대 안된다. scope값이 다르면 install 자체에서 error를 엄청 뱉게되어 있다. user scope는 user 자체를 OAuth 로 해당 workspace로 데리고오는 것이다. 그 user에게 부여되는 Auth에 관한 값이 user scope이다. 지금 목적에서는, 우리는 bot만 신경쓰면 된다.

  4. /auth/slack/callback 에서 처리가 정말 중요하다. slack에서 제공해주는 js sdk @slack/web-api 를 활용해서 처리하는게 좋다. client.oauth.v2.access 로 redirect를 통해 넘어온 request를 처리한다. "code"라는 query string을 넘기는데, 해당값은 slack에서 조합해서 넘겨주는 값이다. 우리가 신경쓸 값은 SLACK_CLIENT_SECRET 값이다.

  5. SLACK_CLIENT_SECRET 이 값때문에 고생했는데, slack에서 token값이 워낙 많기 때문에 정확하게 차이를 알아야한다.

  • crawler에서 bot에게 자기를 추가한 모든 채널에 message를 보내라는 slack API를 사용할 때는 slack 엡페이지 > Features > OAuth & Permissions 에서 OAuth Tokens for Your Workspace 색션에서 Bot User OAuth Token 값이 필요하다. 우리가 bot을 제어해서 slack API 를 사용하기 때문이다.

  • SLACK_CLIENT_SECRET은 bot install을 위해 OAuth가 필요한 상황이다. slack 엡페이지 > Settings > Basic information > App Credentials 색션 에서 아래 사진과 같은 부분이 필요하다.

  • 그리고 이건oauth.v2.access request 만을 위해서 사용되는 비밀값이다. 노출되면 안된다!

  1. 만약 user 대상으로 workspace OAuth를 만들고 있다면, user scope 값이랑 위 (5)에서 언급한 Bot User OAuth Token 이 아니라 User OAuth Token 값이 필요할 수 있다.

  2. 그리고 client.oauth.v2.access 가 정상적으로 처리가 되었으면 저렇게 처리하지말고, Deeplink - URI 스킴 방식 : 앱에 URI 스킴(scheme), slack의 스킴 slack:// 을 활용하면 더욱 깔끔하게 처리가 가능하다.

실제 적용

You have successfully logged in with your slack account! Here are the details:

Response: 
{"ok":true,"app_id":"비공개처리","authed_user":{"id":"비공개처리"},
"scope":"calls:write,chat:write,channels:read,groups:read,mpim:read,im:read",
"token_type":"bot","access_token":"비공개처리","bot_user_id":"비공개처리",
"team":{"id":"비공개처리","name":"amnotifyKR"},"enterprise":null,
"is_enterprise_install":false,"response_metadata":{}}

token 값 관련 issue


slack에 위 app install 해보기

아직은 베타라 테스트성 메시지를 수신할 수 있습니다.
아직 완벽한 Public 권한을 받지 못한 상태입니다. (2022.11.15 기준)

profile
도메인 중심의 개발, 깊이의 가치를 이해하고 “문제 해결” 에 몰두하는 개발자가 되고싶습니다. 그러기 위해 항상 새로운 것에 도전하고 노력하는 개발자가 되고 싶습니다!

0개의 댓글