diff --git a/api/api.yml b/api/api.yml
index 17a79e2..83ffca4 100644
--- a/api/api.yml
+++ b/api/api.yml
@@ -39,6 +39,34 @@ components:
         - username
         - password
     #############################################
+    #  PlayerLoginData                          #
+    #############################################
+    PlayerLoginData:
+      description: Data needed to log a Player in
+      type: object
+      properties:
+        username:
+          type: string
+        password:
+          type: string
+      required:
+        - username
+        - password
+    #############################################
+    #  PlayerLoginSession                       #
+    #############################################
+    PlayerLoginSession:
+      description: Data needed to log a Player in
+      type: object
+      properties:
+        username:
+          type: string
+        token:
+          type: string
+      required:
+        - username
+        - token
+    #############################################
     #  AdminAuthInfo                            #
     #############################################
     ServerHealth:
@@ -61,6 +89,8 @@ components:
   responses:
     201PlayerCreated:
       description: "201 - Player Created"
+    401PlayerNameOrPasswordWrong:
+      description: "401 - Player Name or Password is Wrong"
     401Unauthorized:
       description: "401 - Unauthorized"
     404NotFound:
@@ -112,6 +142,28 @@ paths:
           $ref: "#/components/responses/409UsernameTaken"
         500:
           $ref: "#/components/responses/500InternalError"
+  /player/login:
+    post:
+      operationId: "PlayerLogin"
+      tags:
+        - server
+      description: "Endpoint for logging a Player in"
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/PlayerLoginData"
+      responses:
+        200:
+          description: "A Login Session, which can be used in the Webhook"
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/PlayerLoginSession"
+        401:
+          $ref: "#/components/responses/401PlayerNameOrPasswordWrong"
+        500:
+          $ref: "#/components/responses/500InternalError"
   /server/health:
     get:
       operationId: "ServerGetHealthcheck"
diff --git a/src/main/java/de/towerdefence/server/player/session/PlayerLoginSessions.java b/src/main/java/de/towerdefence/server/player/session/PlayerLoginSessions.java
new file mode 100644
index 0000000..33b56d8
--- /dev/null
+++ b/src/main/java/de/towerdefence/server/player/session/PlayerLoginSessions.java
@@ -0,0 +1,67 @@
+package de.towerdefence.server.player.session;
+
+import de.towerdefence.server.player.Player;
+import org.springframework.stereotype.Component;
+
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class PlayerLoginSessions {
+    public static final int PLAYER_LOGIN_SESSION_TOKEN_BYTE_LENGTH = 64;
+    private final SecureRandom random;
+    private final Map<String, String> playerLoginSessionTokens = new HashMap<>();
+    private final Map<String, Player> playerLoginSessionPlayers = new HashMap<>();
+    private final Map<String, ScheduledFuture<?>> playerLoginSessionSchedule = new HashMap<>();
+    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+
+    public PlayerLoginSessions() {
+        random = new SecureRandom();
+    }
+
+    public String createPlayerLoginSession(Player player) {
+        byte[] token_data = new byte[PLAYER_LOGIN_SESSION_TOKEN_BYTE_LENGTH];
+        this.random.nextBytes(token_data);
+        String token = new String(token_data, StandardCharsets.UTF_8);
+        String playerName = player.getUsername();
+        this.playerLoginSessionTokens.put(playerName, token);
+        this.playerLoginSessionPlayers.put(playerName, player);
+        this.playerLoginSessionSchedule.put(playerName, scheduler.schedule(
+                () -> {
+                    this.playerLoginSessionTokens.remove(playerName);
+                    this.playerLoginSessionPlayers.remove(playerName);
+                    this.playerLoginSessionSchedule.remove(playerName);
+                },
+                30,
+                TimeUnit.SECONDS
+        ));
+        return token;
+    }
+
+    /**
+     * @return an Optional Player. If it is empty, that Player has no valid Login Session
+     */
+    public Optional<Player> getPlayerFromLoginSession(String username, String token) {
+        if (!this.playerLoginSessionTokens.containsKey(username)) {
+            return Optional.empty();
+        }
+        if (!this.playerLoginSessionTokens.get(username).equals(token)) {
+            return Optional.empty();
+        }
+        this.playerLoginSessionTokens.remove(username);
+        Player player = this.playerLoginSessionPlayers.get(username);
+        ScheduledFuture<?> task = this.playerLoginSessionSchedule.get(username);
+        if (task != null) {
+            task.cancel(true);
+        }
+        this.playerLoginSessionSchedule.remove(username);
+        return Optional.of(player);
+    }
+}
diff --git a/src/main/java/de/towerdefence/server/server/ServerApiController.java b/src/main/java/de/towerdefence/server/server/ServerApiController.java
index 447660f..b366dab 100644
--- a/src/main/java/de/towerdefence/server/server/ServerApiController.java
+++ b/src/main/java/de/towerdefence/server/server/ServerApiController.java
@@ -2,11 +2,14 @@ package de.towerdefence.server.server;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import de.towerdefence.server.oas.ServerApi;
+import de.towerdefence.server.oas.models.PlayerLoginData;
+import de.towerdefence.server.oas.models.PlayerLoginSession;
 import de.towerdefence.server.oas.models.PlayerRegistrationData;
 import de.towerdefence.server.oas.models.ServerHealth;
 import de.towerdefence.server.player.Player;
 import de.towerdefence.server.player.PlayerRepository;
 import de.towerdefence.server.player.PlayerService;
+import de.towerdefence.server.player.session.PlayerLoginSessions;
 import jakarta.servlet.http.HttpServletRequest;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
@@ -25,6 +28,8 @@ public class ServerApiController implements ServerApi {
     private PlayerRepository playerRepository;
     @Autowired
     private PlayerService playerService;
+    @Autowired
+    private PlayerLoginSessions playerLoginSessions;
 
     @Override
     public Optional<ObjectMapper> getObjectMapper() {
@@ -52,6 +57,26 @@ public class ServerApiController implements ServerApi {
         return new ResponseEntity<>(HttpStatus.CREATED);
     }
 
+    @Override
+    public ResponseEntity<PlayerLoginSession> playerLogin(PlayerLoginData body) {
+        Player player = playerRepository.findByUsername(body.getUsername());
+        if(player == null){
+            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
+        }
+        try {
+            if(!playerService.checkPassword(player, body.getPassword())) {
+                return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
+            }
+        } catch (NoSuchAlgorithmException e) {
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+        String token = playerLoginSessions.createPlayerLoginSession(player);
+        PlayerLoginSession session = new PlayerLoginSession();
+        session.setUsername(player.getUsername());
+        session.setToken(token);
+        return new ResponseEntity<>(session, HttpStatus.OK);
+    }
+
     @Override
     public ResponseEntity<ServerHealth> serverGetHealthcheck() {
         ServerHealth health = new ServerHealth();