Sitemap

[iOS] App Store Connect API 사용하기

feat. Postman 에서 Authorization 을 위한 JWT 토큰 만들기

naljin
13 min readAug 20, 2024

들어가기 전에

개-하! 내가 App Store Connect 에 올린 앱들을 받아오려면 어떻게 하게여??

API 문서 보고 url 호출하면 됨요!

https://developer.apple.com/documentation/appstoreconnectapi/list_apps

애플에서는 다양한 App Store Connect API 를 제공하고 있기 때문에, 문서를 참고해서 알맞은 path 와 parameter 를 넣으면 됩니다

🙂‍↕️ : 오 그럼 나도ㅎ?ㅎㅎㅎ

401 ㅎㅇ

 : 어림 없지 ㅎㅎ 401이나 맞고 가라

ㅋ,,, 예,, 오랜만에 돌아와서 왜 갑자기 시비냐구여?

모든 App Store Connect API 호출은 헤더의 Authorization ES256 알고리즘으로 서명된 JWT 가 들어가야한다 를 적어두러 왔기 때문이져 (당연히 미래의 저를 위해?)

🫠 : 에..? JWT요..? 서명이요..?

🙋🏻‍♀️ : ㄱㅊㄱㅊ 별거 없어용

  1. JWT 헤더를 생성합니다.
  2. JWT 페이로드를 생성합니다.
  3. JWT에 서명합니다. (App Store Connect에서 다운로드한 개인 키로 서명)

그럼 냅다 JWT 열차 출발!! 🚃🚃🚃

0. App Store Connect API 키 생성

먼저 App Store Connect API 키부터 발급받아 봅시다!

얘로 나중에 JWT 서명할거예여! 즉, JWT 마지막에 붙는 서명 만든다는 뜻인데,, 이해 안가면 일단 넘어가져 ㄱㄱ

참고로 여기서 받은 키는 App Store Connect API 에만 고유하며 다른 Apple 서비스에는 사용할 수 없습니다.

  1. AppStore Connect 의 사용자 및 액세스 를 선택합니다.

2. 통합 > 키 > App Store Connect API 탭을 선택합니다.

3. 팀 키에서 추가(+) 버튼을 클릭합니다.

팀 키와 개별 키에는 아래와 같은 차이점이 있지만 여기서는 편의상 팀 키를 만들겠습니다. 두 차이점에 대한 자세한 내용은 Creating API Keys for App Store Connect API 을 참고하세요

- 팀 키 : 단일 앱에 격리되지 않은 접근 권한을 제공

- 개별 키 : 앱과 사용자의 권한에 연결. 개별 API 키 생성 권한이 없으면 API 키 생성 버튼이 프로필에 표시되지 않으며, 팀 관리자가 이 권한을 부여할 수 있음.

4. 키의 이름과 역할을 선택 후 생성합니다. (이름은 참조용일 뿐이며 키 자체의 일부가 아님)

5. 다운로드 해둡니다. (1회만 가능)

자 뭔가 후루룩 지나갔져? 방금한게 App Store Connect API 키를 발급받은 거구요! ! 얘 저장해뒀다가 나중에 JWT 서명하는데 사용할거예여

그럼 다음 ㄱㄱ

1. JWT 헤더 생성

JWT 는 크게 [헤더] — [페이로드]-[서명]으로 구성되어있습니다. 근데 먼저 헤더에는 뭘넣어줘야할지지,, 당연히 감도 안오져?

[헤더 📍] — [페이로드]-[서명]

하지만 웬일로 Generating Tokens for API Requests 에 예시가 잘 나와있더라구여

{
"alg": "ES256",
"kid": "2X9R4HXF34", // ✅ 변경 필요
"typ": "JWT"
}
와 근데 아직도 미디엄 표 지원 안되는거 실화예여?

여기서 변경해야할건 kid 로, 아까 키 만든 페이지에서 (AppStore Connect 사용자 및 액세스 > 통합 > 키 > App Store Connect API) 키 ID 값을 확인할 수 있습니다

요 값 복사해서 넣어주면 돼용

{
"alg": "ES256",
"kid": "App Store Connect API 에서 복사한 키 ID 값", // ✅ 변경 필요
"typ": "JWT"
}

그럼 다음 ㄱㄱ

2. JWT 페이로드 생성

JWT 는 크게 [헤더] — [페이로드]-[서명] 으로 구성한다고 했져? 그리고 방금 위에서 헤더를 채웠어요!

그럼 이제 뭐다? 페이로드를 채울 차례다~

[헤더 ✅] — [페이로드 📍]-[서명]

아까 키를 팀 키 (Team key) 로 생성했냐, 개별 키 (Individual Key) 로 생성했냐에 따라 넣어야하는 페이로드 값이 좀 다르긴 한데요, 저희는 팀 키로 생성했으니 요 기준으로 볼게요

{
"iss": "57246542-96fe-1a63-e053-0824d011072a", // ✅ 변경 필요
"iat": 1528407600, // ✅ 변경 필요
"exp": 1528408800, // ✅ 변경 필요
"aud": "appstoreconnect-v1"
}

이건 하나씩 짚고 넘어가 봅시다

iss

  • issuer 의 약어로, 토큰을 발급한 주체를 나타냅니다
  • 아까 키 만든 페이지에서 (AppStore Connect 사용자 및 액세스 > 통합 > 키 > App Store Connect API) Issuer ID 를 확인할 수 있습니다. 요 값 복사해서 넣어주면 돼용
  • 참고로 Individual key 는 iss 대신 sub 를 요구합니다. (하지만 우리는 Team key 를 기준으로 보고 있져?)

iat

  • issued at 의 약어로, 토큰이 발급된 시점을 나타냅니다
  • 토큰이 생성된 날짜와 시간을 Unix epoch time 기준으로 표시합니다 (1970년 1월 1일 기준으로 경과한 seconds)
  • https://www.unixtimestamp.com/ 에서 현재 unix timestamp 를 간단히 확인할 수 있습니다
  • 사실 얘 없이 토큰 발급하고 요청 날려도 에러는 안나더라구요? 자동으로 현재가 기준이 되는걸까요?

exp

  • expiration 의 약어로, 토큰이 만료되는 시점을 나타냅니다.
  • 토큰의 만료 시간을 Unix epoch time 기준으로 표시합니다
  • 20분 이상 지속되는 토큰은 Determine the Appropriate Token Lifetime 의 조건을 충족하는 리소스를 제외하고는 유효하지 않습니다.

💡 중요 — 유효 기간 20분이 넘어가면 다른 값 맞아도 응답에서 401 에러 뜸. 근데 저 조건 맞으면 최대 6개월까지 허용하기도

  • 토큰의 수명(lifetime)은 iatexp 의 차이로 계산됩니다
  • https://www.unixtimestamp.com/ 에서 현재 시간 + 1000 (약 16분) 정도 더해서 간단히 유효한 lifetime 의 토큰을 만들 수 있습니다

aud

  • audience의 약어로, 토큰을 수신할 것으로 예상되는 대상이나 서비스의 식별자를 포함합니다.

자 그럼 아까의 예시를 보고 적당히 값을 채워넣을 수 있겠죠???

{
"iss": "App Store Connect API 에서 복사한 Issuer ID", // ✅ 변경 필요
"iat": {토큰 발급 시간}, // ✅ 변경 필요
"exp": {20분 내의 토큰 만료 시간}, // ✅ 변경 필요
"aud": "appstoreconnect-v1"
}

사실 scope 라는 페이로드 필드를 통해, 추가적으로 토큰의 범위를 명시적으로 지정 및 토큰의 보안을 강화할 수도 있는데요! 요건 optional 이니까 슬쩍 넘어갈게요,,? ㅎ 자세한 내용은 Generating Tokens for API Requests 을 확인해주세여

3. JWT 에 서명

자자 JWT 에 헤더랑 페이로드 채워서 기깔나게 만들었잖아여?

이제 헤더에 지정한 키 ID와 연관된 개인 키를 사용하여 서명할 차례입니다!

[헤더 ✅] — [페이로드 ✅]-[서명 📍]

서명은 대략 아래와 같은 과정으로 만들어지는데요

  1. 헤더와 페이로드를 base64 로 인코딩 후 . 으로 연결
  2. 헤더에 명시된 서명 알고리즘과 비밀키를 통해 1번 값 해싱
  3. 2번에서 만들어진 해쉬값을 Base64로 한번 더 인코딩
