diff --git a/TODO.md b/TODO.md
index 6682a5d02257a2180ba5e041f64078a3cb64e071..3718a82bff18daa8e5fadda8d88256760a3f8ae2 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,3 +1,5 @@
 [ ] Remove references to localhost and ports
 [ ] Tests
 [ ] Remove useless fmt.Print
+[ ] Obviously : TODOs
+[ ] Css and JS messages
diff --git a/go.mod b/go.mod
index d328a29c44f8c5f72c363ccbec2893f9658bc42f..54cd2c80578487c7415619e436955a51384f4cb0 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,9 @@ go 1.16
 
 require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/mattn/go-sqlite3 v1.14.7 // indirect
 	github.com/oschwald/maxminddb-golang v1.8.0
-	golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect
+	golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71 // indirect
+	gorm.io/driver/sqlite v1.1.4
+	gorm.io/gorm v1.21.10
 )
diff --git a/go.sum b/go.sum
index e838a1efcfda08a7654d910be07358d99517578c..2272e66e9ccfd97595a3634ae7f80c638caca4a5 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,14 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
+github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
+github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
+github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
 github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -9,8 +17,13 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8=
-golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71 h1:X/2sJAybVknnUnV7AD2HdT6rm2p5BP6eH2j+igduWgk=
+golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
+gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
+gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gorm.io/gorm v1.21.10 h1:kBGiBsaqOQ+8f6S2U6mvGFz6aWWyCeIiuaFcaBozp4M=
+gorm.io/gorm v1.21.10/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
diff --git a/internal/mandate/mandate.go b/internal/mandate/mandate.go
new file mode 100644
index 0000000000000000000000000000000000000000..3a93b30b01c3b5fb24b1833b37946c3ed5de779b
--- /dev/null
+++ b/internal/mandate/mandate.go
@@ -0,0 +1,38 @@
+package mandate
+
+import (
+	"gorm.io/driver/sqlite"
+	"gorm.io/gorm"
+)
+
+type Mandate struct {
+	gorm.Model
+	Sirent      string
+	MandateeSub string
+	MandaterSub string
+	// TODO : Expiration date
+}
+
+var db *gorm.DB
+
+func init() {
+	var err error
+	db, err = gorm.Open(sqlite.Open("glcpro.db"), &gorm.Config{})
+	if err != nil {
+		panic("failed to connect database")
+	}
+	// Migrate the schema
+	db.AutoMigrate(&Mandate{})
+}
+
+func Exists(sirent string, mandateeSub string) bool {
+	var mandate Mandate
+	if err := db.Where("sirent = ? AND mandatee_sub = ?", sirent, mandateeSub).First(&mandate).Error; err != nil {
+		return false
+	}
+	return true
+}
+
+func Create(sirent string, mandateeSub string, mandaterSub string) {
+	db.Create(&Mandate{Sirent: sirent, MandateeSub: mandateeSub, MandaterSub: mandaterSub})
+}
diff --git a/internal/mandate/mandate_test.go b/internal/mandate/mandate_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e5c10153f23e5d5fab531cedd74f3cfcb0d96870
--- /dev/null
+++ b/internal/mandate/mandate_test.go
@@ -0,0 +1,30 @@
+package mandate
+
+import (
+	"testing"
+)
+
+func TestExists(t *testing.T) {
+	Create("001", "101", "201")
+	type args struct {
+		sirent      string
+		mandateeSub string
+	}
+	tests := []struct {
+		name string
+		args args
+		want bool
+	}{
+		{"exists", args{sirent: "001", mandateeSub: "101"}, true},
+		{"bad_mandatee", args{sirent: "001", mandateeSub: "102"}, false},
+		{"bad_sirent", args{sirent: "002", mandateeSub: "101"}, false},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+
+			if got := Exists(tt.args.sirent, tt.args.mandateeSub); got != tt.want {
+				t.Errorf("Exists() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/mailtemplate.html b/mailtemplate.html
index 35abb6a4e1300c8ec77a1e31cca5036f0c8305ca..abf7d3b8c7388e605b5e134fe6be7727b950d39e 100644
--- a/mailtemplate.html
+++ b/mailtemplate.html
@@ -7,8 +7,12 @@
       l'entreprise ou de l'établissement portant le numéro SIREN/SIRET
       {{.Sirent}}.
     </p>
-    <p>Pour valider cette demande, veuillez cliquer sur ce lien :</p>
-    <a href="localhost:8080/api/matcher/validate&code={{.Token}}">Valider</a>
+    <p>
+      Pour valider cette demande, veuillez cliquer sur ce lien :
+      <a href="http://localhost:8080/api/matcher/validate?code={{.Token}}"
+        >Valider.</a
+      >
+    </p>
     <p>
       Pour refuser cette demande, il suffit de ne rien faire et de supprimer ce
       mail.
diff --git a/pkg/franceconnect/franceconnect.go b/pkg/franceconnect/franceconnect.go
new file mode 100644
index 0000000000000000000000000000000000000000..bd07b877baafacb349d6f464d8dbbeba8e7acdff
--- /dev/null
+++ b/pkg/franceconnect/franceconnect.go
@@ -0,0 +1,91 @@
+package franceconnect
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+type Identity struct {
+	GivenName         string `json:"given_name"`
+	FamilyName        string `json:"family_name"`
+	Birthdate         string `json:"birthdate"`
+	Gender            string `json:"gender"`
+	Birthplace        string `json:"birthplace"`
+	Birthcountry      string `json:"birthcountry"`
+	PreferredUsername string `json:"preferred_username"`
+	Sub               string `json:"sub"`
+}
+
+func RedirectToFranceConnect(w http.ResponseWriter, r *http.Request) {
+	// TODO : France Connect parameters as env variables
+	fcAuth := "https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize"
+
+	fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e"
+	params := url.Values{}
+	// TODO : State and nonce
+	params.Add("state", "A_RANDOM_STATE")
+	params.Add("nonce", "A_RANDOM_NONCE")
+	params.Add("scope", "openid identite_pivot")
+	// TODO : Redirect from request
+	params.Add("redirect_uri", "http://localhost:8080"+"/callback")
+	params.Add("client_id", fcClientID)
+	params.Add("response_type", "code")
+	http.Redirect(w, r, fcAuth+"?"+params.Encode(), http.StatusFound)
+}
+
+func HandleCallBack(w http.ResponseWriter, r *http.Request) (Identity, error) {
+	// TODO : fcToken and client secret from env
+	fcToken := "https://fcp.integ01.dev-franceconnect.fr/api/v1/token"
+	fcUserInfo := "https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo?schema=openid"
+	fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e"
+	fcClientSecret := "2791a731e6a59f56b6b4dd0d08c9b1f593b5f3658b9fd731cb24248e2669af4b"
+	// TODO : Check the state
+	fcCode := r.URL.Query().Get("code")
+	data := url.Values{}
+	data.Set("grant_type", "authorization_code")
+	data.Set("redirect_uri", "http://localhost:8080/callback")
+	data.Set("client_id", fcClientID)
+	data.Set("client_secret", fcClientSecret)
+	data.Set("code", fcCode)
+
+	client := &http.Client{}
+	req, err := http.NewRequest(http.MethodPost, fcToken, strings.NewReader(data.Encode()))
+	if err != nil {
+		return Identity{}, fmt.Errorf("could not perform the request to France Connect token url")
+	}
+	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+	req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
+
+	resp, err := client.Do(req)
+	if err != nil {
+		return Identity{}, fmt.Errorf("could not perform the request to France Connect token url")
+	}
+	// Get the access_token information
+	rBody, _ := ioutil.ReadAll(resp.Body)
+	fmt.Printf("rBody: %v\n", string(rBody))
+	accessToken := regexp.MustCompile(`.*"access_token":"([^"]*)`).FindStringSubmatch(string(rBody))[1]
+
+	// Call the user info endpoint
+	req, err = http.NewRequest(http.MethodGet, fcUserInfo, nil)
+	if err != nil {
+		return Identity{}, fmt.Errorf("could not perform the request to France Connect userinfo url")
+	}
+	req.Header.Add("Authorization", "Bearer "+accessToken)
+	resp, err = client.Do(req)
+	if err != nil {
+		return Identity{}, fmt.Errorf("could not perform the request to France Connect userinfo url")
+	}
+	rBodyFC, _ := ioutil.ReadAll(resp.Body)
+	i := Identity{}
+	err = json.Unmarshal(rBodyFC, &i)
+	if err != nil {
+		return Identity{}, fmt.Errorf("unmarshall the France Connect user info")
+	}
+	return i, nil
+}
diff --git a/pkg/matcher/mailtemplate.html b/pkg/matcher/mailtemplate.html
index 35abb6a4e1300c8ec77a1e31cca5036f0c8305ca..abf7d3b8c7388e605b5e134fe6be7727b950d39e 100644
--- a/pkg/matcher/mailtemplate.html
+++ b/pkg/matcher/mailtemplate.html
@@ -7,8 +7,12 @@
       l'entreprise ou de l'établissement portant le numéro SIREN/SIRET
       {{.Sirent}}.
     </p>
-    <p>Pour valider cette demande, veuillez cliquer sur ce lien :</p>
-    <a href="localhost:8080/api/matcher/validate&code={{.Token}}">Valider</a>
+    <p>
+      Pour valider cette demande, veuillez cliquer sur ce lien :
+      <a href="http://localhost:8080/api/matcher/validate?code={{.Token}}"
+        >Valider.</a
+      >
+    </p>
     <p>
       Pour refuser cette demande, il suffit de ne rien faire et de supprimer ce
       mail.
diff --git a/pkg/matcher/matcher.go b/pkg/matcher/matcher.go
index 657e7674364aec02395b8b130ba9744740ce860d..5c07c24383dc9ac2e690733d97da9fec7ddade7e 100644
--- a/pkg/matcher/matcher.go
+++ b/pkg/matcher/matcher.go
@@ -10,7 +10,10 @@ import (
 	"regexp"
 	"time"
 
+	"forge.grandlyon.com/npernoud/glcpro/internal/mandate"
+	"forge.grandlyon.com/npernoud/glcpro/pkg/apientreprise"
 	"forge.grandlyon.com/npernoud/glcpro/pkg/common"
+	"forge.grandlyon.com/npernoud/glcpro/pkg/franceconnect"
 	"forge.grandlyon.com/npernoud/glcpro/pkg/oidcserver"
 	"forge.grandlyon.com/npernoud/glcpro/pkg/tokens"
 )
@@ -68,16 +71,61 @@ func CreateMatcherServer() *http.ServeMux {
 	mux.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) {
 
 		// Unpack the token to work out who the mandate is for
+		md := MandateDemand{}
+		tokens.ExtractAndValidateToken(r, "", &md, false)
 
-		// Perform an France Connect authentication
+		// Store the demand in a cookie
+		tokens.CreateCookie(md, "localhost", "MandateDemand", 60*time.Second, w)
 
-		// Check that the France Connect user can create a mandate : he is the CEO of the company or has a transitive mandate
+		// Set a cookie to redirect to the needed callback route from the front end generic callback
+		cookie := http.Cookie{Name: "callbackRoute", Domain: "localhost", Path: "/", Value: "/api/matcher/callback", MaxAge: 60, Secure: false, HttpOnly: false, SameSite: http.SameSiteLaxMode}
+		http.SetCookie(w, &cookie)
+
+		// Perform an France Connect authentication ... and wait for the callback
+		franceconnect.RedirectToFranceConnect(w, r)
+
+	})
+
+	mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
+		// Get the mandater identity from
+		i, err := franceconnect.HandleCallBack(w, r)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		// Get the redirect url from the cookie
+		md := MandateDemand{}
+		_, err = tokens.ExtractAndValidateToken(r, "MandateDemand", &md, false)
+		if err != nil {
+			http.Error(w, "could not get the mandate demand data from cookie", http.StatusInternalServerError)
+			return
+		}
 
-		// Create the mandate
+		// Check that the France Connect user can create a mandate : he is the CEO of the company or has a transitive mandate
+		// Get the data from the API Entreprise
+		e, _, err := apientreprise.GetData(md.Sirent)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
 
-		// TODO : Inform the original asket that is demand has been validated/rejected
+		// TODO : Iterate on all MandatairesSociaux and clean the big if, refactor with oidcserver
+		if len(e.MandatairesSociaux) < 1 {
+			http.Error(w, "there is no Mandataires Sociaux for this SIREN/T", http.StatusOK)
+			return
+		}
+		if e.MandatairesSociaux[0].Nom == i.FamilyName || e.MandatairesSociaux[0].Prenom == i.GivenName || e.MandatairesSociaux[0].DateNaissance == i.Birthdate {
+			// Create the mandate
+			mandate.Create(md.Sirent, md.AskerSub, i.Sub)
+			w.Write([]byte("the mandate was successfully created "))
+			return
+		}
+		w.Write([]byte("the mandate could not be created, you do not seem to be the company CEO"))
+		return
 
+		// TODO : Inform the original asker that is demand has been validated/rejected
 	})
+
 	return mux
 }
 
