Connecting to the Apple Music API from a Ruby (on Rails) application
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 there 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!