서명알고리즘(
비밀키,
base64urlEncoding(header) + '.' + base64urlEncoding(payload)
)

JWT 토큰을 만들고 서명하기 위해서는 언어별로 사용 가능한 다양한 오픈 소스 라이브러리가 있기 때문에 알아서 잘 골라쓰시면 됩니다 ㅎㅎ

하지만 지금 제가 하고 싶은건 postman 에서 요청을 쏘고 확인하는거니까 포스트맨의 Authorization 탭 기능을 이용해볼게여!

4. Postman 을 이용한 JWT 생성 및 서명

  1. Auth Type — JWT Bearer 선택
  2. Add JWT token to — Request Header 선택
  3. Algorithm — ES256 선택 (아까 App Store Connect API의 모든 JWT는 ES256 암호화로 서명되어야 한다는 내용이 있었져?)
  4. Private key — 0. App Store Connect API 키 생성 에서 다운받은 개인 키

5. Payload — 2. JWT 페이로드 생성 에서 만든 값

{
"iss": "App Store Connect API 에서 복사한 Issuer ID", // ✅ 변경 필요
"iat": {토큰 발급 시간}, // ✅ 변경 필요
"exp": {20분 내의 토큰 만료 시간}, // ✅ 변경 필요
"aud": "appstoreconnect-v1"
}

6. Request header prefix — Bearer

왜 앞에 Bearer 을 붙여야하냐,, 하면 Generating Tokens for API Requests 문서에서 token 을 bearer token 으로 제공하라고 되어있거든여 ㅎ

bearer token 이 뭔지 궁금하신 분들은 따로 찾아보시고여!

7. JWT headers – 1. JWT 헤더 생성에서 만든 값

{
"alg": "ES256",
"kid": "App Store Connect API 에서 복사한 키 ID 값", // ✅ 변경 필요
"typ": "JWT"
}

이제 요청을 하면? 숨겨진 헤더에 Authorization 이 추가되어 정상적인 응답이 오는것을 확인할 수 있습니다.

참고로 포스트맨 콘솔 창에서는 Request Header 의 Authorization 에서 실제로 어떤 값이 들어갔나 확인할 수도 있고

혹은 스크립트의 Post-response 쪽에서 콘솔 창에 출력하는 스크립트를 작성할 수도 있습니다

let auth = pm.request.headers.get('Authorization');
console.log(auth);

4–1. iat / exp 현재 시간 기준으로 채워 넣기

App Store Connect API의 성능을 향상시키기 위해, 토큰이 만료될 때까지 동일한 서명된 토큰을 여러 요청에서 재사용하는게 좋다고합니다.

근데 이건 나중에 생각할 문제고 일단 여기까지 따라왔으면,, 지금은 iatexp 를 일일이 입력하는게 귀찮잖아여? (ex. 20분 후에는 또 iatexp 설정해야해?!)

그러니까 postman 의 Pre-request script 와 collectionVariable 을 이용해서 API 호출 할때마다 iatexp 를 현재 시간 기준으로 설정해볼거예여.

Collections > Scripts > Pre-request 에 스크립트를 작성하면 request 를 보내기 전에 요 스크립트가 일일이 먼저 수행되는데요!

여기서 currentTimestampfutureTimestamp 를 각각 현재와 5분뒤로 할당해 collection variable 에 세팅해주겠습니다.

let currentTimestamp = Math.floor(new Date().getTime() / 1000);
let futureTimestamp = currentTimestamp + 300;
pm.collectionVariables.set("currentTimestamp", currentTimestamp);
pm.collectionVariables.set("futureTimestamp", futureTimestamp);

참고로 postman 에서 collection 변수의 범위는 요렇게 돼여

이제 스크립트 한번 돌리고 나면 요렇게 Collections > Varaiables 쪽에 세팅된걸 볼 수 있답니다?

그리고 아까 Authorization 의 payload 쪽에서 아래와 같이 currentTimestamp, futureTimestamp collection 변수를 사용하게 만들면?!

{
"iss": "AApp Store Connect API 에서 복사한 Issuer ID",
"iat": {{currentTimestamp}},
"exp": {{futureTimestamp}},
"aud": "appstoreconnect-v1"
}

아까 처음에 보여드린 성공적인 response 를 확인할 수 있습니당

--

--

No responses yet