diff --git a/pkg/oidcserver/oidcserver.go b/pkg/oidcserver/oidcserver.go
index 9a07bc5da3649ad8bd4438e69149d413e3bf707a..28ae9df1669ba0e39b1d76cfc0c4bb75f0781ddf 100644
--- a/pkg/oidcserver/oidcserver.go
+++ b/pkg/oidcserver/oidcserver.go
@@ -3,17 +3,15 @@ package oidcserver
 import (
 	"encoding/base64"
 	"encoding/json"
-	"fmt"
-	"io/ioutil"
 	"net/http"
 	"net/url"
-	"regexp"
-	"strconv"
 	"strings"
 	"time"
 
+	"forge.grandlyon.com/npernoud/glcpro/internal/mandate"
 	"forge.grandlyon.com/npernoud/glcpro/pkg/apientreprise"
 	"forge.grandlyon.com/npernoud/glcpro/pkg/common"
+	"forge.grandlyon.com/npernoud/glcpro/pkg/franceconnect"
 	"forge.grandlyon.com/npernoud/glcpro/pkg/tokens"
 )
 
@@ -21,25 +19,14 @@ type ClientRequestData struct {
 	RedirectURI string
 	State       string
 	Sirent      string
-	Id          FCIdentity
+	Id          franceconnect.Identity
 }
 
 type GLCId struct {
-	Id FCIdentity               `json:"id_token"`
+	Id franceconnect.Identity   `json:"id_token"`
 	E  apientreprise.Entreprise `json:"entreprise"`
 }
 
