Hello, In order to fully understand Kratos i've tr...
# talk-kratos
g
Hello, In order to fully understand Kratos i've tried to implement a registration handler with Go. I'm using a self hosted Kratos instance on Docker on the same machine that my code is running with the default password identity. I followed the User Registration flow (https://www.ory.sh/docs/kratos/self-service/flows/user-registration). With the following code :
Copy code
package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type PasswordRequest struct {
	CsrfToken  string `json:"csrf_token"`
	Identifier string `json:"traits.email"`
	Password   string `json:"password"`
	Method     string `json:"method"`
}

func getActionUrl(jsonResponse []byte) (string, error) {
	var jsonResponseData map[string]interface{}
	err := json.Unmarshal(jsonResponse, &jsonResponseData)
	if err != nil {
		return "", err
	}
	// UI is the JSON field that ORY send us to build the register form
	uiJsonField := jsonResponseData["ui"].(map[string]interface{})

	// Clause Guard that verify if the ui is not null
	if uiJsonField == nil {
		return "", errors.New("The JSON field 'ui' doesn't exists")
	}

	actionUrl := uiJsonField["action"]

	if actionUrl == nil {
		return "", errors.New("The json field 'ui/action' doesn't exists")
	}

	return actionUrl.(string), nil
}

func getCrsfToken(jsonResponse []byte) (string, error) {
	var jsonResponseData map[string]interface{}
	err := json.Unmarshal(jsonResponse, &jsonResponseData)
	if err != nil {
		return "", err
	}

	// UI is the JSON field that ORY send us to build the register form
	uiJsonField := jsonResponseData["ui"].(map[string]interface{})

	// Clause Guard that verify if the ui is not null
	if uiJsonField == nil {
		return "", errors.New("The JSON field 'ui' doesn't exists")
	}

	// In UI we have a 'nodes' that give us a list of <input> to build our form
	inputListJsonField := uiJsonField["nodes"].([]interface{})
	if inputListJsonField == nil {
		return "", errors.New("The JSON field 'ui/nodes' doesn't exists")
	}

	// Attributes give us the attributes for the input. The value returned is the CRSF token
	crsf_token := inputListJsonField[0].(map[string]interface{})["attributes"].(map[string]interface{})["value"]
	if crsf_token == nil {
		return "", errors.New("The JSON field 'ui/nodes/attributes/value' doesn't exists")
	}

	return crsf_token.(string), nil

}

func main() {
	// HTTP GET Request to initialize the flow
	req, err := http.NewRequest("GET", "<http://localhost:4433/self-service/registration/browser>", nil)
	if err != nil {
		panic(err)
	}

	// Set headers
	req.Header.Set("Accept", "application/json")

	// Execute the request
	client := &http.Client{}
	resp, err := <http://client.Do|client.Do>(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

    // From the GET Request we can make the POST request to the actionUrl

	// Read the response body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	crsf_token, err := getCrsfToken(body)

	if err != nil {
		fmt.Println("Error in retrieving crsf_token : ", err)
	}

	actionUrl, err := getActionUrl(body)

	passwordRequest := PasswordRequest{
		CsrfToken:  crsf_token,
		Identifier: "<mailto:test1312@gmail.com|test1312@gmail.com>",
		Password:   "RandomPassword1234!",
		Method:     "password",
	}

	passwordRequestStr, _ := json.Marshal(passwordRequest)

	postReq, err := http.NewRequest("POST", actionUrl, bytes.NewBuffer(passwordRequestStr))

	postReq.Header.Set("Content-Type", "application/json")
    
    // We set the cookies and headers for CSRF
	postReq.Header.Set("X-CSRF-Token", crsf_token)
	cookie := &http.Cookie{
		Name:  "csrf_token",
		Value: crsf_token,
	}
	postReq.AddCookie(cookie)

	postClient := &http.Client{}
	postResp, err := <http://postClient.Do|postClient.Do>(postReq)

	postBody, err := ioutil.ReadAll(postResp.Body)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(postBody))

}
But i keep getting a CRSF Token error
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"
  }
}
Fixed. I just needed to include the cookies that the first GET response sended me. For future readers who encounter a similar issue here's my full code :
Copy code
package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
)

