diff --git a/src/states/Login/tabs/LoginTab/components/TotpForm/TotpForm.jsx b/src/states/Login/tabs/LoginTab/components/TotpForm/TotpForm.jsx
new file mode 100644
index 0000000..58e4c4e
--- /dev/null
+++ b/src/states/Login/tabs/LoginTab/components/TotpForm/TotpForm.jsx
@@ -0,0 +1,46 @@
+import {Alert, Button, CircularProgress, Stack, TextField} from "@mui/material";
+import {useContext, useState} from "react";
+import {request} from "@/common/utils/RequestUtil.js";
+import {UserContext} from "@contexts/User";
+
+export const TotpForm = ({token}) => {
+
+    const [code, setCode] = useState("");
+    const [loading, setLoading] = useState(false);
+    const [error, setError] = useState(false);
+
+    const {updateSessionToken} = useContext(UserContext);
+
+    const login = (event) => {
+        event.preventDefault();
+
+        setError(false);
+        setLoading(true);
+
+        setTimeout(async () => {
+            try {
+                const data = await request("/auth/verify", "POST", {token, code: parseInt(code)});
+                if (data.code) throw new Error("Invalid code");
+
+                updateSessionToken(token);
+            } catch (e) {
+                setLoading(false);
+                setError(true);
+            }
+        }, 500);
+    }
+
+    return (
+        <Stack gap={1.5} component="form" onSubmit={login} sx={{width: "20rem"}}>
+            <Alert severity="info">Bitte geben Sie den Code aus Ihrer Authenticator App ein.</Alert>
+            {error && <Alert severity="error">Falscher 2FA Code!</Alert>}
+
+            <TextField label="Code" variant="outlined" type="number"
+                          placeholder="Code" autoComplete="one-time-code" value={code}
+                            onChange={(e) => setCode(e.target.value)}/>
+            <Button variant="contained" type="submit" disabled={loading}>
+                {loading ? <CircularProgress size={24} color="inherit"/> : "Anmelden"}
+            </Button>
+        </Stack>
+    )
+}
\ No newline at end of file