First of all, I assume a backend that control inputs to prevent XSS vulnerabilities.
In this answer @Les Hazlewood explain how to protect the JWT in the client side.
Why Same-origin policy isn't enough to prevent CSRF attacks?
Because the Same-origin Policy only applies to reading data and not writing it.
You want to avoid http://compromised.com
from making a request like this (from the user's browser):
POST https://example.com/transfer-funds
fromAccountId:1
toAccountId:666
A legit request would look like this:
POST https://example.com/transfer-funds
fromAccountId: 1
toAccountId: 666
csrfToken: 249f3c20-649b-44de-9866-4ed72170d985
You do this by demanding a value (the CSRF token) that cannot be read by an external site, ie in an HTML form value or response header.
Regarding the Origin header, older browsers don't support it, and Flash had some vulnerabilities that let the client change it. Basically you'd be trusting Adobe not to screw anything up in the future...does that sound like a good idea?
ensure that you have CSRF protection on every HTTP request
You only need CSRF protection on requests with side-effects, such as changing state or sending a message