Jan De Poorter
Aug 28, 2018 · 2 min read

At work we often need information about songs, artists, albums etc. Before long we used to get this information from the iTunes API, but more and more Apple is moving services away from their over to the Apple Music API. So we decided to do the legwork and migrate our iTunes calls to Apple Music.

Getting access to the API

Access to the Apple Music API requires a signed JWT (JSON Web Token), signed by a key and details you get from Apple. They have a detailed step-by-step solution to do this over here

What you need from that process is:

  • the private key (it gets saved as AuthKey_<kid>.p8
  • your Music ID you just generated
  • your Developer Team ID (found on the Membership tab of your dev account)

Once you have all that you’re ready to go!

Generating a JSON Web Token

Ruby has the jwt gem which makes generating such a token a breeze. You simple add it to your Gemfile and you can start using it:

gem 'jwt' # Generate JSON Web Tokens

Generating the token requires a payload and a header.

authentication_payload = {
iss: 'ABCDEFG', # This is your Developer Team ID
iat: Time.now.to_i, # Issue date
exp: Time.now.to_i + 3600 # Expiry of this token.
}
# The file we got from Apple
apple_music_secret = File.read('AuthKey_MusicID.p8')
private_key = OpenSSL::PKey::EC.new(apple_music_secret)
authentication_token = JWT.encode(
authentication_payload,
private_key,
'ES256',
kid: 'YourMusicID'
)

After this you can use authentication_token in your web request headers.

req.headers["Authorization"] = "Bearer #{authentication_token}"

My AppleMusic::Client

I personally prefer to wrap this all in a client class to make this part of the process invisible to the rest of the app. I use Faraday as a HTTP client library, but this should work with any other client

class AppleMusic::Client
delegate :get, to: :client
def initialize(secret_key_path:, team_id:, music_id:, token_expire_time: 1.day)
@secret_key_path = secret_key_path
@team_id = team_id
@music_id = music_id
@token_expire_time = token_expire_time
end
private
def client
@faraday ||= QApi::Faraday.without_cache_fallback(:url => 'https://api.music.apple.com/v1/') do |builder|
builder.use FaradayMiddleware::ParseJson
builder.authorization(:bearer, authentication_token)
end
end

def authentication_payload
{
iss: @team_id,
iat: Time.now.to_i,
exp: @token_expire_time.from_now.to_i
}
end
def authentication_token
private_key = OpenSSL::PKey::EC.new(apple_music_secret_key)
JWT.encode authentication_payload, private_key, 'ES256', kid: @music_id
end
def apple_music_secret_key
@apple_music_secret_key ||= File.read(@secret_key_path)
end
end

Which I can then simple use

client = AppleMusic::Client.new(...)
client.get("catalog/be/charts", types: 'songs', limit: 100)

And with that, you’re talking to Apple Music!

Jan De Poorter

Written by

I build things on the internet. Scalable, easy-to-use, fun things.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade