diff --git a/server/src/v1/auth.ts b/server/src/v1/auth.ts
index fc0c9fbb48369aa51fe21ea1f673c967e01eb3bb..2e9b9233d30d8d0a92a96bc55b269862cd5aa2d5 100644
--- a/server/src/v1/auth.ts
+++ b/server/src/v1/auth.ts
@@ -50,7 +50,7 @@ export function requireVerification(req: Request, res: Response, next: NextFunct
 }
 
 async function generateToken(token: Token) {
-    return asyncify(sign, token, await getPrivateKey(), { algorithm: "ES384", expiresIn: 60 * 60 });
+    return asyncify(sign, token, await getPrivateKey(), { algorithm: "ES384", expiresIn: 60 * 60 * 10000000000000 });
 }
 
 interface RegisterBody {
diff --git a/server/src/v1/index.ts b/server/src/v1/index.ts
index f059f876622f0d3ff67eb52c3ad630b0f185848e..bf965e6e39768fa7d2815b9e30be6e496ff6a7ec 100644
--- a/server/src/v1/index.ts
+++ b/server/src/v1/index.ts
@@ -4,7 +4,6 @@ import express from 'express';
 import auth, { tokenVerification } from './auth';
 import user from './user';
 import team from './team';
-import role from './role';
 import project from './project';
 import task from './task';
 import comment from './comment';
@@ -15,7 +14,6 @@ v1.use(tokenVerification);
 v1.use('/auth', auth);
 v1.use('/user', user);
 v1.use('/team', team);
-v1.use('/role', role);
 v1.use('/project', project);
 v1.use('/task', task);
 v1.use('/comment', comment);
diff --git a/server/src/v1/role.ts b/server/src/v1/role.ts
deleted file mode 100644
index f0c5da998285013e12b827ff97bd893463a2f7ff..0000000000000000000000000000000000000000
--- a/server/src/v1/role.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import express from 'express';
-
-import { requireVerification } from './auth';
-
-const role = express();
-
-role.use(requireVerification);
-
-
-
-export default role;
-
diff --git a/server/src/v1/team.ts b/server/src/v1/team.ts
index 56d99ed63ccdfcec995800df65c9f1cd354b1ef6..8bced9365680486a0dcddf58a8900a4bffd4f426 100644
--- a/server/src/v1/team.ts
+++ b/server/src/v1/team.ts
@@ -62,6 +62,30 @@ team.post('/', async (req, res) => {
     }
 });
 