type PasswordRequest struct {
	CsrfToken  string `json:"csrf_token"`
	Identifier string `json:"traits.email"`
	Password   string `json:"password"`
	Method     string `json:"method"`
}

func getActionUrl(jsonResponse []byte) (string, error) {
	var jsonResponseData map[string]interface{}
	err := json.Unmarshal(jsonResponse, &jsonResponseData)
	if err != nil {
		return "", err
	}
	// UI is the JSON field that ORY send us to build the register form
	uiJsonField := jsonResponseData["ui"].(map[string]interface{})

	// Clause Guard that verify if the ui is not null
	if uiJsonField == nil {
		return "", errors.New("The JSON field 'ui' doesn't exists")
	}

	actionUrl := uiJsonField["action"]

	if actionUrl == nil {
		return "", errors.New("The json field 'ui/action' doesn't exists")
	}

	return actionUrl.(string), nil
}

func getCrsfToken(jsonResponse []byte) (string, error) {
	var jsonResponseData map[string]interface{}
	err := json.Unmarshal(jsonResponse, &jsonResponseData)
	if err != nil {
		return "", err
	}

	// UI is the JSON field that ORY send us to build the register form
	uiJsonField := jsonResponseData["ui"].(map[string]interface{})

	// Clause Guard that verify if the ui is not null
	if uiJsonField == nil {
		return "", errors.New("The JSON field 'ui' doesn't exists")
	}

	// In UI we have a 'nodes' that give us a list of <input> to build our form
	inputListJsonField := uiJsonField["nodes"].([]interface{})
	if inputListJsonField == nil {
		return "", errors.New("The JSON field 'ui/nodes' doesn't exists")
	}

	// Attributes give us the attributes for the input. The value returned is the CRSF token
	crsf_token := inputListJsonField[0].(map[string]interface{})["attributes"].(map[string]interface{})["value"]
	if crsf_token == nil {
		return "", errors.New("The JSON field 'ui/nodes/attributes/value' doesn't exists")
	}

	return crsf_token.(string), nil

}

func main() {
	// Create a new HTTP request
	req, err := http.NewRequest("GET", "<http://localhost:4433/self-service/registration/browser>", nil)
	if err != nil {
		panic(err)
	}

	// Set headers
	req.Header.Set("Accept", "application/json")

	// Execute the request
	client := &http.Client{}
	resp, err := <http://client.Do|client.Do>(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	get_cookies := resp.Cookies()

	// Read the response body
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}

	crsf_token, err := getCrsfToken(body)

	if err != nil {
		fmt.Println("Error in retrieving crsf_token : ", err)
	}

	actionUrl, err := getActionUrl(body)

	passwordRequest := PasswordRequest{
		CsrfToken:  crsf_token,
		Identifier: "<mailto:test1312@gmail.com|test1312@gmail.com>",
		Password:   "RandomPassword1234!",
		Method:     "password",
	}

	passwordRequestStr, _ := json.Marshal(passwordRequest)

	postReq, err := http.NewRequest("POST", actionUrl, bytes.NewBuffer(passwordRequestStr))

	postReq.Header.Set("Content-Type", "application/json")
	postReq.Header.Set("X-CSRF-Token", crsf_token)
	cookie := &http.Cookie{
		Name:  "csrf_token",
		Value: crsf_token,
	}
	postReq.AddCookie(cookie)
	for _, cookie := range get_cookies {
		postReq.AddCookie(cookie)
	}

	postClient := &http.Client{}
	postResp, err := <http://postClient.Do|postClient.Do>(postReq)

	postBody, err := ioutil.ReadAll(postResp.Body)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(postBody))

}
s
@green-cartoon-16392 can you please write a test suite for the http function? getActionURL_test is missing