The client calculates the path, sends it over, the server verifies or rejects it, and if it rejects it tells the client to make a new path.
I ended up implementing a solution in my Dragon project. It is as described, but the server considers the path valid if the first move is valid. Each move in the path is only checked for validity by the server when the unit actually needs to move there. The client goes ahead and blindly moves the whole path. The server sends a correction if a move in the path is invalid when it is time to make that move.
One issue that came up is that, because the client is not authoritative, the client cannot decide to stop a unit from moving just because that client sees the unit is blocked. If the client did stop movement and the server decided there actually was no obstruction, then the server would not know the client is in an incorrect state. As a result, on the client, it has to be possible for two units to occupy the same map location. Without this, if a unit on the client moves on top of another unit (which can happen since the client can't make the decision to stop), the map location of the other unit gets stomped.
Maybe a better example: units A and B are moving through map location 3,7 in such a way that they just barely miss a collision from the servers point of view. A client with some latency might have the units both occupying the same location briefly, even though there was no collision. Best case is one of the units disappears and then appears again when he moves to the next location. However, if one of the units stopped on the square where a collision was barely missed, he would not reappear. If this was your unit, you'd be unable to click him to move him so he would appear!