-type FCIdentity struct {
-	GivenName         string `json:"given_name"`
-	FamilyName        string `json:"family_name"`
-	Birthdate         string `json:"birthdate"`
-	Gender            string `json:"gender"`
-	Birthplace        string `json:"birthplace"`
-	Birthcountry      string `json:"birthcountry"`
-	PreferredUsername string `json:"preferred_username"`
-	Sub               string `json:"sub"`
-}
-
 // CreateOIDCServer creates a Open ID Connect Server as proxy to France Connect
 func CreateOIDCServer() *http.ServeMux {
 	rd := ClientRequestData{}
@@ -79,80 +66,28 @@ func CreateOIDCServer() *http.ServeMux {
 		rd.Sirent = query.Get("sirent")
 		tokens.CreateCookie(rd, "localhost", "ClientRequestData", 60*time.Second, w)
 
+		// Set a cookie to redirect to the needed callback route from the front end generic callback
+		cookie := http.Cookie{Name: "callbackRoute", Domain: "localhost", Path: "/", Value: "/api/oidc/callback", MaxAge: 60, Secure: false, HttpOnly: false, SameSite: http.SameSiteLaxMode}
+		http.SetCookie(w, &cookie)
+
 		// Redirect to France Connect with the callback as parameter
-		// TODO : France Connect parameters as env variables
-		fcAuth := "https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize"
-
-		fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e"
-		params := url.Values{}
-		// TODO : State and nonce
-		params.Add("state", "A_RANDOM_STATE")
-		params.Add("nonce", "A_RANDOM_NONCE")
-		params.Add("scope", "openid identite_pivot")
-		// TODO : Redirect from request
-		params.Add("redirect_uri", "http://localhost:8080"+"/callback")
-		params.Add("client_id", fcClientID)
-		params.Add("response_type", "code")
-		http.Redirect(w, r, fcAuth+"?"+params.Encode(), http.StatusFound)
+		franceconnect.RedirectToFranceConnect(w, r)
 
 		// And that's all the reste will be handed by France Connect callback : get the France Connect identity, do the API Entreprise call, get the original parameters from the client and redirect to the original client
 	})
 
 	// Handles the France Connect Callback, get the user info from the token, and end the original response
 	mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
