[Kratos] CSRF token is missing or invalid when post data to registration endpoint

Hi, I’m implementing a self-service for Kratos but in Java, I can get data from kratos for rendering form. But when I post data to register new identity, I got a error below

{“error”:{“code”:400,“status”:“Bad Request”,“reason”:“CSRF token is missing or invalid.”,“message”:“The request was malformed or contained invalid parameters”}}

When I check the requests in browser, csrf token is similar, but in kratos logs, expected and actual token are different.

Below is registration data from kratos admin api

And request data when do a post to complete registration

Kratos log

oathkeeper_1 | [cors] 2020/03/23 16:11:37 Handler: Actual request
oathkeeper_1 | [cors] 2020/03/23 16:11:37 Actual response added headers: map[Access-Control-Allow-Credentials:[true] Access-Control-Allow-Origin:[*] Access-Control-Expose-Headers:[Content-Type] Vary:[Origin]]
oathkeeper_1 | {"level":"info","method":"GET","msg":"started handling request","remote":"172.23.0.1:56288","request":"/auth/registration","time":"2020-03-23T16:11:37Z"}
oathkeeper_1 | {"granted":true,"http_host":"127.0.0.1:4455","http_method":"GET","http_url":"[http://host.docker.internal:8088/auth/registration](https://slack-redir.net/link?url=http%3A%2F%2Fhost.docker.internal%3A8088%2Fauth%2Fregistration&v=3)","http_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0","level":"warning","msg":"Access request granted","subject":"guest","time":"2020-03-23T16:11:37Z"}
oathkeeper_1 | {"level":"info","measure#oathkeeper-proxy.latency":37000200,"method":"GET","msg":"completed handling request","remote":"172.23.0.1:56288","request":"/auth/registration","status":302,"text_status":"Found","time":"2020-03-23T16:11:37Z","took":37000200}
oathkeeper_1 | [cors] 2020/03/23 16:11:37 Handler: Actual request
oathkeeper_1 | [cors] 2020/03/23 16:11:37 Actual response added headers: map[Access-Control-Allow-Credentials:[true] Access-Control-Allow-Origin:[*] Access-Control-Expose-Headers:[Content-Type] Vary:[Origin]]
oathkeeper_1 | {"level":"info","method":"GET","msg":"started handling request","remote":"172.23.0.1:56288","request":"/.ory/kratos/public/self-service/browser/flows/registration","time":"2020-03-23T16:11:37Z"}
kratos_1 | time="2020-03-23T16:11:37Z" level=info msg="started handling request" method=GET name="public#[http://127.0.0.1:4455/.ory/kratos/public/](https://slack-redir.net/link?url=http%3A%2F%2F127.0.0.1%3A4455%2F.ory%2Fkratos%2Fpublic%2F&v=3)" remote="172.23.0.7:39566" request=/self-service/browser/flows/registration
kratos_1 | time="2020-03-23T16:11:37Z" level=info msg="completed handling request" method=GET name="public#[http://127.0.0.1:4455/.ory/kratos/public/](https://slack-redir.net/link?url=http%3A%2F%2F127.0.0.1%3A4455%2F.ory%2Fkratos%2Fpublic%2F&v=3)" remote="172.23.0.7:39566" request=/self-service/browser/flows/registration status=302 text_status=Found took=43.5578ms
oathkeeper_1 | {"granted":true,"http_host":"127.0.0.1:4455","http_method":"GET","http_url":"[http://kratos:4433/self-service/browser/flows/registration](https://slack-redir.net/link?url=http%3A%2F%2Fkratos%3A4433%2Fself-service%2Fbrowser%2Fflows%2Fregistration&v=3)","http_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0","level":"warning","msg":"Access request granted","subject":"","time":"2020-03-23T16:11:37Z"}
oathkeeper_1 | {"level":"info","measure#oathkeeper-proxy.latency":50985800,"method":"GET","msg":"completed handling request","remote":"172.23.0.1:56288","request":"/.ory/kratos/public/self-service/browser/flows/registration","status":302,"text_status":"Found","time":"2020-03-23T16:11:37Z","took":50985800}
oathkeeper_1 | [cors] 2020/03/23 16:11:37 Handler: Actual request
oathkeeper_1 | [cors] 2020/03/23 16:11:37 Actual response added headers: map[Access-Control-Allow-Credentials:[true] Access-Control-Allow-Origin:[*] Access-Control-Expose-Headers:[Content-Type] Vary:[Origin]]
oathkeeper_1 | {"level":"info","method":"GET","msg":"started handling request","remote":"172.23.0.1:56288","request":"/auth/registration?request=e136e899-7cbb-4068-8181-054005c2812f","time":"2020-03-23T16:11:37Z"}
kratos_1 | time="2020-03-23T16:11:37Z" level=info msg="started handling request" method=GET name="admin#[http://kratos:4434/](https://slack-redir.net/link?url=http%3A%2F%2Fkratos%3A4434%2F&v=3)" remote="172.23.0.1:58716" request="//self-service/browser/flows/requests/registration?request=e136e899-7cbb-4068-8181-054005c2812f"
kratos_1 | time="2020-03-23T16:11:37Z" level=info msg="completed handling request" method=GET name="admin#[http://kratos:4434/](https://slack-redir.net/link?url=http%3A%2F%2Fkratos%3A4434%2F&v=3)" remote="172.23.0.1:58716" request="//self-service/browser/flows/requests/registration?request=e136e899-7cbb-4068-8181-054005c2812f" status=301 text_status="Moved Permanently" took="700.9µs"
oathkeeper_1 | {"granted":true,"http_host":"127.0.0.1:4455","http_method":"GET","http_url":"[http://host.docker.internal:8088/auth/registration?request=e136e899-7cbb-4068-8181-054005c2812f](https://slack-redir.net/link?url=http%3A%2F%2Fhost.docker.internal%3A8088%2Fauth%2Fregistration%3Frequest%3De136e899-7cbb-4068-8181-054005c2812f&v=3)","http_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0","level":"warning","msg":"Access request granted","subject":"guest","time":"2020-03-23T16:11:37Z"}
oathkeeper_1 | {"level":"info","measure#oathkeeper-proxy.latency":87799200,"method":"GET","msg":"completed handling request","remote":"172.23.0.1:56288","request":"/auth/registration?request=e136e899-7cbb-4068-8181-054005c2812f","status":200,"text_status":"OK","time":"2020-03-23T16:11:37Z","took":87799200}
kratos_1 | time="2020-03-23T16:11:37Z" level=info msg="started handling request" method=GET name="admin#[http://kratos:4434/](https://slack-redir.net/link?url=http%3A%2F%2Fkratos%3A4434%2F&v=3)" remote="172.23.0.1:58716" request="/self-service/browser/flows/requests/registration?request=e136e899-7cbb-4068-8181-054005c2812f"
kratos_1 | time="2020-03-23T16:11:37Z" level=info msg="completed handling request" method=GET name="admin#[http://kratos:4434/](https://slack-redir.net/link?url=http%3A%2F%2Fkratos%3A4434%2F&v=3)" remote="172.23.0.1:58716" request="/self-service/browser/flows/requests/registration?request=e136e899-7cbb-4068-8181-054005c2812f" status=200 text_status=OK took=7.6258ms
oathkeeper_1 | {"level":"info","method":"POST","msg":"started handling request","remote":"172.23.0.1:56288","request":"/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=e136e899-7cbb-4068-8181-054005c2812f","time":"2020-03-23T16:12:36Z"}
oathkeeper_1 | {"granted":true,"http_host":"127.0.0.1:4455","http_method":"POST","http_url":"[http://kratos:4433/self-service/browser/flows/registration/strategies/password?request=e136e899-7cbb-4068-8181-054005c2812f](https://slack-redir.net/link?url=http%3A%2F%2Fkratos%3A4433%2Fself-service%2Fbrowser%2Fflows%2Fregistration%2Fstrategies%2Fpassword%3Frequest%3De136e899-7cbb-4068-8181-054005c2812f&v=3)","http_user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0","level":"warning","msg":"Access request granted","subject":"","time":"2020-03-23T16:12:36Z"}
oathkeeper_1 | {"level":"info","measure#oathkeeper-proxy.latency":1736900,"method":"POST","msg":"completed handling request","remote":"172.23.0.1:56288","request":"/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=e136e899-7cbb-4068-8181-054005c2812f","status":400,"text_status":"Bad Request","time":"2020-03-23T16:12:36Z","took":1736900}
oathkeeper_1 | [cors] 2020/03/23 16:12:36 Handler: Actual request
oathkeeper_1 | [cors] 2020/03/23 16:12:36 Actual response added headers: map[Access-Control-Allow-Credentials:[true] Access-Control-Allow-Origin:[*] Access-Control-Expose-Headers:[Content-Type] Vary:[Origin]]
kratos_1 | time="2020-03-23T16:12:36Z" level=info msg="started handling request" method=POST name="public#[http://127.0.0.1:4455/.ory/kratos/public/](https://slack-redir.net/link?url=http%3A%2F%2F127.0.0.1%3A4455%2F.ory%2Fkratos%2Fpublic%2F&v=3)" remote="172.23.0.7:39566" request="/self-service/browser/flows/registration/strategies/password?request=e136e899-7cbb-4068-8181-054005c2812f"
kratos_1 | time="2020-03-23T16:12:36Z" level=warning msg="A request failed due to a missing or invalid csrf_token value" expected_token="o6NXLbPk68qEfFEE5iCKD4+XANec5n7VZNlNzoF42p8+vpGbQDNpSiBskVQUj5SNqSImUR+zKvp4j+q4FkODWg==" received_token="AFtF6kkDwfeQpoSAi1D2XkAYgybKKgnVnCmpbQdO1SddUJIeFs5wLh1aRYYNiORsIzIob3awjpslBhDwCvLNUQ==" received_token_form="AFtF6kkDwfeQpoSAi1D2XkAYgybKKgnVnCmpbQdO1SddUJIeFs5wLh1aRYYNiORsIzIob3awjpslBhDwCvLNUQ=="
kratos_1 | time="2020-03-23T16:12:36Z" level=error msg="An error occurred while handling a request" code=400 debug= details="map[]" error="The request was malformed or contained invalid parameters" reason="CSRF token is missing or invalid." request-id= status=400 writer=JSON
kratos_1 | time="2020-03-23T16:12:36Z" level=info msg="completed handling request" method=POST name="public#[http://127.0.0.1:4455/.ory/kratos/public/](https://slack-redir.net/link?url=http%3A%2F%2F127.0.0.1%3A4455%2F.ory%2Fkratos%2Fpublic%2F&v=3)" remote="172.23.0.7:39566" request="/self-service/browser/flows/registration/strategies/password?request=e136e899-7cbb-4068-8181-054005c2812f" status=400 text_status="Bad Request" took="449.9µs"

