트위터 Oauth 1.0a oauth_signature

milmil·2022년 7월 12일
1

트위터 봇 만들기

목록 보기
1/3

트위터 API와 Oauth 1.0

지난번에 트위터 개발자 승인받는 글을 쓴 적이 있는데 그 후로 손을 안 대고 있다가, 심심해서 무언가 만드려 보려고 문서를 뒤적거렸다. 그런데 문제가 있다. 멀쩡하게 있는 Oauth 2.0을 두고 1.0으로 인증했다.

이것은... 2007년에 나온 옛날 기술이다...
그래도 장점이 있는데 액세스 토큰을 한번 받으면 트위터에서 만료시키지 않는다.

내가 만드는 앱에 내 계정을 연동시킬 거니 평생 만료 안 된다고 하면 오히려 편할 수도 있다. 리프레시 토큰을 저장 안 해도 되고.

어떻게 인증할 수 있는지 이왕 이렇게 된 거 알아보자.

oauth signature?

const header = `OAuth oauth_consumer_key="${parameters.oauth_consumer_key}",oauth_token="${parameters.oauth_token}",oauth_signature_method="HMAC-SHA1",oauth_timestamp="${parameters.oauth_timestamp}",oauth_nonce="${parameters.oauth_nonce}",oauth_version="1.0",oauth_signature="${encoded_oauth_signature}"`;

Oauth 1.0의 헤더 Authorization에 들어가야 할 내용은 다음과 같다.

oauth_consumer_key
oauth_nonce
oauth_signature_method: 'HMAC-SHA1'
oauth_timestamp
oauth_version: '1.0'
oauth_token

Oauth

ouath_consumer_key

트위터에서 발급해주는 api key를 그대로 쓰면 된다!

oauth_nonce

내가 임의로 설정하는 문자열이다!

...
import { v4 } from 'uuid';
...
const oauth_nonce = v4();

uuid로 간단하게 만들 수 있다

oauth_signature_method

암호 알고리즘인데 'HMAC-SHA1'고정이다

oauth_timestamp

timestamp 넣어주면 된다. (예시)

const oauth_timestamp = Math.floor(+new Date() / 1000);

oauth_version

'1.0' 고정이다

oauth_token

access_token이라고 보면 된다! 이것은 토큰 발급 요청을 보내서 받아와야 한다.
발급받은 적 없다면, 아직 없다.

oauth_signature

다른 것들은 알아서 구하면 되는데 얘가 문제다. 공식문서에 보면 이, oauth_signature를 만드는 방법을 설명해주고 있지만 처음 보면 무슨 소린지 이해하기 어렵다.

요약하자면, 내가 요청할 url과, 메소드(GET, POST 등), 파라미터의 내용을 퍼센트로 인코딩한 후 하나의 문자열로 합치고, 그것을 api 비밀 키와, access_token의 비밀 키를 합친 걸로 sha1 알고리즘으로 암호화 다음 base 64로 인코딩을 하고, 퍼센트 인코딩을 한 문자열이다.

oauth_token을 빠르게 발급받기

제 3자 계정에 자동으로 글을 쓰는 봇을 만들기 위한 액세스 토큰을 발급받을 것이다. 직접 구현해도 좋지만 빠르게 발급받는 방법이 있다. 공식 post man collection을 이용하는 것이다. 우선, 개발자 계정에서 oauth 1.0을 허용하고, 권한 설정을 하고, redirect url을 설정하는 건 알아서 하자.

https://www.postman.com/twitter/workspace/twitter-s-public-workspace/collection/9956214-784efcda-ed4c-4491-a4c0-a26470a67400?ctx=documentation


Twitter OAuth 1.0a flow test를 fork하자.

step 1

consumer key = api key
onsumer secret = api secret
값을 채우고 send

step 2


step 1에서 받은 oauth_tokenoauth_token_secret을 입력한다
그리고 send를 누르면 안 된다
값을 입력하면 자동으로 완성되는 쿼리까지 다 복사해서 자신의 웹 브라우저를 통해 접속한다.그러면 트위터 로그인 창이 뜬다. 로그인이 끝나면 redirect url뒤에 붙은 oauth_tokenoauth_verifier를 확인할 수 있다.

step 3


이제 받아온 값들을 여기에 입력한다

그런데 Authorization을 No Auth로 설정했어야 됐었던 거 같음.

