[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