Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a JWT for use with Apple Music

Tags:

ruby

jwt

Im trying to create a developer token that is a ES256 JWT to use for Apple Music authentication. (Here)

Im using ruby and the JWT gem but after creating the token I get a 401 error when authenticating with Apple Music

    require 'jwt'

    payload = {:iss => 'CapExdTeam', :iat => '1497335982', :exp => '1513112982'}

    priv = "-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU208KCg/doqiSzsVF5sknVtYSgt8/3oiYGbvryIRrzSgCgYIKoZIzj0DAQehRANCAAQfrvDWizEnWAzB2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf8dvzTOZMaC/iLqZFKSN6
-----END PRIVATE KEY-----"
    ecdsa_key = OpenSSL::PKey::EC.new(priv)

    token = JWT.encode payload, ecdsa_key, 'ES256', { :kid => "CapExedKid", :alg => "ES256" }
    puts token

    `curl -v -H 'Authorization: Bearer #{token}' "https://api.music.apple.com/v1/catalog/us/songs/203709340"

Im using the sample private key to simulate 429 error just for illustration purposes

like image 660
Johan de Klerk Avatar asked Jun 13 '17 06:06

Johan de Klerk


People also ask

How do I get an Apple music token?

To generate the user token (a MusicKit private key in a *. p8 file, a 10-digit key identifier in your Apple Developer account and your 10-digit Apple Developer Account Team ID), you can visit Apple member centre. If you have a member account, you can proceed with the generating the stuffs.

Can I use Apple Music API?

Use Apple Music API to access information about media in the Apple Music Catalog and a user's personal iCloud Music Library. Apple Music Catalog includes all resources available in Apple Music. iCloud Music Library contains only those resources the user adds to their personal library.


3 Answers

It's now also possible in pure Swift!

You first have to create a MusicKit identifier and a private key using this guide from Apple. Then a token can be easily created using Swift-JWT from IBM in pure Swift.

It's more or less just an invocation of the SwiftJWT API:

let teamId = "yourTeamID"
let keyId = "yourKeyID"
let keyFileUrl = URL(fileURLWithPath:"/pathToYour/key.p8")

struct MyClaims: Claims {
    let iss: String
    let iat: Date?
    let exp: Date?
}

let myHeader = Header(kid: keyId)
let myClaims = MyClaims(iss: teamId, iat: Date(), exp: Date() +  24 * 60 * 60)
var myJWT = SwiftJWT.JWT(header: myHeader, claims: myClaims)

let token = try! myJWT.sign(using: .es256(privateKey: try! String(contentsOf: keyFileUrl).data(using: .utf8)!))

I created a simple example and a command line tool using the Swift Package Manager: SwiftJWTSample

like image 66
Klaas Avatar answered Oct 16 '22 11:10

Klaas


I used this script and it works perfectly https://github.com/pelauimagineering/apple-music-token-generator

like image 45
Johan de Klerk Avatar answered Oct 16 '22 13:10

Johan de Klerk


Based on @DanDevine's answer, here's a more Ruby/OO approach:

require "openssl"

# Example: 
#
#  token = AppleMusic::Token.new(key_id: "...", team_id: "...", keyfile: File.new("lib/assets/AuthKey_xxxxxxx.p8"))
#  token.auth_token
#  token.auth_header
# 
module AppleMusic
  class Token
    attr_reader :key_id, :team_id, :keyfile

    # Keyfile should be an IO type that responds to `read`
    def initialize(key_id:, team_id:, keyfile:)
      @key_id = key_id
      @team_id = team_id
      @keyfile = keyfile
    end

    def auth_token
      @auth_token ||= fetch_auth_token
    end

    def auth_header
      "Bearer #{auth_token}"
    end

  protected

    def fetch_auth_token
      header = {
        typ: "JWT", # Must be specified; not in documentation
        alg: "ES256",
        kid: key_id
      }

      body = {
        iss: team_id,
        iat: Time.now.to_i,
        exp: Time.now.to_i + 43_200 # 12hrs
      }

      JWT.encode(body, auth_key, 'ES256', header)
    end

    def auth_key
      key = OpenSSL::PKey::EC.new(keyfile.read)
      key.check_key
      key
    end
  end
end
like image 42
coreyward Avatar answered Oct 16 '22 13:10

coreyward