send를 누르면 oauth token과 oauth secret, id, name 을 받을 수 있는데 oauth token과 oauth secret을 저장해주자.

signature 만들기

import { v4 } from 'uuid';
import crypto from 'crypto';
import dotenv from 'dotenv';
import axios from 'axios';

dotenv.config();

const oauth_timestamp = Math.floor(+new Date() / 1000);
const oauth_nonce = v4();
const oauth_consumer_key = process.env.TWITTER_API_KEY;
const oauth_consumer_secret = process.env.TWITTER_API_SECRET_KEY;
const oauth_token = process.env.TWITTER_ACCESS_TOKEN;
const oauth_token_secret = process.env.TWITTER_ACEESS_TOKEN_SECRET;

필요한 모듈을 import하고, 필요한 값들을 환경변수에서 불러온다.
consumer key 은 api key
oauth token, oauth token secret 은 아까 발급받은 값들이다

const parameters = {
  oauth_consumer_key,
  oauth_nonce,
  oauth_signature_method: 'HMAC-SHA1',
  oauth_timestamp,
  oauth_version: '1.0',
  oauth_token,
};

//요청에 필요한 파라미터를 모두 모은다

이제 이 값들을 인코딩 할 건데, 트위터로 보낼 퍼센트 인코딩은,
encodeURIComponent(), encodeURI(), escape()와는 모두 결과가 달랐다. (특수문자에서 문제가 생긴다.) 나는 그래서 아래의 함수를 만들었다.

function encodeValue(text) {
  const encodedText = encodeURIComponent(text).replace(/[_!'()*]/g, function (c) {
    return '%' + c.charCodeAt(0).toString(16).toUpperCase();
  });
  return q_encoded;
}

이제 이것을 인코딩 한다

let ordered = {};
Object.keys(parameters)
  .sort()
  .forEach(function (key) {
    ordered[key] = parameters[key];
  });
//key 알파벳 순으로 정렬한다

let encodedParameters = '';

for (let key in ordered) {
  const encodedValue = encodeValue(ordered[key]);
  const encodedKey = encodeURIComponent(key);
  if (encodedParameters === '') {
    encodedParameters += `${encodedKey}=${encodedValue}`;
  } else {
    encodedParameters += `&${encodedKey}=${encodedValue}`;
  }
}
//키와 밸류를 퍼센트 인코딩

&로 연결해준다.

const method = 'POST';
const url = 'https://api.twitter.com/2/tweets';
const encodedUrl = encodeURIComponent(url);
encodedParameters = encodeURIComponent(encodedParameters);

//base string으로 합치기
const signature_base_string = `${method}&${encodedUrl}&${encodedParameters}`;

이제 method와 (대문자여야 함) url을 인코딩 하고 &로 연결한다.이것이 base string이다

다음으로는 signing_key를 만든다


//consumer secret과 oauth scret을 &로 연결
const signing_key = `${encodeURIComponent(oauth_consumer_secret)}&${encodeURIComponent(access_token_secret)}`;

그리고 암호화 하고 인코딩도 한다


//서명 키로 base string을 sha1로 암호화
const oauth_signature = crypto.createHmac('sha1', signing_key).update(signature_base_string).digest('base64');

const encoded_oauth_signature = encodeURIComponent(oauth_signature);

encoded_oauth_signature가 이제 최종 oauth signature다.

그리고 이걸 다 합쳐준다.

const header = `OAuth oauth_consumer_key="${parameters.oauth_consumer_key}",oauth_token="${parameters.oauth_token}",oauth_signature_method="HMAC-SHA1",oauth_timestamp="${parameters.oauth_timestamp}",oauth_nonce="${parameters.oauth_nonce}",oauth_version="1.0",oauth_signature="${encoded_oauth_signature}"`;

마지막으로 axios를 잘 설정한다

 const result = await axios.post(
    'https://api.twitter.com/2/tweets',
    {
      text: 'Hello world',
    },
    {
      headers: {
        Authorization: header,
      },
    }
  );

트윗이 작성된 것을 확인할 수 있다.

참고 자료

https://developer.twitter.com/en/docs/authentication/oauth-1-0a/creating-a-signature
https://sidharath.medium.com/oauth-1-0-twitter-authentication-on-nodejs-26e300957975
https://stackoverflow.com/a/51175973

profile
쓰고 싶은 글만 씀

0개의 댓글