-		// TODO : fcToken and client secret from env
-		fcToken := "https://fcp.integ01.dev-franceconnect.fr/api/v1/token"
-		fcUserInfo := "https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo?schema=openid"
-		fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e"
-		fcClientSecret := "2791a731e6a59f56b6b4dd0d08c9b1f593b5f3658b9fd731cb24248e2669af4b"
-		// TODO : Check the state
-		fcCode := r.URL.Query().Get("code")
-		data := url.Values{}
-		data.Set("grant_type", "authorization_code")
-		data.Set("redirect_uri", "http://localhost:8080/callback")
-		data.Set("client_id", fcClientID)
-		data.Set("client_secret", fcClientSecret)
-		data.Set("code", fcCode)
-
-		client := &http.Client{}
-		req, err := http.NewRequest(http.MethodPost, fcToken, strings.NewReader(data.Encode()))
-		if err != nil {
-			http.Error(w, "could not perform the request to France Connect token url", http.StatusInternalServerError)
-			return
-		}
-		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
-		req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
 
-		resp, err := client.Do(req)
+		i, err := franceconnect.HandleCallBack(w, r)
 		if err != nil {
-			http.Error(w, "could not perform the request to France Connect token url", http.StatusInternalServerError)
-			return
-		}
-		// Get the access_token information
-		rBody, _ := ioutil.ReadAll(resp.Body)
-		fmt.Printf("rBody: %v\n", string(rBody))
-		accessToken := regexp.MustCompile(`.*"access_token":"([^"]*)`).FindStringSubmatch(string(rBody))[1]
-
-		// Call the user info endpoint
-		req, err = http.NewRequest(http.MethodGet, fcUserInfo, nil)
-		if err != nil {
-			http.Error(w, "could not perform the request to France Connect userinfo url", http.StatusInternalServerError)
-			return
-		}
-		req.Header.Add("Authorization", "Bearer "+accessToken)
-		resp, err = client.Do(req)
-		if err != nil {
-			http.Error(w, "could not perform the request to France Connect userinfo url", http.StatusInternalServerError)
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 			return
 		}