I don’t use server-rendering, so I create an api in my self-service to return json from kratos, and then my web frontend (react) use this json to populates data.

Here is my java self-service code

import java.net.URI;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import sh.ory.kratos.ApiClient;
import sh.ory.kratos.ApiException;
import sh.ory.kratos.ApiResponse;
import sh.ory.kratos.api.AdminApi;
import sh.ory.kratos.model.RegistrationRequest;

@RestController
@Slf4j
public class RegistrationController {

  private final IdpProperties idpProperties;

  @Autowired
  public RegistrationController(IdpProperties idpProperties) {
    this.idpProperties = idpProperties;
  }

  @GetMapping("auth/registration")
  public ResponseEntity<RegistrationRequest> register(
      @RequestParam(required = false) String request) {
    if (request == null) {
      log.info("Parameter 'request' not available");
      // redirect
      return redirect();
    }
    log.info(request);
    ApiClient client = new ApiClient();
    client.setBasePath(idpProperties.getUrl().getKratosAdminApi());
    AdminApi adminEndpoint = new AdminApi(client);
    try {
      ApiResponse<RegistrationRequest> response =
          adminEndpoint.getSelfServiceBrowserRegistrationRequestWithHttpInfo(request);
      if (response.getStatusCode() == 404
          || response.getStatusCode() == 403
          || response.getStatusCode() == 410) {
        // redirect
        return redirect();
      }
      if (response.getStatusCode() == 200) {
        RegistrationRequest data = response.getData();
        return new ResponseEntity<>(data, HttpStatus.OK);
      }
    } catch (ApiException e) {
      log.error("Kratos connection error", e);
    }
    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
  }

