The 21-year-old Croat saved three of the four break points that he faced and took his second match point to end Federer's 20-match win streak on grass for his second career title.
Federer had been going for his 99th title one week after taking his 18th grass-court title in Stuttgart. It would have been a record-extending 10th title from his 12th final in Halle.
The Swiss great remains two match wins away from matching Jimmy Connors' career record of 174 victories on grass.
As a result of Federer's loss, Rafael Nadal will reclaim the top spot in the ATP rankings on June 25.