import PromiseRouter from "express-promise-router"; import ApiError from "@common/ApiError"; import { ErrorCode } from "@common/ErrorCode"; import * as httpApi from "@common/httpApi"; import * as authentication from "@server/authentication"; import { User } from "@server/entities"; import { verifyAuthorization } from "@server/express/verifyAuthorization"; import { ServerState } from "@server/state"; export function token(state: ServerState) { const router = PromiseRouter(); async function passwordGrant( body: httpApi.TokenGrantPasswordRequest, res: Express.Response ): Promise { const { username, password } = body; if (!body || !username || !password) { throw new ApiError("Must specify username and password"); } const user = await state.database.users.findByUsername(username); if (!user) { throw new ApiError("User does not exist"); } const passwordMatches = await user.comparePassword(password); if (passwordMatches) { return user; } else { throw new ApiError("Invalid user credentials"); } } async function refreshGrant( body: httpApi.TokenGrantRefreshRequest, res: Express.Response ): Promise { const { refresh_token } = body; if (!body || !refresh_token) { throw new ApiError("Must specify a refresh_token", ErrorCode.BadToken); } const claims = await authentication.verifyToken(refresh_token); if (claims.type !== "refresh") { throw new ApiError("Not a refresh token", ErrorCode.BadToken); } const user = await state.database.users.findOne(claims.aud); if (!user) { throw new ApiError("User no longer exists", ErrorCode.BadToken); } return user; } router.post("/grant", async (req, res) => { const body: httpApi.TokenGrantRequest = req.body; let user: User; if (body.grant_type === "password") { user = await passwordGrant(body, res); } else if (body.grant_type === "refresh") { user = await refreshGrant(body, res); } else { throw new ApiError("Invalid grant_type"); } // tslint:disable-next-line:variable-name const [access_token, refresh_token] = await Promise.all([ await authentication.generateAccessToken(user), await authentication.generateRefreshToken(user) ]); const response: httpApi.TokenGrantResponse = { access_token, refresh_token }; res.json(response); }); router.post("/grant_device_reg", verifyAuthorization(), async (req, res) => { // tslint:disable-next-line:no-shadowed-variable const token = await authentication.generateDeviceRegistrationToken(); res.json({ token }); }); router.post("/verify", verifyAuthorization(), async (req, res) => { res.json({ ok: true, token: req.token }); }); return router; }