-		rBodyFC, _ := ioutil.ReadAll(resp.Body)
-		i := FCIdentity{}
-		json.Unmarshal(rBodyFC, &i)
 
 		// Get the redirect url from the cookie
 		rd := ClientRequestData{}
 		_, err = tokens.ExtractAndValidateToken(r, "ClientRequestData", &rd, false)
-		fmt.Printf("Request Data:%v\n", rd)
 		if err != nil {
 			http.Error(w, "could not get the initial client request data from cookie", http.StatusInternalServerError)
 			return
@@ -165,19 +100,19 @@ func CreateOIDCServer() *http.ServeMux {
 			return
 		}
 
-		// Match the fetched company to the user company
-		// TODO : Check if there is a mandate
-		// TODO : Iterate on all MandatairesSociaux and clean the big if
-		if len(e.MandatairesSociaux) < 1 {
-			http.Error(w, "there is no Mandataires Sociaux for this SIREN/T", http.StatusOK)
-			return
-		}
-		if e.MandatairesSociaux[0].Nom != i.FamilyName || e.MandatairesSociaux[0].Prenom != i.GivenName || e.MandatairesSociaux[0].DateNaissance != i.Birthdate { // If not redirect to the matcher to ask for a mandate
-			// Add the asker identity to the request data
-			rd.Id = i
-			tokens.CreateCookie(rd, "localhost", "ClientRequestData", 60*time.Second, w)
-			http.Redirect(w, req, "/matcher", http.StatusFound)
-			return
+		if !mandate.Exists(rd.Sirent, i.Sub) { // If there is a mandate, continue
+			if len(e.MandatairesSociaux) < 1 {
+				http.Error(w, "there is no Mandataires Sociaux for this SIREN/T", http.StatusOK)
+				return
+			}
+			// TODO : Iterate on all MandatairesSociaux and clean the big if
+			if e.MandatairesSociaux[0].Nom != i.FamilyName || e.MandatairesSociaux[0].Prenom != i.GivenName || e.MandatairesSociaux[0].DateNaissance != i.Birthdate { // If not redirect to the matcher to ask for a mandate
+				// Add the asker identity to the request data
+				rd.Id = i
+				tokens.CreateCookie(rd, "localhost", "ClientRequestData", 60*time.Second, w)
+				http.Redirect(w, r, "/matcher", http.StatusFound)
+				return
+			}
 		}
 
 		// Redirect to the initial caller with an code that it actually the data bundled in an opaque token
@@ -190,7 +125,7 @@ func CreateOIDCServer() *http.ServeMux {
 			http.Error(w, "could not create an authorisation code", http.StatusInternalServerError)
 			return
 		}
-		http.Redirect(w, req, rd.RedirectURI+"?code="+url.QueryEscape(code), http.StatusFound)
+		http.Redirect(w, r, rd.RedirectURI+"?code="+url.QueryEscape(code), http.StatusFound)
 	})
 
 	// Returns access token back to the user