  private ResponseEntity redirect() {
    HttpHeaders headers = new HttpHeaders();
    headers.setLocation(
        URI.create(
            idpProperties.getUrl().getKratosBrowser()
                + idpProperties.getUrl().getRegistrationRequest()));
    return new ResponseEntity<>(headers, HttpStatus.FOUND);
  }
}

Configuration:

idp:
  url:
    kratos-admin-api: http://127.0.0.1:4434/
    kratos-public-api: http://127.0.0.1:4433/
    kratos-browser: http://127.0.0.1:4455/.ory/kratos/public
    registration-request: /self-service/browser/flows/registration
    login-request: /self-service/browser/flows/login

My kratos and oathkeeper config is similar to config files in kratos repository
Any ideas? Thank you!

which versions are you using?

I use a docker image build from master branch (newer than 0.1.1.alpha.1) because I need to run with postgresql

This is most likely a problem with your cookies. In your chrome DevTools, check out the “cookie” payloads and make sure that they are set for all the requests.

As you can see in your logs, the CSRF tokens do not match:

kratos_1 | time=“2020-03-23T16:12:36Z” level=warning msg=“A request failed due to a missing or invalid csrf_token value” expected_token=“o6NXLbPk68qEfFEE5iCKD4+XANec5n7VZNlNzoF42p8+vpGbQDNpSiBskVQUj5SNqSImUR+zKvp4j+q4FkODWg==” received_token=“AFtF6kkDwfeQpoSAi1D2XkAYgybKKgnVnCmpbQdO1SddUJIeFs5wLh1aRYYNiORsIzIob3awjpslBhDwCvLNUQ==” received_token_form=“AFtF6kkDwfeQpoSAi1D2XkAYgybKKgnVnCmpbQdO1SddUJIeFs5wLh1aRYYNiORsIzIob3awjpslBhDwCvLNUQ==”