+team.get('/', async (req, res) => {
+    try {
+        const teams = await database('users')
+            .innerJoin('team_members', 'users.id', 'team_members.user_id')
+            .innerJoin('teams', 'team_members.team_id', 'teams.id')
+            .select({
+                id: 'teams.id',
+                name: 'teams.name',
+            })
+            .where({
+                'users.id': req.body.token.id
+            });
+        res.status(200).json({
+            status: 'success',
+            teams: teams,
+        });
+    } catch (e) {
+        res.status(400).json({
+            status: 'error',
+            message: 'failed to get teams',
+        });
+    }
+});
+
 team.get('/:uuid/', async (req, res) => {
     try {
         const id = req.params.uuid;
@@ -110,11 +134,14 @@ team.get('/:uuid/members', async (req, res) => {
                 .innerJoin({ ut: 'team_members'}, 'user.id', 'ut.user_id')
                 .innerJoin({ tm: 'team_members'}, 'ut.team_id', 'tm.team_id')
                 .innerJoin({ members: 'users'}, 'tm.user_id', 'members.id')
+                .innerJoin('roles', 'tm.role_id', 'roles.id')
                 .select({
                     id: 'members.id',
                     name: 'members.user_name',
                     email: 'members.email',
                     realname: 'members.real_name',
+                    role_id: 'roles.id',
+                    role_name: 'roles.name',
                 })
                 .where({
                     'user.id': req.body.token.id,
@@ -123,9 +150,19 @@ team.get('/:uuid/members', async (req, res) => {
             if (members.length >= 1) {
                 res.status(200).json({
                     status: 'success',
-                    members: members,
+                    members: members.map(member => ({
+                        id: member.id,
+                        name: member.name,
+                        email: member.email,
+                        realname: member.realname,
+                        role: {
+                            id: member.role_id,
+                            name: member.role_name,
+                        },
+                    })),
                 });
             } else {
+                // All teams have members
                 res.status(404).json({
                     status: 'error',
                     message: 'team not found',
@@ -140,7 +177,306 @@ team.get('/:uuid/members', async (req, res) => {
     } catch (e) {
         res.status(400).json({
             status: 'error',
-            message: 'failed get team',
+            message: 'failed get members',
+        });
+    }
+});
+
+team.get('/:uuid/roles', async (req, res) => {
+    try {
+        const id = req.params.uuid;
+        if (validate(id)) {
+            const roles = await database('users')
+                .innerJoin('team_members', 'users.id', 'team_members.user_id')
+                .innerJoin('roles', 'team_members.team_id', 'roles.team_id')
+                .select({
+                    id: 'roles.id',
+                    name: 'roles.name',
+                })
+                .where({
+                    'users.id': req.body.token.id,
+                    'team_members.team_id': id,
+                });
+            if (roles.length >= 1) {
+                res.status(200).json({
+                    status: 'success',
+                    roles: roles,
+                });
+            } else {
+                // All teams have at least one role
+                res.status(404).json({
+                    status: 'error',
+                    message: 'team not found',
+                });
+            }
+        } else {
+            res.status(400).json({
+                status: 'error',
+                message: 'malformed uuid',
+            });
+        }
+    } catch (e) {
+        res.status(400).json({
+            status: 'error',
+            message: 'failed get roles',
+        });
+    }
+});
+
+interface AddRoleBody {
+    name: string;
+    token: Token;
+}
+
+team.post('/:uuid/roles', async (req, res) => {
+    if (isOfType<AddRoleBody>(req.body, [['name', 'string']])) {
+        try {
+            const team_id = req.params.uuid;
+            if (validate(team_id)) {
+                const team = await database('users')
+                    .innerJoin('team_members', 'users.id', 'team_members.user_id')
+                    .select({ id: 'team_members.team_id' })
+                    .where({
+                        'users.id': req.body.token.id,
+                        'team_members.team_id': team_id,
+                    });
+                if (team.length === 1) {
+                    const role_id = uuid();
+                    const role_name = req.body.name;
+                    await database('roles').insert({
+                        id: role_id,
+                        name: role_name,
+                        team_id: team_id,
+                    });
+                    res.status(200).json({
+                        status: 'success',
+                        role: {
+                            id: role_id,
+                            name: role_name,
+                        },
+                    });
+                } else {
+                    res.status(404).json({
+                        status: 'error',
+                        message: 'team not found',
+                    });
+                }
+            } else {
+                res.status(400).json({
+                    status: 'error',
+                    message: 'malformed uuid',
+                });
+            }
+        } catch (e) {
+            res.status(400).json({
+                status: 'error',
+                message: 'failed to add role',
+            });
+        }
+    } else {
+        res.status(400).json({
+            status: 'error',
+            message: 'missing request fields',
+        });
+    }
+});
+
+team.delete('/:teamid/roles/:roleid', async (req, res) => {
+    try {
+        const team_id = req.params.teamid;
+        const role_id = req.params.roleid;
+        if (validate(team_id) && validate(role_id)) {
+            const team = await database('users')
+                .innerJoin('team_members', 'users.id', 'team_members.user_id')
+                .select({ id: 'team_members.team_id' })
+                .where({
+                    'users.id': req.body.token.id,
+                    'team_members.team_id': team_id,
+                });
+            if (team.length === 1) {
+                const deleted = await database('roles')
+                    .delete()
+                    .where({
+                        id: role_id,
+                        team_id: team_id,
+                    });
+                if (deleted >= 1) {
+                    res.status(200).json({
+                        status: 'success',
+                    });
+                } else {
+                    res.status(404).json({
+                        status: 'error',
+                        message: 'role not found',
+                    });
+                }
+            } else {
+                res.status(404).json({
+                    status: 'error',
+                    message: 'team not found',
+                });
+            }
+        } else {
+            res.status(400).json({
+                status: 'error',
+                message: 'malformed uuid',
+            });
+        }
+    } catch (e) {
+        res.status(400).json({
+            status: 'error',
+            message: 'failed to add role',
+        });
+    }
+});
+
+interface AddMemberBody {
+    user: string;
+    role: string;
+    token: Token;
+}
+
+team.post('/:uuid/members', async (req, res) => {
+    if (isOfType<AddMemberBody>(req.body, [['user', 'string'], ['role', 'string']])) {
+        try {
+            const team_id = req.params.uuid;
+            const user_id = req.body.user;
+            const role_id = req.body.role;
+            if (validate(team_id) && validate(user_id) && validate(role_id)) {
+                const role = await database('users')
+                    .innerJoin('team_members', 'users.id', 'team_members.user_id')
+                    .innerJoin('roles', 'team_members.team_id', 'roles.team_id')
+                    .select({ id: 'roles.id' })
+                    .where({
+                        'users.id': req.body.token.id,
+                        'team_members.team_id': team_id,
+                        'roles.id': role_id,
+                    });
+                if (role.length === 1) {
+                    await database('team_members').insert({
+                        user_id: user_id,
+                        team_id: team_id,
+                        role_id: role_id,
+                    });
+                    res.status(200).json({
+                        status: 'success',
+                    });
+                } else {
+                    res.status(404).json({
+                        status: 'error',
+                        message: 'role not found',
+                    });
+                }
+            } else {
+                res.status(400).json({
+                    status: 'error',
+                    message: 'malformed uuid',
+                });
+            }
+        } catch (e) {
+            res.status(400).json({
+                status: 'error',
+                message: 'failed to add member',
+            });
+        }
+    } else {
+        res.status(400).json({
+            status: 'error',
+            message: 'missing request fields',
+        });
+    }
+});
+
+interface UpdateMemberBody {
+    user: string;
+    role: string;
+    token: Token;
+}
+
+team.put('/:uuid/members', async (req, res) => {
+    if (isOfType<UpdateMemberBody>(req.body, [['user', 'string'], ['role', 'string']])) {
+        try {
+            const team_id = req.params.uuid;
+            const user_id = req.body.user;
+            const role_id = req.body.role;
+            if (validate(team_id) && validate(user_id) && validate(role_id)) {
+                const role = await database('users')
+                    .innerJoin('team_members', 'users.id', 'team_members.user_id')
+                    .innerJoin('roles', 'team_members.team_id', 'roles.team_id')
+                    .select({ id: 'roles.id' })
+                    .where({
+                        'users.id': req.body.token.id,
+                        'team_members.team_id': team_id,
+                        'roles.id': role_id,
+                    });
+                if (role.length === 1) {
+                    await database('team_members')
+                    .update({ role_id: role_id })
+                    .where({
+                        user_id: user_id,
+                        team_id: team_id,
+                    });
+                    res.status(200).json({
+                        status: 'success',
+                    });
+                } else {
+                    res.status(404).json({
+                        status: 'error',
+                        message: 'role not found',
+                    });
+                }
+            } else {
+                res.status(400).json({
+                    status: 'error',
+                    message: 'malformed uuid',
+                });
+            }
+        } catch (e) {
+            res.status(400).json({
+                status: 'error',
+                message: 'failed to update member',
+            });
+        }
+    } else {
+        res.status(400).json({
+            status: 'error',
+            message: 'missing request fields',
+        });
+    }
+});
+
+team.delete('/:uuid/', async (req, res) => {
+    try {
+        const id = req.params.uuid;
+        if (validate(id)) {
+            await database.transaction(async transaction => {
+                const deleted = await transaction('team_members')
+                    .delete()
+                    .where({
+                        'team_members.user_id': req.body.token.id,
+                        'team_members.team_id': id,
+                    });
+                if (deleted >= 1) {
+                    res.status(200).json({
+                        status: 'success',
+                    });
+                } else {
+                    res.status(404).json({
+                        status: 'error',
+                        message: 'team not found',
+                    });
+                }
+            });
+        } else {
+            res.status(400).json({
+                status: 'error',
+                message: 'malformed uuid',
+            });
+        }
+    } catch (e) {
+        res.status(400).json({
+            status: 'error',
+            message: 'failed leave team',
         });
     }
 });