In the prologue, Thomas Dagget (Elias Koteas) is about to be ordained as a priest, but he is stricken by horrific visions of angels at war with each other. Several years later, Thomas, having lost his faith and abandoned the church, is a detective with the Los Angeles Police Department. Two angels fall to Earth: one, Simon (Eric Stoltz), warns Thomas of coming events, before disappearing. The second, Uziel (Jeff Cadiente), tracks Simon down and attempts to kill him, but is killed himself. Investigating the disturbance, Thomas goes to Simon's apartment and finds an obituary for a recently deceased Korean War veteran named Arnold Hawthorne. Thomas finds a copy of a theology text that he himself wrote years ago. In Chimney Rock, Simon finds the veteran and removes the soul from the body.
The medical examiner informs Thomas that Uziel's body is like nothing he has seen before: it has no eyes, no signs of bone growth, hermaphroditism, and the same blood chemistry as an aborted fetus. Among the personal effects found on the body is an ancient, hand-written Bible, which includes a Twenty-Third chapter of the Book of Revelation - a chapter that does not exist in any other version. Thomas translates the chapter and learns of a second war in Heaven, instigated by a group of angels who refused to accept God's elevation of man over all other creatures, including angels. The chapter includes a prophecy that a "dark soul" will be found on Earth that can be used as a terrible weapon.
Unknown to Thomas, Gabriel (Christopher Walken) has arrived on Earth. Needing a human helper, Gabriel recruits Jerry (Adam Goldberg), who has been in a state of limbo ever since Gabriel prevented him from dying when he committed suicide. Jerry retrieves Uziel's belongings from the police station while Gabriel destroys Uziel's body in the morgue. After finding Hawthorne's obituary, Gabriel and Jerry head for Chimney Rock, Arizona, where Hawthorne lived. Before Gabriel arrives, Simon befriends a little girl, Mary (Moriah Shining Dove Snyder), and passes the soul on to her in order to hide it from Gabriel. Mary immediately falls ill and is taken care of by her teacher, Katherine (Virginia Madsen).
After finding Uziel's body, Thomas decides to go to Chimney Rock. Gabriel and Jerry locate Hawthorne's body, but the soul is gone. Gabriel locates Simon by scent and confronts him at the school; Simon refuses to tell him where he hid the soul. Gabriel says Hawthorne's soul will end the stalemate, tipping the balance to whichever side possesses it. He then kills Simon by setting him aflame and ripping the heart from his chest.
Mary begins showing signs of being possessed by Hawthorne's soul. Meanwhile, Thomas arrives in Chimney Rock, examines Simon's remains, and questions Katherine. He searches Hawthorne's home and finds evidence of a court martial in which Hawthorne was accused of a number of war crimes, including cannibalism. Thomas visits a church to reflect and is shaken by a confrontation with Gabriel.
Katherine arrives at school the next day to find Gabriel questioning the children. He leaves, and she rushes to Mary's home to find Thomas there. Mary's condition is worsening. Katherine takes Thomas to an abandoned mine where she saw Gabriel's car parked. Inside, they find angelic script on the walls and they experience a vision of a battlefield strewn with dead angels. Thomas and Katherine rush back to Mary's home. Gabriel and Jerry are there, and Gabriel is attempting to extract the soul. Thomas kills Jerry, and Katherine blows up Mary's trailer home by shooting a propane cylinder. Mary informs Thomas that angels aren't immortal on Earth; they can be killed by tearing out their heart. Katherine takes Thomas and Mary to a Native American site where a ritual is to be performed to expel Hawthorne's soul from Mary. Gabriel recruits another "assistant", Rachael (Amanda Plummer), from the terminal unit of the ICU at a nearby hospital.
That night, as Mary starts to undergo her exorcism, Katherine is confronted by Lucifer (Viggo Mortensen) himself. Lucifer tells Katherine that "other angels" have taken up this war against mankind, and since then, no human souls have been able to "meet their maker". He knows of Gabriel's plot to use Hawthorne's soul to overthrow the agents of Heaven, and he knows that if Gabriel wins, his "new" Heaven will ultimately devolve into another Hell, resulting in unwanted competition. The next day, Lucifer appears to Thomas and advises him to use Gabriel's lack of faith against him. Gabriel arrives and attempts to disrupt the ritual. Thomas stops him, kills Rachael and then subdues Gabriel.
As the ritual continues, Lucifer appears and confronts Gabriel, ripping out the angel's heart and devouring it. Once the tribal ancestors complete the exorcism, Hawthorne's soul is expelled from Mary and destroyed. With the threat eliminated, Lucifer asks Thomas and Katherine to "come home" with him, but they refuse. The film closes with Lucifer returning to Hell, and Thomas commenting on the nature of faith and what it means to truly be human.