This means that the CSRF cookie from the initial GET request does not match the one from the final POST request. This can be because you’re using different hosts (e.g. localhost / 127.0.0.1), for example.

I suggest you check out the cookies in the DevTools and make sure that hosts etc are properly set.

Also, as pointed out in another thread, make sure to use --dev flag when running ORY Kratos because it will make use of fortified cookeis otherwise which do not work on HTTP.

Thanks for your reply! I will check these cookies is available or not, i access to my web using hostname 127.0.0.1

Thank you!, I resolved this issue by allow cors request using credentials such as cookies in my javascript http client

1 Like

I have written docs for this topic, hope it helps!

Cool! it’s very helpful

Hello everyone. I’m new with ORY Kratos. I’ve the same issue even the csrf_token that generated from /self-service/browser/flows/registration is included with POST request after form like username and email is filled and sent to /self-service/browser/flows/registration/strategies/password. Can you help me?

Not without technical details, please also check our docs regarding this: https://www.ory.sh/kratos/docs/debug/csrf

1 Like

I’m sorry for not telling the technical details. I’ve read the documentation about CSRF from your link. The documentation said that Accessing ORY’s APIs from server side application is better to use the Admin API port (I use default port, 4434) because Public Port requires csrf token, and Admin doesn’t. However, I tried to send request to Admin port for completing password strategy and the result is not found.

For example, I send the post request to Admin port:
POST http://127.0.0.1:4434/self-service/browser/flows/login/strategies/password?request=1b7112e9-78be-4f21-8f01-d78453ed0fc3

Response:
404 page not found

image

time="2020-06-21T04:01:54Z" level=info msg="completed handling request" method=POST name="admin#http://kratos:4434/" remote="172.18.0.1:58786" request="/self-service/browser/flows/login/strategies/password?request=1b7112e9-78be-4f21-8f01-d78453ed0fc3" status=404 text_status="Not Found" took="63.092µs"

I’ve tried to send request to Public port, but error CSRF token is missing or invalid is appeared.

image

level=warning msg="A request failed due to a missing or invalid csrf_token value" expected_token="lFl+ErNdDL+2IRVpt1OvZyE2D9kCqyMEoG3qn+khlqpG4xZ1F5374E+hlQ659jayCG1Cswefgb0eSbRufCxpRQ==" received_token="wmJblK1gToY4l3/WQFRBzUkJsaMcTR9ELlBFBeu+jUAgQ1sm1epycrMqMpOrmL0FnDQcC7NBe0QJHVnHz/30gA==" received_token_form="wmJblK1gToY4l3/WQFRBzUkJsaMcTR9ELlBFBeu+jUAgQ1sm1epycrMqMpOrmL0FnDQcC7NBe0QJHVnHz/30gA==" app-kratos | time="2020-06-21T04:07:39Z" level=error msg="An error occurred while handling a request" code=400 debug= details="map[]" error="The request was malformed or contained invalid parameters" reason="CSRF token is missing or invalid." request-id= status=400 writer=JSON app-kratos | time="2020-06-21T04:07:39Z" level=info msg="completed handling request" method=POST name="public#http://127.0.0.1:9080/.ory/kratos/public/" remote="172.18.0.1:48740" request="/self-service/browser/flows/login/strategies/password?request=a47f4bb2-ff1d-4f2d-a573-d1cc161358e3" status=400 text_status="Bad Request" took="190.86µs

