Spotify refresh token 자동화하기

Sharlotte ·2023년 1월 20일
1

이전 글의 제 4안에서 수동적인 방법 으로 refresh token를 얻어 access token가 expired될 때마다 refresh하는 flow를 소개했었다.
하지만 이 수동적인 방법은 너무나도 번거로운 방법이였다.

계획

  1. https://accounts.spotify.com/authorize에 들어가 리다이렉트된 다음 authorization code를 얻는다
  2. 얻은 code로 https://accounts.spotify.com/api/token에 요청하여 refresh token를 얻는다
  3. 코드에 수동적으로 복사 붙여넣기하고 업데이트한다

구체화

직접 리다이렉트되기

https://apipheny.io/spotify-api-google-sheets/
에서 Step 4 부분에선 https://accounts.spotify.com/authorize의 폼 형식을 알려준다.

In your internet browser’s address/URL bar, copy and paste the following link:

https://accounts.spotify.com/authorize?
client_id=your_client_id&
response_type=code&
redirect_uri=https://apipheny.io/&
scope=user-read-private%20user-read-email&
state=34fFs29kd09

Make sure to replace the your_client_id parameter with your client id from Step 2.

client id와 scope를 알맞게 맞추고, state는 선택적이니 생략했다. redirect_uri는 http://localhost:3000/callback/spotify로 잠시 맞춰두었다.

처음 해당 url로 들어가면 로그인 페이지 -> auth 페이지 -> 리다이렉트 uri 로 순차적인 리다이렉트를 거친다. 하지만 쿠키에 의해 한번 로그인되었고, 한번 auth 승인을 했더라면 이 수동적인 과정이 전부 생략된다.
즉, 한번 auth하고 나면 redirect uri로 알아서 리다이렉트된다.

--> 자동으로 입장하기

하지만 직접 url를 복사하고, 브라우저를 키고, 붙여넣고, 엔터를 누르고, 뭔가가 나오길 기다리는건 번거롭다. redirect uri초기값 설정은 생각 이상으로 최대한 간단해져야 한다. 힘겨운 spotify api flow 구현의 보상 차원에서.

이를 위해 처음엔 batch script를 차용했지만 구글링을 하면서 bash script 명령어가 더 쉽게 보여서 방향성을 .bat에서 .bash로 바꿨다.
bash script에선 무려 환경변수 파일에서 환경변수를 불러올 수 있다!

scriptDir=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")

source "$scriptDir"/../.env.local
start "https://accounts.spotify.com/authorize?response_type=code&client_id=$SPOTIFY_CLIENT_ID&scope=user-read-playback-state&redirect_uri=https://sharjects-sharlottes.vercel.app/callback/spotify"

scriptDir로 현재 디렉토리 경로를 저장하고 source 명령어로 .env.local 파일을 불러와서 start 명령어로 해당 url를 열게끔 한다. source 명령어 덕분에 환경변수는 깃허브에 올라오지 않는 .env.local에 안전하게 있으므로 bash script 파일에서도 은닉성을 지키며 사용할 수 있게 됐다.

리다이렉트된 페이지 팝업화하기

callback/spotify 페이지에서 얻을건 오직 url에 담긴 code 쿼리값이다. next/routeruseRouter를 통해 파싱된 query를 얻어서 code가 있으면 클립보드에 자동 복사를 시도한다. 클립보드 복사가 성공하면 창을 닫는다.

import React from "react";
import CSR from "src/components/CSR";
import { useRouter } from "next/router";
import { copy } from "src/utils/copy";

const SpotifyCallbackPage = () => {
  const { query } = useRouter();

  React.useEffect(() => {
    if (!query["code"]) return;
    copy(query["code"].toString())
      .then(() => window.close())
      .catch(console.log);
  }, [query["code"]]);

  return <>{query["code"]}</>;
};
export default () => (
  <CSR>
    <SpotifyCallbackPage />
  </CSR>
);

단순히 복사를 하는 최신 방법은 navigator.clipboard.write이나 문제는 권한이 없거나 브라우저 호환이 안될 때 발생한다. 이번 테스트에선 사전에 복사 권한을 주지 않아 단순히 clipboard.write로는 작동하지 않았다.
관련 Stack overflow 답변이 클립보드 복사가 안되는 다양한 가능성 및 원인을 나열하고 이들을 모두 아우르는 유틸 함수를 첨부해서 큰 도움이 되었다.

Typescript의 `navigator.permissions.query 파라미터 타입 문제
타입스크립트로 위 답변에 첨부된 copy 함수를 사용한다면
'"clipboard-write"' 형식은 'PermissionName' 형식에 할당할 수 없습니다.
와 같은 타입 에러를 맞이할 것이다. 이는 MDN 호환성 표에서 clipboard-write가 파이어폭스와 사파리에서 호환되지 않기 때문이다.
단순히 as로 타입을 뭉개어 해결했다. 파이어폭스와 사파리에선 당초에 보안상 이유때문인지 불가능하고, 이건 오직 나만 쓸 것이며 난 크로미움 브라우저를 사용하기 때문이다.`

useEffect에서 document가 무조건 있어야 하므로 이 페이지는 무조건 client side에서 렌더되어야 한다. Next13에선 렌더 방식을 명시화할 수 있지만 이 프로젝트는 Next12이므로 사전에 만들어둔 CSR 컴포넌트를 통해 CSR화했다.

이제 https://accounts.spotify.com/authorize 페이지에 가기만 하면 알아서 authorzation code가 클립보드에 들어가있게 된다!

토큰 얻기!

자고로 윈도우에서 http 요청을 할 때 가장 원시적이고도 단순한 방법이 있으니, 바로 CURL이다.

response=$(curl -X POST -d grant_type=authorization_code -d code=$code -d client_id=$SPOTIFY_CLIENT_ID -d client_secret=$SPOTIFY_CLIENT_SECRET -d redirect_uri=https://sharjects-sharlottes.vercel.app/callback/spotify https://accounts.spotify.com/api/token)
if [[ "$response" =~ \"refresh_token\":\"([0-9A-Za-z_-]*)\", ]]; then
  echo "new refresh token granted!"
  echo "${BASH_REMATCH[1]}"
fi

bash는 여기에 한술 더 떠서 결과값을 쉽게 문자열화하여 변수에 할당할 수 있다.
string =~ regex는 정규표현식 테스트 연산자로써 매칭여부가 결과값이다. 매칭이 된다면 BASH_REMATCH로 매칭된 값들을 얻을 수 있는데, group expression으로 토큰 부분만 그룹으로 감싸서 따냈다. =~ 연산자와 if문에 오기까지 sed, grep, exer 등 여러 명령어들과 혼선을 겪어서 토큰 표현식이 길어졌는데 이제보니 .*로 간략화해도 될 것 같다.

결과


스크립트를 입력하고, ctrl+v로 자동복사된 코드를 붙여넣고, 출력된 refresh token를 사용하면 된다.
내심 완전한 자동화를 원했지만, 이정도로 부분적인 자동화를 구현하는 것으로도 만족한다.
스크립트 언어가 조건문과 변수 할당까지 자유로이 할 정도로 큰 세상인걸 깨달았고, CLI 구현이 마냥 먼 산이 아님을 느꼈다.

profile
샤르르르

0개의 댓글