diff --git a/internal/apiuser/apiuser.go b/internal/apiuser/apiuser.go
new file mode 100644
index 0000000000000000000000000000000000000000..8e1ad2515141fbc07bfea290414a87be4f84406c
--- /dev/null
+++ b/internal/apiuser/apiuser.go
@@ -0,0 +1,120 @@
+package apiuser
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"net/http"
+	"strings"
+	"time"
+
+	"forge.grandlyon.com/npernoud/glcpro/internal/client"
+	"forge.grandlyon.com/npernoud/glcpro/pkg/common"
+	"forge.grandlyon.com/npernoud/glcpro/pkg/tokens"
+)
+
+// User represents a logged client
+type User struct {
+	ID    int    `json:"id,omitempty"`
+	Login string `json:"login"`
+}
+
+// TokenData represents the data held into a token
+type TokenData struct {
+	User
+	URL       string `json:"url,omitempty"`
+	XSRFToken string `json:"xsrftoken,omitempty"`
+}
+
+type LoginInfo struct {
+	Id_user string `json:"id_user,omitempty"`
+	Secret  string `json:"secret,omitempty"`
+}
+
+var (
+	TokenLifeTime time.Duration = 10000
+)
+
+type key int
+
+const (
+	AuthTokenKey     = "auth_token"
+	ContextData  key = 0
+)
+
+func Login(w http.ResponseWriter, r *http.Request) {
+	if r.Method != http.MethodPost {
+		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	var loginInfos LoginInfo
+	err := json.NewDecoder(r.Body).Decode(&loginInfos)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+
+	user, err := client.GetClient(loginInfos.Id_user, loginInfos.Secret)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusForbidden)
+		return
+	}
+
+	xsrfToken, err := common.GenerateRandomString(16)
+	if err != nil {
+		http.Error(w, "error generating XSRF Token", http.StatusInternalServerError)
+	}
+
+	tokenData := TokenData{User: User{ID: user.ID, Login: user.Id_client}, XSRFToken: xsrfToken}
+	tokens.CreateCookie(tokenData, common.GetDomain(r), AuthTokenKey, TokenLifeTime*time.Second, w)
+}
+
+func WhoAmI() http.Handler {
+
+	whoAmI := func(w http.ResponseWriter, r *http.Request) {
+		user, err := getTokenData(r)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+		json.NewEncoder(w).Encode(user)
+	}
+
+	return http.HandlerFunc(whoAmI)
+}
+
+func getTokenData(r *http.Request) (TokenData, error) {
+	user, ok := r.Context().Value(ContextData).(TokenData)
+	if !ok {
+		return user, errors.New("user could not be got from context")
+	}
+	return user, nil
+}
+
+func MiddlewareValidateAuth(next http.Handler, checkXSRF bool) http.Handler {
+	userChecker := func(w http.ResponseWriter, r *http.Request) {
+		user := TokenData{}
+		checkXSRF, err := tokens.ExtractAndValidateToken(r, AuthTokenKey, &user, checkXSRF)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+
+		if checkXSRF && r.Header.Get("XSRF-TOKEN") != user.XSRFToken {
+			http.Error(w, "XSRF protection triggered", http.StatusUnauthorized)
+		}
+
+		if user.URL != "" {
+			requestURL := strings.Split(r.Host, ":")[0] + r.URL.EscapedPath()
+			if user.URL != requestURL {
+				http.Error(w, "token restricted to url: "+user.URL, http.StatusUnauthorized)
+				return
+			}
+		}
+
+		ctx := context.WithValue(r.Context(), ContextData, user)
+		next.ServeHTTP(w, r.WithContext(ctx))
+	}
+	return http.HandlerFunc(userChecker)
+}