Here’s my ORY Kratos configuration:
image

Do not send the POST request to the admin port, that will not work. The admin port is not to exposed to the internet, it does not make sense to try to access it from the browser.

Please provide the full config including set up and everything. Or just use our quickstart guide from the docs to get started. No help can be provided otherwise.

I have the same problem.

kratos_1 | time=2020-08-21T12:34:37Z level=info msg=started handling request method=POST name=public#http://127.0.0.1:8089/.ory/kratos/public/ remote=172.27.0.1:49434 request=/self-service/browser/flows/login/strategies/password?request=f17d7cdc-dca3-4a0e-b7d7-c9e0c244f1af
kratos_1 | time=2020-08-21T12:34:37Z level=warning msg=A request failed due to a missing or invalid csrf_token value audience=application expected_token=/fqHecL1chI+dTL+XPtuCDEqsAvklk/1FtBOT3LCY+VRbWMfmWnKxpqur0Vd0PvOnC06CqrD3UokGXT5AXunFA== received_token=O0W8DrG7CxTrsRzWbOhI9vj+SHNj7KgOBwys0OdroK071NKquqyPo5R40ueKwFqYNwXjZ8/nOxeybjxq38k4Ow== received_token_form=O0W8DrG7CxTrsRzWbOhI9vj+SHNj7KgOBwys0OdroK071NKquqyPo5R40ueKwFqYNwXjZ8/nOxeybjxq38k4Ow== service_name=kratos service_version=
kratos_1 | time=2020-08-21T12:34:37Z level=error msg=An error occurred while handling a request audience=application error=map[debug: message:The request was malformed or contained invalid parameters reason:CSRF token is missing or invalid. status:Bad Request status_code:400] http_request=map[headers:map[accept-encoding:gzip user-agent:Go-http-client/1.1] host:127.0.0.1:4433 method:POST path:/self-service/browser/flows/login/strategies/password query:Value is sensitive and has been redacted. To see the value set config key “log.leak_sensitive_values = true” or environment variable “LOG_LEAK_SENSITIVE_VALUES=true”. remote:172.27.0.1:49434 scheme:http] http_response=map[status_code:400] service_name=kratos service_version=
kratos_1 | time=2020-08-21T12:34:37Z level=info msg=completed handling request method=POST name=public#http://127.0.0.1:8089/.ory/kratos/public/ remote=172.27.0.1:49434 request=/self-service/browser/flows/login/strategies/password?request=f17d7cdc-dca3-4a0e-b7d7-c9e0c244f1af status=400 text_status=Bad Request took=306.8µs

My kratos.yml config

#version: v0.4.6-alpha.1

dsn: memory

serve:
public:
base_url: http://127.0.0.1:8089/.ory/kratos/public/
admin:
base_url: http://kratos:4434/

selfservice:
default_browser_return_url: http://127.0.0.1:8089/
whitelisted_return_urls:
- http://127.0.0.1:8089

strategies:
password:
enabled: true

flows:
error:
ui_url: http://127.0.0.1:8089/error

settings:
  ui_url: http://127.0.0.1:4455/settings
  privileged_session_max_age: 15m

recovery:
  enabled: true
  ui_url: http://127.0.0.1:4455/recovery

verification:
  enabled: true
  ui_url: http://127.0.0.1:4455/verify
  after:
    default_browser_return_url: http://127.0.0.1:4455/

logout:
  after:
    default_browser_return_url: http://127.0.0.1:4455/auth/login

login:
  ui_url: http://127.0.0.1:8089
  request_lifespan: 10m

registration:
  request_lifespan: 10m
  ui_url: http://127.0.0.1:4455/auth/registration
  after:
    password:
      hooks:
        -
          hook: session

log:
level: debug
format: text

secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE

hashers:
argon2:
parallelism: 1
memory: 131072
iterations: 2
salt_length: 16
key_length: 16

identity:
default_schema_url: file:///etc/config/kratos/identity.traits.schema.json

courier:
smtp:
connection_uri: smtps://test:test@mailslurper:1025/?skip_ssl_verify=true

PS: I have read https://www.ory.sh/kratos/docs/debug/csrf/