The Archangel Gabriel (Christopher Walken) is leading a rebellion of angels in Heaven against God because He elevated humans over angels when He gave them souls.
The War of Heaven has been at a stalemate for centuries, and Gabriel believes that an evil soul is needed to tip the battle in his favor. That soul is in the body of a recently deceased Army Colonel, who was accused of cannibalism during the Korean War. At the beginning of the movie, the angel Simon (Eric Stoltz) fights with and eventually kills another angel, Uziel, who is one of Gabriel's lieutenants. Uziel's death is investigated by LAPD Detective Thomas Daggett (Elias Koteas), a former seminary student who lost his faith.
Simon finds out the Colonel is near an Indian reservation in Arizona. He travels there, and discovers the Colonel's body at his wake. He takes the soul, and later hides it in the body of a young Indian girl named Mary. Gabriel also travels to Arizona, where he realizes the Colonel's soul has been taken. He also finds Simon, and he demands to know where the soul is. Simon refuses to tell him and is killed.
Meanwhile, Thomas's investigation points him towards angels and prophecy, especially involving Gabriel. The investigation also shows Simon was involved in the death of Uziel, and Thomas tracks him down in Arizona. There, he befriends Catherine (Virginia Madsen), Mary's schoolteacher, and later learns that Simon was killed by Gabriel. The next day, Thomas and Catherine go to her school and finds Gabriel questioning the children, where he figures out that Mary is the one with the soul. He leaves, with Thomas and Catherine following.
Gabriel goes to Mary's home, where she is sick from being possessed by the evil soul, and attempts to take it, which will kill her. Thomas fights Gabriel, and knocks him out by triggering an explosion. While Gabriel is taken into custody, Thomas and Catherine take Mary into the reservation where a ritual will be performed to safely remove the evil soul. Gabriel easily escapes police custody, and heads towards the ritual site. While at the site, Thomas and Catherine meet an unexpected ally, Lucifer (Viggo Mortensen). Before creation, Lucifer was an angel who led a rebellion against God, and was cast out after he lost and created Hell. Lucifer says that if Gabriel is successful in his rebellion, he would turn Heaven into a second Hell, and he doesn't want that. Use Gabriel's lack of faith against him, he advises, which Thomas does once Gabriel arrives and attempts to stop the ritual.
Thomas asks Gabriel why he started the war in the first place, and it's because God will no longer speak to him. Lucifer then tells Gabriel that the war is based on arrogance, which is evil and his territory, not Gabriel's. Lucifer then kills Gabriel just as the soul is expelled from Mary, which is promptly destroyed by a ray of light sent by God. The movie ends with Lucifer taking Gabriel to Hell, the recovered Mary returning home, and Thomas (with his faith restored) and Catherine getting together.