Hi all, I am currently building a `ui` with `nuxt`...
# talk-kratos
b
Hi all, I am currently building a
ui
with
nuxt
. Most of the time I use the SDK to communicate with
Kratos
and it works fine, but to submit flows I simply use
fetch()
. No I'm facing the problem, that
Kratos
responds with a
403
, saying that the
csrf_token
is missing. I already went through the (pitfalls)[https://www.ory.sh/docs/kratos/debug/csrf#running-on-separate-sub-domains] and for me the the request headers are all set (cookie) and the request body looks fine too. Does anyone know what I'm missing? And how can I check if the csrf-token in the cookie matches the one in the form, as they look different? I am running
Kratos
and the
ui
as docker containers. Request:
Copy code
POST /?flow=d16e0089-a607-4070-b12e-90a81f07ef2b HTTP/1.1
Host: 127.0.0.1:4433
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: <http://127.0.0.1:3000/>
content-type: application/json
Content-Length: 216
Origin: <http://127.0.0.1:3000>
DNT: 1
Connection: keep-alive
Cookie: csrf_token_806060ca5bf70dff3caa0e5c860002aade9d470a5a4dce73bcfa7ba10778f481=g0nlXiujTgN6cDGdx4pqJN6oiW5SHKxutbstbG8apKw=
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Body:
Copy code
{
  "csrf_token": "AoL6XD3nMeIy8o4VtGHoamjpgAPX2DbIL3/0KvZk1xiByx8CFkR/4UiCv4hz64JOtkEJbYXEmqaaxNlGmX5ztA==",
  "method": "password",
  "password": "adw1221aS",
  "traits": {
    "email": "<mailto:ad@lo.de|ad@lo.de>",
    "name": {
      "first": "hhfr",
      "last": "hrww"
    }
  }
}
Thank you!
b
Hi Markus, could you post the full error response? It should contain a hint on what exactly went wrong in the Ory Kratos application logic.
b
Copy code
{
  "error": {
    "id": "security_csrf_violation",
    "code": 403,
    "status": "Forbidden",
    "reason": "Please retry the flow and optionally clear your cookies. The request was rejected to protect you from Cross-Site-Request-Forgery (CSRF) which could cause account takeover, leaking personal information, and other serious security issues.",
    "details": {
      "docs": "<https://www.ory.sh/kratos/docs/debug/csrf>",
      "hint": "The anti-CSRF cookie was found but the CSRF token was not included in the HTTP request body (csrf_token) nor in the HTTP Header (X-CSRF-Token).",
      "reject_reason": "The HTTP Cookie Header was set and a CSRF token was sent but they do not match. We recommend deleting all cookies for this domain and retrying the flow."
    },
    "message": "the request was rejected to protect you from Cross-Site-Request-Forgery"
  }
}
b
check that you have matched domain name between the one that generates the csrf token and the one that sends it
b
It seems like the tokens did not match, e.g. the token in the cookie was not the same as sent in the request payload.
Could you try to clear your cookies and re-start a new flow?
b
Thank you both. I checked the domains and they are the same, both requests are coming from 127.0.0.1:3000. I also cleared my cookies and retried, but it didn't work. And do the tokens in the form and cookie have to match?
b
Yes, the tokens are a mechanism to check that the form submission came from the same browser window that initiated the request. So they need to match. If they don’t Ory Kratos rejects the request for the safety of your users.
Is there any reason, you don’t use the SDK to submit the request as well?
b
I am trying to build a universal form builder, thought that it was easier to call the api via fetch() to avoid calling the specific SDK method depending on which flow I want to submit.
I don't know if i got it right, the token in the form has to be the same as in the cookie? Couldn't the token in the cookie be used in another window?
b
Ah, I see. Yes, and that is the problem, an attacker could create a form on their page that submits the form on behalf of the user which would include the cookie and thus receive the response from Kratos. That’s why you need to send the token in the form submission as well. The CSRF token is regenerated whenever you initiate a flow and sometimes whenever you fetch a page. Read more about that here: https://stackoverflow.com/a/33829607
b
Thank you! I tried to use the SDK again, submitted a register flow and compared the request headers with the body. As well as with using fetch the tokens in the cookie and the form do not match.
Copy code
POST /self-service/registration?flow=57f6e2e1-b85f-465f-b6a1-ddae97b2a9a0 HTTP/1.1
Host: 127.0.0.1:4433
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 225
Origin: <http://127.0.0.1:3000>
DNT: 1
Connection: keep-alive
Referer: <http://127.0.0.1:3000/>
Cookie: csrf_token_806060ca5bf70dff3caa0e5c860002aade9d470a5a4dce73bcfa7ba10778f481=CnZCaDRekkEhK4PrZH/R0ashn3uhiiVbeNM2+U/UCdg=
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Copy code
{
  "csrf_token": "WH/Fwpe6kDys6HlKr+EhWE9Ziv6lKsvI+kFgRFkiJUhSCYeqo+QCfY3D+qHLnvCJ5HgVhQSg7pOCkla9FvYskA==",
  "method": "password",
  "password": "asdeqw121QA",
  "traits": {
    "email": "peter@pan.mail",
    "name": {
      "first": "ASDw",
      "last": "kjkji"
    }
  }
}
However the response is
status 200
.
p
Hi @bored-vegetable-68997 I believe Kratos sets two csrf tokens, one in the cookie and another in the form data. You need to include the csrf in both, meaning the form csrf will be a hidden input field and the csrf cookie should be included in the header.
You can check this login page in our nextjs example https://github.com/ory/kratos-selfservice-ui-react-nextjs/blob/master/pages/login.tsx and here we map all of the nodes (retrieve from kratos as json) to html https://github.com/ory/kratos-selfservice-ui-react-nextjs/blob/master/pkg/ui/Flow.tsx#L161-L193
the SDK or axios / fetch also needs to explicitly include cookies e.g.
Copy code
fetch(url, {
  credentials: 'include'
})
b
Thank you @proud-plumber-24205, that is what I was thinking, that there are two different tokens. And I just found the problem while checking the requests again. I was just calling the wrong endpoint.... should be e.g.
/self-service/registration
of course. And I was just calling
/
. 😄 Thank you all for your help!