This commit is contained in:
Fabian Stamm 2019-12-16 14:05:54 +01:00
commit 327a38db4b
4 changed files with 146 additions and 115 deletions

View File

@ -12,6 +12,8 @@ import { ObjectID } from "bson";
const AuthRoute = Stacker(GetUserMiddleware(true), async (req: Request, res: Response) => { const AuthRoute = Stacker(GetUserMiddleware(true), async (req: Request, res: Response) => {
let { response_type, client_id, redirect_uri, scope, state, nored } = req.query; let { response_type, client_id, redirect_uri, scope, state, nored } = req.query;
const sendError = (type) => { const sendError = (type) => {
if (redirect_uri === "$local")
redirect_uri = "/code";
res.redirect(redirect_uri += `?error=${type}&state=${state}`); res.redirect(redirect_uri += `?error=${type}&state=${state}`);
} }
/** /**
@ -63,7 +65,9 @@ const AuthRoute = Stacker(GetUserMiddleware(true), async (req: Request, res: Res
}); });
await ClientCode.save(code); await ClientCode.save(code);
let ruri = client.redirect_url + `?code=${code.code}&state=${state}`; let redir = client.redirect_url === "$local" ? "/code" : client.redirect_url;
let ruri = redir + `?code=${code.code}&state=${state}`;
if (nored === "true") { if (nored === "true") {
res.json({ res.json({
redirect_uri: ruri redirect_uri: ruri

View File

@ -13,12 +13,24 @@ import { JWTExpDur } from "../../keys";
import RefreshToken from "../../models/refresh_token"; import RefreshToken from "../../models/refresh_token";
import { getEncryptionKey } from "../../helper/user_key"; import { getEncryptionKey } from "../../helper/user_key";
// 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");
const RefreshTokenRoute = Stacker(GetClientAuthMiddleware(false, false, true), async (req: Request, res: Response) => { const RefreshTokenRoute = Stacker(GetClientAuthMiddleware(false, false, true), async (req: Request, res: Response) => {
let grant_type = req.query.grant_type || req.body.grant_type; let grant_type = req.query.grant_type || req.body.grant_type;
if (!grant_type || grant_type === "authorization_code") { if (!grant_type || grant_type === "authorization_code") {
let code = req.query.code || req.body.code; let code = req.query.code || req.body.code;
let c = await ClientCode.findOne({ code: code }) let c = await ClientCode.findOne({ code: code })
if (!c) { if (!c || moment(c.validTill).isBefore()) {
throw new RequestError(req.__("Invalid code"), HttpStatusCode.BAD_REQUEST); throw new RequestError(req.__("Invalid code"), HttpStatusCode.BAD_REQUEST);
} }
@ -33,7 +45,7 @@ const RefreshTokenRoute = Stacker(GetClientAuthMiddleware(false, false, true), a
permissions: c.permissions, permissions: c.permissions,
token: randomBytes(16).toString("hex"), token: randomBytes(16).toString("hex"),
valid: true, valid: true,
validTill: moment().add(6, "months").toDate() validTill: moment().add(refreshTokenValidTime).toDate()
}); });
await RefreshToken.save(token); await RefreshToken.save(token);
await ClientCode.delete(c); await ClientCode.delete(c);
@ -63,7 +75,11 @@ const RefreshTokenRoute = Stacker(GetClientAuthMiddleware(false, false, true), a
if (!refresh_token) throw new RequestError(req.__("refresh_token not set"), HttpStatusCode.BAD_REQUEST); if (!refresh_token) throw new RequestError(req.__("refresh_token not set"), HttpStatusCode.BAD_REQUEST);
let token = await RefreshToken.findOne({ token: refresh_token }); let token = await RefreshToken.findOne({ token: refresh_token });
if (!token) throw new RequestError(req.__("Invalid token"), HttpStatusCode.BAD_REQUEST); if (!token || !token.valid || moment(token.validTill).isBefore())
throw new RequestError(req.__("Invalid token"), HttpStatusCode.BAD_REQUEST);
token.validTill = moment().add(refreshTokenValidTime).toDate();
await RefreshToken.save(token);
let user = await User.findById(token.user); let user = await User.findById(token.user);
let client = await Client.findById(token.client) let client = await Client.findById(token.client)

View File

@ -34,6 +34,14 @@ ViewRouter.get("/login", (req, res) => {
res.send(GetLoginPage(req.__)) res.send(GetLoginPage(req.__))
}) })
ViewRouter.get("/code", (req, res) => {
res.setHeader("Cache-Control", "no-cache");
if (req.query.error)
res.send("Some error occured: " + req.query.error);
else
res.send(`Your code is: ${req.query.code}`);
})
ViewRouter.get("/admin", GetUserMiddleware(false, true), (req: Request, res, next) => { ViewRouter.get("/admin", GetUserMiddleware(false, true), (req: Request, res, next) => {
if (!req.isAdmin) res.sendStatus(HttpStatusCode.FORBIDDEN) if (!req.isAdmin) res.sendStatus(HttpStatusCode.FORBIDDEN)
else next() else next()

View File

@ -7,6 +7,9 @@ function submit() {
document.getElementById("cancel").onclick = () => { document.getElementById("cancel").onclick = () => {
let u = new URL(window.location); let u = new URL(window.location);
let uri = u.searchParams.get("redirect_uri"); let uri = u.searchParams.get("redirect_uri");
if (uri === "$local") {
uri = "/code";
}
window.location.href = uri + "?error=access_denied&state=" + u.searchParams.get("state"); window.location.href = uri + "?error=access_denied&state=" + u.searchParams.get("state");
} }