Coding History/project

스포티파이 for Developers 튜토리얼 번역 ( 2 )

BlackBirdIT 2024. 8. 29. 09:14

PKCE를 사용한 인증 코드 플로우

PKCE를 사용한 인증 코드 플로우는 모바일 앱, 단일 페이지 웹 앱 또는 클라이언트 시크릿을 안전하게 저장할 수 없는 다른 유형의 애플리케이션에서 권장되는 인증 플로우입니다.

PKCE 확장의 구현은 다음 단계로 구성됩니다:

  1. 코드 검증기를 사용하여 코드 챌린지를 생성합니다.
  2. 사용자로부터 권한을 요청하고 인증 코드를 가져옵니다.
  3. 인증 코드를 사용하여 액세스 토큰을 요청합니다.
  4. 마지막으로, 액세스 토큰을 사용하여 API 호출을 수행합니다.

전제 조건

이 가이드는 다음을 전제로 합니다:

  • 인증 가이드를 읽었습니다.
  • 앱 가이드를 따라 앱을 생성했습니다.

예시

GitHub의 web-api-examples 저장소에서 PKCE 확장이 포함된 인증 코드 플로우를 구현한 예제 앱을 찾을 수 있습니다.

코드 검증기

PKCE 인증 플로우는 코드 검증기를 생성하는 것으로 시작됩니다. PKCE 표준에 따르면, 코드 검증기는 43자에서 128자 사이의 길이를 가진 고난도 암호화 무작위 문자열입니다(길수록 좋습니다). 이 문자열은 문자, 숫자, 밑줄, 마침표, 하이픈 또는 물결표를 포함할 수 있습니다.

코드 검증기는 다음 JavaScript 함수를 사용하여 구현할 수 있습니다:

코드 예시: JavaScript

const generateRandomString = (length) => {
  const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const values = crypto.getRandomValues(new Uint8Array(length));
  return values.reduce((acc, x) => acc + possible[x % possible.length], "");
}

const codeVerifier  = generateRandomString(64);

코드 챌린지

코드 검증기가 생성된 후, 이를 SHA256 알고리즘을 사용하여 변환(해시)해야 합니다. 이 값이 사용자 권한 요청 시 전송될 값입니다.

window.crypto.subtle.digest를 사용하여 제공된 데이터를 통해 SHA256 알고리즘을 사용하여 값을 생성합시다:

코드 예시: JavaScript

const sha256 = async (plain) => {
  const encoder = new TextEncoder()
  const data = encoder.encode(plain)
  return window.crypto.subtle.digest('SHA-256', data)
}

다음으로, base64encode라는 함수를 구현하여 sha256 함수로 계산된 다이제스트의 base64 표현을 반환합니다:

코드 예시: JavaScript

const base64encode = (input) => {
  return btoa(String.fromCharCode(...new Uint8Array(input)))
    .replace(/=/g, '')
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
}

이제 모든 조각을 결합하여 코드 챌린지 생성을 구현합니다:

코드 예시: JavaScript

const hashed = await sha256(codeVerifier)
const codeChallenge = base64encode(hashed);

사용자 권한 요청

사용자에게 권한을 요청하기 위해, /authorize 엔드포인트에 GET 요청을 보내야 합니다. 이 요청에는 인증 코드 플로우와 동일한 매개변수 외에 두 개의 추가 매개변수 code_challengecode_challenge_method가 포함되어야 합니다:

쿼리 매개변수 필수 여부
client_id 필수 애플리케이션 등록 후 생성된 클라이언트 ID
response_type 필수 code로 설정
redirect_uri 필수 사용자가 권한을 부여하거나 거부한 후 리디렉션할 URI. 이 URI는 애플리케이션을 등록할 때 지정한 리디렉션 URI 허용 목록에 입력되어 있어야 합니다. 여기서 redirect_uri의 값은 애플리케이션을 등록할 때 입력한 값과 정확히 일치해야 합니다. 대소문자, 슬래시 포함 여부 등을 포함하여 정확히 일치해야 합니다.
state 선택 사항 크로스 사이트 요청 위조와 같은 공격으로부터 보호하기 위해 이 매개변수를 사용하는 것이 강력히 권장됩니다. RFC-6749를 참조하세요.
scope 선택 사항 공백으로 구분된 스코프 목록. 스코프가 지정되지 않은 경우, 공개적으로 이용 가능한 정보에만 접근할 수 있는 권한이 부여됩니다. 즉, Spotify 데스크톱, 웹 및 모바일 플레이어에서 일반적으로 볼 수 있는 정보만 접근할 수 있습니다.
code_challenge_method 필수 S256으로 설정
code_challenge 필수 이전 단계에서 애플리케이션이 계산한 코드 챌린지로 설정

사용자 권한 요청을 위한 코드는 다음과 같습니다:

