diff --git a/web/src/components/App.js b/web/src/components/App.js
index f0c48dec..36c90f76 100644
--- a/web/src/components/App.js
+++ b/web/src/components/App.js
@@ -30,6 +30,7 @@ import api from "../app/Api";
 import prefs from "../app/Prefs";
 import session from "../app/Session";
 import Pricing from "./Pricing";
+import Signup from "./Signup";
 
 // TODO races when two tabs are open
 // TODO investigate service workers
@@ -45,6 +46,7 @@ const App = () => {
                             <Route path={routes.home} element={<Home/>}/>
                             <Route path={routes.pricing} element={<Pricing/>}/>
                             <Route path={routes.login} element={<Login/>}/>
+                            <Route path={routes.signup} element={<Signup/>}/>
                             <Route element={<Layout/>}>
                                 <Route path={routes.app} element={<AllSubscriptions/>}/>
                                 <Route path={routes.settings} element={<Preferences/>}/>
diff --git a/web/src/components/Login.js b/web/src/components/Login.js
index 7f1469d3..eefc4b36 100644
--- a/web/src/components/Login.js
+++ b/web/src/components/Login.js
@@ -1,29 +1,14 @@
 import * as React from 'react';
-import {Avatar, Checkbox, FormControlLabel, Grid, Link, Stack} from "@mui/material";
+import {Avatar, Checkbox, FormControlLabel, Grid, Link} from "@mui/material";
 import Typography from "@mui/material/Typography";
-import Container from "@mui/material/Container";
 import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
 import TextField from "@mui/material/TextField";
 import Button from "@mui/material/Button";
 import Box from "@mui/material/Box";
 import api from "../app/Api";
-import {useNavigate} from "react-router-dom";
 import routes from "./routes";
 import session from "../app/Session";
 
-const Copyright = (props) => {
-    return (
-        <Typography variant="body2" color="text.secondary" align="center" {...props}>
-            {'Copyright © '}
-            <Link color="inherit" href="https://mui.com/">
-                Your Website
-            </Link>{' '}
-            {new Date().getFullYear()}
-            {'.'}
-        </Typography>
-    );
-};
-
 const Login = () => {
     const handleSubmit = async (event) => {
         event.preventDefault();
@@ -93,14 +78,13 @@ const Login = () => {
                             </Link>
                         </Grid>
                         <Grid item>
-                            <Link href="#" variant="body2">
+                            <Link to={routes.signup} variant="body2">
                                 {"Don't have an account? Sign Up"}
                             </Link>
                         </Grid>
                     </Grid>
                 </Box>
             </Box>
-            <Copyright sx={{mt: 8, mb: 4}}/>
         </>
     );
 }
diff --git a/web/src/components/Signup.js b/web/src/components/Signup.js
new file mode 100644
index 00000000..408ca6ae
--- /dev/null
+++ b/web/src/components/Signup.js
@@ -0,0 +1,94 @@
+import * as React from 'react';
+import {Avatar, Checkbox, FormControlLabel, Grid, Link, Stack} from "@mui/material";
+import Typography from "@mui/material/Typography";
+import Container from "@mui/material/Container";
+import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
+import TextField from "@mui/material/TextField";
+import Button from "@mui/material/Button";
+import Box from "@mui/material/Box";
+import api from "../app/Api";
+import {useNavigate} from "react-router-dom";
+import routes from "./routes";
+import session from "../app/Session";
+
+const Signup = () => {
+    const handleSubmit = async (event) => {
+        event.preventDefault();
+        const data = new FormData(event.currentTarget);
+        const user = {
+            username: data.get('username'),
+            password: data.get('password'),
+        }
+        const token = await api.login("http://localhost:2586"/*window.location.origin*/, user);
+        console.log(`[Api] User auth for user ${user.username} successful, token is ${token}`);
+        session.store(user.username, token);
+        window.location.href = routes.app;
+    };
+
+    return (
+        <>
+            <Box
+                sx={{
+                    marginTop: 8,
+                    display: 'flex',
+                    flexDirection: 'column',
+                    alignItems: 'center',
+                }}
+            >
+                <Avatar sx={{m: 1, bgcolor: 'secondary.main'}}>
+                    <LockOutlinedIcon/>
+                </Avatar>
+                <Typography component="h1" variant="h5">
+                    Sign in
+                </Typography>
+                <Box component="form" onSubmit={handleSubmit} noValidate sx={{mt: 1}}>
+                    <TextField
+                        margin="normal"
+                        required
+                        fullWidth
+                        id="username"
+                        label="Username"
+                        name="username"
+                        autoFocus
+                    />
+                    <TextField
+                        margin="normal"
+                        required
+                        fullWidth
+                        name="password"
+                        label="Password"
+                        type="password"
+                        id="password"
+                        autoComplete="current-password"
+                    />
+                    <FormControlLabel
+                        control={<Checkbox value="remember" color="primary"/>}
+                        label="Remember me"
+                    />
+                    <Button
+                        type="submit"
+                        fullWidth
+                        variant="contained"
+                        sx={{mt: 3, mb: 2}}
+                    >
+                        Sign up
+                    </Button>
+                    <Grid container>
+                        <Grid item xs>
+                            <Link href="#" variant="body2">
+                                Forgot password?
+                            </Link>
+                        </Grid>
+                        <Grid item>
+                            <Link to={routes.signup} variant="body2">
+                                {"Don't have an account? Sign Up"}
+                            </Link>
+                        </Grid>
+                    </Grid>
+                </Box>
+            </Box>
+        </>
+    );
+}
+
+export default Signup;
diff --git a/web/src/img/ntfy2.svg b/web/src/img/ntfy2.svg
new file mode 100644
index 00000000..cd5f908e
--- /dev/null
+++ b/web/src/img/ntfy2.svg
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   width="50mm"
+   height="50mm"
+   viewBox="0 0 50 50"
+   version="1.1"
+   id="svg8"
+   inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
+   sodipodi:docname="appstore_ios.svg"
+   inkscape:export-filename="/home/pheckel/Code/ntfy-android/assets/appstore_ios.png"
+   inkscape:export-xdpi="520.19202"
+   inkscape:export-ydpi="520.19202"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
+  <defs
+     id="defs2">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4714">
+      <stop
+         style="stop-color:#348878;stop-opacity:1"
+         offset="0"
+         id="stop4710" />
+      <stop
+         style="stop-color:#52bca6;stop-opacity:1"
+         offset="1"
+         id="stop4712" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient28858-5">
+      <stop
+         style="stop-color:#348878;stop-opacity:1"
+         offset="0"
+         id="stop28854-3" />
+      <stop
+         style="stop-color:#56bda8;stop-opacity:1"
+         offset="1"
+         id="stop28856-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient28858-5"
+       id="linearGradient3255"
+       x1="160.72209"
+       y1="128.53317"
+       x2="168.41153"
+       y2="134.32626"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(3.7495873,0,0,3.7495873,-541.79055,-387.59852)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4714"
+       id="linearGradient4633"
+       x1="0.034492966"
+       y1="-0.0003150744"
+       x2="50.319355"
+       y2="50.284546"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99433502,0,0,0.99433502,-0.03429756,-1.7848888e-6)" />
+    <filter
+       style="color-interpolation-filters:sRGB;"
+       inkscape:label="Drop Shadow"
+       id="filter3958"
+       x="-0.076083149"
+       y="-0.091641662"
+       width="1.1759423"
+       height="1.2114791">
+      <feFlood
+         flood-opacity="0.192157"
+         flood-color="rgb(0,0,0)"
+         result="flood"
+         id="feFlood3948" />
+      <feComposite
+         in="flood"
+         in2="SourceGraphic"
+         operator="in"
+         result="composite1"
+         id="feComposite3950" />
+      <feGaussianBlur
+         in="composite1"
+         stdDeviation="4"
+         result="blur"
+         id="feGaussianBlur3952" />
+      <feOffset
+         dx="3"
+         dy="2.95367"
+         result="offset"
+         id="feOffset3954" />
+      <feComposite
+         in="SourceGraphic"
+         in2="offset"
+         operator="over"
+         result="composite2"
+         id="feComposite3956" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.8244841"
+     inkscape:cx="4.6588512"
+     inkscape:cy="174.84395"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer3"
+     showgrid="false"
+     inkscape:measure-start="0,0"
+     inkscape:measure-end="0,0"
+     inkscape:snap-text-baseline="true"
+     inkscape:window-width="1863"
+     inkscape:window-height="1025"
+     inkscape:window-x="57"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     showguides="false"
+     inkscape:guide-bbox="true"
+     inkscape:pagecheckerboard="0">
+    <sodipodi:guide
+       position="10.173514,67.718331"
+       orientation="1,0"
+       id="guide1770" />
+    <sodipodi:guide
+       position="39.965574,62.077508"
+       orientation="1,0"
+       id="guide1772" />
+    <sodipodi:guide
+       position="10.173514,39.789015"
+       orientation="0,-1"
+       id="guide1774" />
+    <sodipodi:guide
+       position="-2.3077334,9.9462015"
+       orientation="0,-1"
+       id="guide1776" />
+    <sodipodi:guide
+       position="14.990626,36.198285"
+       orientation="1,0"
+       id="guide4020" />
+    <sodipodi:guide
+       position="34.930725,39.789015"
+       orientation="1,0"
+       id="guide4022" />
+    <sodipodi:guide
+       position="12.7026,32.00465"
+       orientation="0,-1"
+       id="guide4024" />
+    <sodipodi:guide
+       position="11.377711,17.981227"
+       orientation="0,-1"
+       id="guide4026" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="background"
+     style="display:inline">
+    <rect
+       style="fill:url(#linearGradient4633);fill-opacity:1;stroke:none;stroke-width:0.286502;stroke-linejoin:bevel"
+       id="rect4545"
+       width="50"
+       height="50"
+       x="0"
+       y="-0.0003150744" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer5"
+     inkscape:label="drop shadow"
+     style="display:inline">
+    <path
+       id="path3646"
+       style="color:#000000;display:inline;fill:#ffffff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none;filter:url(#filter3958)"
+       d="m 50.400391,46.882812 c -9.16879,0 -17.023438,7.2146 -17.023438,16.386719 v 0.0078 l 0.08984,71.369139 -2.302735,16.99219 31.3125,-8.31836 h 77.841802 c 9.16877,0 17.02344,-7.22425 17.02344,-16.39648 V 63.269531 c 0,-9.169496 -7.85031,-16.382463 -17.01563,-16.386719 h -0.008 z m 0,11.566407 h 89.917969 0.008 c 3.22151,0.0033 5.44922,2.346918 5.44922,4.820312 v 63.654299 c 0,2.47551 -2.23164,4.82031 -5.45703,4.82031 H 60.779297 l -15.908203,4.80664 0.162109,-0.9375 -0.08789,-72.343749 c 0,-2.475337 2.229739,-4.820312 5.455078,-4.820312 z"
+       transform="scale(0.26458333)" />
+  </g>
+  <g
+     inkscape:label="foreground"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-51.147327,-81.515579)"
+     style="display:inline">
+    <path
+       style="color:#000000;fill:url(#linearGradient3255);stroke:none;stroke-width:2.49558;-inkscape-stroke:none"
+       d="M 88.200706,95.308804 H 64.918622 c -1.600657,0 -2.910245,1.235977 -2.910245,2.74661 l 0.02224,18.601596 -0.434711,2.5057 6.231592,-1.88118 h 20.371766 c 1.600658,0 2.910282,-1.23597 2.910282,-2.74664 V 98.055414 c 0,-1.510633 -1.309624,-2.74661 -2.910282,-2.74661 z"
+       id="path7368" />
+    <path
+       id="path2498"
+       style="color:#000000;fill:#ffffff;stroke:none;stroke-width:1.93113;-inkscape-stroke:none"
+       d="m 50.400391,46.882812 c -9.16879,0 -17.023438,7.2146 -17.023438,16.386719 v 0.0078 l 0.08984,71.369139 -2.302735,16.99219 31.3125,-8.31836 h 77.841802 c 9.16877,0 17.02344,-7.22425 17.02344,-16.39648 V 63.269531 c 0,-9.169496 -7.85031,-16.382463 -17.01563,-16.386719 h -0.008 z m 0,11.566407 h 89.917969 0.008 c 3.22151,0.0033 5.44922,2.346918 5.44922,4.820312 v 63.654299 c 0,2.47551 -2.23164,4.82031 -5.45703,4.82031 H 60.779297 l -15.908203,4.80664 0.162109,-0.9375 -0.08789,-72.343749 c 0,-2.475337 2.229739,-4.820312 5.455078,-4.820312 z"
+       transform="matrix(0.26458333,0,0,0.26458333,51.147327,81.515579)" />
+    <g
+       id="path1011-6-2"
+       transform="matrix(1.4536603,0,0,1.728146,-23.97473,-90.437157)"
+       style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke:none;stroke-width:0.525121">
+      <path
+         style="color:#000000;-inkscape-font-specification:'JetBrains Mono, Bold';fill:#ffffff;stroke:none;-inkscape-stroke:none"
+         d="m 62.57046,116.77004 v -1.31201 l 3.280018,-1.45904 q 0.158346,-0.0679 0.305381,-0.1018 0.158346,-0.0452 0.282761,-0.0679 0.135725,-0.0113 0.271449,-0.0226 v -0.0905 q -0.135724,-0.0113 -0.271449,-0.0452 -0.124415,-0.0226 -0.282761,-0.0566 -0.147035,-0.0452 -0.305381,-0.1131 l -3.280018,-1.45904 v -1.32332 l 5.067063,2.31863 v 1.4138 z"
+         id="path7553" />
+      <path
+         style="color:#000000;-inkscape-font-specification:'JetBrains Mono, Bold';fill:#ffffff;stroke:none;-inkscape-stroke:none"
+         d="m 62.308594,110.31055 v 1.90234 l 3.4375,1.5293 c 0.0073,0.003 0.0142,0.005 0.02148,0.008 -0.0073,0.003 -0.0142,0.005 -0.02148,0.008 l -3.4375,1.5293 v 1.89258 l 0.371093,-0.16992 5.220704,-2.39063 v -1.75 z m 0.52539,0.8164 4.541016,2.08008 v 1.07617 l -4.541016,2.07813 v -0.73242 l 3.119141,-1.38868 0.0039,-0.002 c 0.09141,-0.0389 0.178343,-0.0676 0.257813,-0.0859 h 0.0059 l 0.0078,-0.002 c 0.09483,-0.0271 0.176055,-0.0474 0.246093,-0.0606 l 0.498047,-0.041 v -0.57422 l -0.240234,-0.0195 c -0.07606,-0.006 -0.153294,-0.0198 -0.230469,-0.0391 l -0.0078,-0.002 -0.0078,-0.002 c -0.07608,-0.0138 -0.16556,-0.0318 -0.263672,-0.0527 -0.08398,-0.0262 -0.172736,-0.058 -0.265625,-0.0977 l -0.0039,-0.002 -3.119141,-1.38868 z"
+         id="path7555" />
+    </g>
+    <g
+       id="g1224"
+       transform="matrix(1.4493527,0,0,1.6641427,-22.956963,-85.389973)"
+       style="font-size:8.48274px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;stroke:none;stroke-width:0.525121">
+      <path
+         style="color:#000000;-inkscape-font-specification:'JetBrains Mono, Bold';fill:#ffffff;stroke:none;-inkscape-stroke:none"
+         d="m 69.17132,117.75404 h 5.428996 v 1.27808 H 69.17132 Z"
+         id="path1220" />
+      <path
+         style="color:#000000;-inkscape-font-specification:'JetBrains Mono, Bold';fill:#ffffff;stroke:none;-inkscape-stroke:none"
+         d="m 68.908203,117.49219 v 0.26172 1.54101 h 5.955078 v -1.80273 z m 0.525391,0.52344 h 4.904297 v 0.7539 h -4.904297 z"
+         id="path1222" />
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="round icon preview"
+     style="display:none">
+    <path
+       id="path18850-8-1"
+       style="display:inline;fill:#ffffff;fill-opacity:1;stroke-width:0.255654"
+       d="M 50.337488,80.973198 V 131.61213 H 101.65302 V 80.973198 Z m 25.676545,1.442307 h 0.555989 a 24.369387,24.369387 0 0 1 23.860308,21.232925 v 6.09963 a 24.369387,24.369387 0 0 1 -21.288308,21.19336 h 21.288308 v 0.0138 H 51.963792 v -0.0158 H 73.428179 A 24.369387,24.369387 0 0 1 51.963792,107.97535 v -2.49089 A 24.369387,24.369387 0 0 1 76.014033,82.415508 Z"
+       transform="translate(-51.147326,-81.51558)" />
+  </g>
+</svg>