diff --git a/web/components/auth/auth.js b/web/components/auth/auth.js
index 02e51976a001ab8fb5429b3d2ec90eeb6f01dc25..4dd86dcad6ce339f7a6be91af8a0314932cb148d 100644
--- a/web/components/auth/auth.js
+++ b/web/components/auth/auth.js
@@ -15,6 +15,7 @@ class Auth {
     <p>Pour essayer avec une autre entreprise, prendre par exemple la Fnac : 350127460</p>
     <input type="text" id="auth-sirent" value="000000001"></input>
     <button id="auth-do">OK</button>
+    <Pour tester un mandatement, utiliser la valeur "000000001", mais avec une connexion France Connect avec le compte "sans_nom_dusage".</p>
     `;
     // TODO : Check SIREN/T is in input and looks like a SIREN/T
     document.getElementById(`auth-do`).addEventListener("click", () => {
diff --git a/web/components/matcher/matcher.js b/web/components/matcher/matcher.js
index 2616f705a71690527c9a72503e601ecb842da66b..360b0a7c1acfbe8960baa3ef9c1d6cea504b256c 100644
--- a/web/components/matcher/matcher.js
+++ b/web/components/matcher/matcher.js
@@ -17,7 +17,7 @@ class Matcher {
       <p>
         Voulez vous qu'une demande de mandatement soit envoyée au dirigeant ?
       </p>
-      <input type="text" id="matcher-email" value="npernoud@grandlyon.com"></input>
+      <input type="text" id="matcher-email" value="nicolas@alpha.grandlyon.com"></input>
       <button id="matcher-do">OK</button>
       <button id="matcher-cancel">Annuler</button>
     `;
diff --git a/web/main.js b/web/main.js
index b42fa22f22c87b918bf9108d267944dc96da09a5..527d4dfe8a53b36d800e7ccf437c50103f72d45e 100644
--- a/web/main.js
+++ b/web/main.js
@@ -22,8 +22,18 @@ async function navigate() {
       });
       break;
     case "/callback":
-      //Redirect to server callback
-      location.pathname = "/api/oidc/callback";
+      // Redirect to server callback according to cookie
+      try {
+        const callbackRoute = document.cookie
+          .split("; ")
+          .find((row) => row.startsWith("callbackRoute="))
+          .split("=")[1];
+        if (callbackRoute != "" && callbackRoute != null) {
+          location.pathname = callbackRoute;
+        }
+      } catch (e) {
+        console.error("no callback route was gotten from the cookie");
+      }
       break;
     default:
       location.pathname = "auth";