코드 예시: JavaScript

const clientId = 'YOUR_CLIENT_ID';
const redirectUri = 'http://localhost:8080';

const scope = 'user-read-private user-read-email';
const authUrl = new URL("https://accounts.spotify.com/authorize")

// 이전 단계에서 생성됨
window.localStorage.setItem('code_verifier', codeVerifier);

const params =  {
  response_type: 'code',
  client_id: clientId,
  scope,
  code_challenge_method: 'S256',
  code_challenge: codeChallenge,
  redirect_uri: redirectUri,
}

authUrl.search = new URLSearchParams(params).toString();
window.location.href = authUrl.toString();

애플리케이션은 PKCE 코드 챌린지를 생성하고 window.location 객체의 값을 업데이트하여 Spotify 인증 서버 로그인 페이지로 리디렉션합니다. 이를 통해 사용자는 애플리케이션에 대한 권한을 부여할 수 있습니다.

코드 검증기 값은 localStorage JavaScript 속성을 사용하여 로컬에 저장되며, 다음 인증 플로우 단계에서 사용됩니다.

응답

사용자가 요청한 권한을 수락하면, OAuth 서비스는 사용자를 redirect_uri 필드에 지정된 URL로 다시 리디렉션합니다. 이 콜백에는 URL에 두 개의 쿼리 매개변수가 포함됩니다:

쿼리 매개변수
code 액세스 토큰으로 교환할 수 있는 인증 코드
state 요청 시 제공한 state 매개변수의 값

그런 다음, URL을 파싱하여 code 매개변수를 가져와야 합니다:

코드 예시: JavaScript

const urlParams = new URLSearchParams(window.location.search);
let code = urlParams.get('code');

이 코드는 다음 단계에서 액세스 토큰을 요청하는 데 필요합니다.

사용자가 요청을 수락하지 않거나 오류가 발생한 경우, 응답 쿼리 문자열에는 다음 매개변수가 포함됩니다:

쿼리 매개변수
error 인증 실패 이유, 예: "access_denied"
state 요청 시 제공한 state 매개변수의 값

액세스 토큰 요청

이전 단계에서 사용자가 인증 요청을 수락한 후, 인증 코드를 액세스 토큰으로 교환할 수 있습니다. /api/token 엔드포인트에 다음 매개변수를 포함하여 POST 요청을 보내야 합니다:

본문 매개변수 필수 여부
grant_type 필수 authorization_code 값을 포함해야 합니다.
code 필수 이전 요청에서 반환된 인증 코드
redirect_uri 필수 이 매개변수는 검증에만 사용됩니다(실제로 리디렉션되지 않음). 이 매개변수의 값은 인증 코드를 요청할 때 제공한 redirect_uri 값과 정확히 일치해야 합니다.
client_id 필수 개발자 대시보드에서 제공되는 애플리케이션의 클라이언트 ID
code_verifier 필수 이전 단계에서 생성한 코드 검증기의 값

요청에는 다음 HTTP 헤더가 포함되어야 합니다:

헤더 매개변수 필수 여부
Content-Type 필수 application/x-www-form-urlencoded로 설정

토큰 요청은 다음 JavaScript 함수로 구현할 수 있습니다:

코드 예시: JavaScript

const getToken = async code => {

  // 이전 단계에서 저장됨
  let codeVerifier = localStorage.getItem('code_verifier');

  const payload = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      client_id: clientId,
      grant_type: 'authorization_code',
      code,
      redirect_uri: redirectUri,
      code_verifier: codeVerifier,
    }),
  }

  const body = await fetch(url, payload);
  const response = await body.json();

  localStorage.setItem('access_token', response.access_token);
}

응답

성공 시, 응답은 200 OK 상태를 가지며 응답 본문에 다음 JSON 데이터를 포함합니다:

타입 설명
access_token string 이후의 호출에서 예: Spotify 웹 API 서비스에 제공할 수 있는 액세스 토큰
token_type string 액세스 토큰을 사용할 수 있는 방식: 항상 "Bearer"
scope string access_token에 대해 부여된 스코프를 공백으로 구분한 목록
expires_in int 액세스 토큰이 유효한 기간(초)
refresh_token string 토큰 새로 고침을 참조하세요.

다음 단계는?

축하합니다! 액세스 토큰을 발급받았습니다. 이제 이 토큰을 어떻게 사용할 수 있는지 궁금하실 것입니다. 새로 발급된 액세스 토큰을 사용하여 API 호출을 수행하는 방법은 액세스 토큰 가이드를 참조하세요.

액세스 토큰이 만료된 경우, 사용자가 애플리케이션을 다시 인증하지 않고도 새 토큰을 발급받는 방법을 배우려면 토큰 새로 고침 가이드를 읽어보세요.

원문