2018-11-06 19:48:50 +00:00
|
|
|
import { Request, Response } from "express";
|
|
|
|
import RequestError, { HttpStatusCode } from "../../helper/request_error";
|
|
|
|
import User from "../../models/user";
|
|
|
|
import Client from "../../models/client";
|
2020-08-07 14:16:39 +00:00
|
|
|
import {
|
|
|
|
getAccessTokenJWT,
|
|
|
|
getIDToken,
|
|
|
|
AccessTokenJWTExp,
|
|
|
|
} from "../../helper/jwt";
|
2018-11-06 19:48:50 +00:00
|
|
|
import Stacker from "../middlewares/stacker";
|
2020-08-07 14:16:39 +00:00
|
|
|
import { GetClientAuthMiddleware } from "../middlewares/client";
|
2018-11-06 19:48:50 +00:00
|
|
|
import ClientCode from "../../models/client_code";
|
|
|
|
import Mail from "../../models/mail";
|
|
|
|
import { randomBytes } from "crypto";
|
|
|
|
import moment = require("moment");
|
2020-03-09 14:03:26 +00:00
|
|
|
// import { JWTExpDur } from "../../keys";
|
2018-11-06 19:48:50 +00:00
|
|
|
import RefreshToken from "../../models/refresh_token";
|
2019-01-21 10:24:20 +00:00
|
|
|
import { getEncryptionKey } from "../../helper/user_key";
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2019-10-12 19:35:21 +00:00
|
|
|
// TODO:
|
|
|
|
/*
|
|
|
|
For example, the authorization server could employ refresh token
|
|
|
|
rotation in which a new refresh token is issued with every access
|
|
|
|
token refresh response. The previous refresh token is invalidated but retained by the authorization server. If a refresh token is
|
|
|
|
compromised and subsequently used by both the attacker and the
|
|
|
|
legitimate client, one of them will present an invalidated refresh
|
|
|
|
token, which will inform the authorization server of the breach.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const refreshTokenValidTime = moment.duration(6, "month");
|
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
const RefreshTokenRoute = Stacker(
|
|
|
|
GetClientAuthMiddleware(false, false, true),
|
|
|
|
async (req: Request, res: Response) => {
|
|
|
|
let grant_type = req.query.grant_type || req.body.grant_type;
|
|
|
|
if (!grant_type || grant_type === "authorization_code") {
|
|
|
|
let code = req.query.code || req.body.code;
|
|
|
|
let nonce = req.query.nonce || req.body.nonce;
|
2020-03-09 14:03:26 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
let c = await ClientCode.findOne({ code: code });
|
|
|
|
if (!c || moment(c.validTill).isBefore()) {
|
|
|
|
throw new RequestError(
|
|
|
|
req.__("Invalid code"),
|
|
|
|
HttpStatusCode.BAD_REQUEST
|
|
|
|
);
|
|
|
|
}
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
let client = await Client.findById(c.client);
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
let user = await User.findById(c.user);
|
|
|
|
let mails = await Promise.all(user.mails.map((m) => Mail.findOne(m)));
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
let token = RefreshToken.new({
|
|
|
|
user: c.user,
|
|
|
|
client: c.client,
|
|
|
|
permissions: c.permissions,
|
|
|
|
token: randomBytes(16).toString("hex"),
|
|
|
|
valid: true,
|
|
|
|
validTill: moment().add(refreshTokenValidTime).toDate(),
|
|
|
|
});
|
|
|
|
await RefreshToken.save(token);
|
|
|
|
await ClientCode.delete(c);
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
let mail = mails.find((e) => e.primary);
|
|
|
|
if (!mail) mail = mails[0];
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
res.json({
|
|
|
|
refresh_token: token.token,
|
|
|
|
token: token.token,
|
|
|
|
access_token: await getAccessTokenJWT({
|
|
|
|
client: client,
|
|
|
|
user: user,
|
|
|
|
permissions: c.permissions,
|
|
|
|
}),
|
|
|
|
token_type: "bearer",
|
|
|
|
expires_in: AccessTokenJWTExp.asSeconds(),
|
|
|
|
profile: {
|
|
|
|
uid: user.uid,
|
|
|
|
email: mail ? mail.mail : "",
|
|
|
|
name: user.name,
|
|
|
|
enc_key: getEncryptionKey(user, client),
|
|
|
|
},
|
|
|
|
id_token: getIDToken(user, client.client_id, nonce),
|
|
|
|
});
|
|
|
|
} else if (grant_type === "refresh_token") {
|
|
|
|
let refresh_token = req.query.refresh_token || req.body.refresh_token;
|
|
|
|
if (!refresh_token)
|
|
|
|
throw new RequestError(
|
|
|
|
req.__("refresh_token not set"),
|
|
|
|
HttpStatusCode.BAD_REQUEST
|
|
|
|
);
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
let token = await RefreshToken.findOne({ token: refresh_token });
|
|
|
|
if (!token || !token.valid || moment(token.validTill).isBefore())
|
|
|
|
throw new RequestError(
|
|
|
|
req.__("Invalid token"),
|
|
|
|
HttpStatusCode.BAD_REQUEST
|
|
|
|
);
|
2019-10-12 19:35:21 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
token.validTill = moment().add(refreshTokenValidTime).toDate();
|
|
|
|
await RefreshToken.save(token);
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
let user = await User.findById(token.user);
|
|
|
|
let client = await Client.findById(token.client);
|
|
|
|
let jwt = await getAccessTokenJWT({
|
|
|
|
user,
|
|
|
|
client,
|
|
|
|
permissions: token.permissions,
|
|
|
|
});
|
|
|
|
res.json({
|
|
|
|
access_token: jwt,
|
|
|
|
expires_in: AccessTokenJWTExp.asSeconds(),
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
throw new RequestError(
|
|
|
|
"invalid grant_type",
|
|
|
|
HttpStatusCode.BAD_REQUEST
|
|
|
|
);
|
|
|
|
}
|
2018-11-06 19:48:50 +00:00
|
|
|
}
|
2020-08-07 14:16:39 +00:00
|
|
|
);
|
2018-11-06 19:48:50 +00:00
|
|
|
|
2020-08-07 14:16:39 +00:00
|
|
|
export default RefreshTokenRoute;
|