started adding video streaming support with cookies

This commit is contained in:
kyle hoell
2020-11-24 18:55:26 -05:00
parent 4660320794
commit df1a488e21
7 changed files with 249 additions and 48 deletions
+42 -16
View File
@@ -6,6 +6,7 @@ import S3Service from "../services/ChunkService/S3Service";
import {UserInterface} from "../models/user";
import tempStorage from "../tempStorage/tempStorage";
import sendShareEmail from "../utils/sendShareEmail";
import { createStreamVideoCookie } from "../cookies/createCookies";
const fileService = new FileService()
@@ -330,30 +331,55 @@ class FileController {
}
}
getDownloadTokenVideo = async(req: RequestTypeFullUser, res: Response) => {
getAccessTokenStreamVideo = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) return;
if (!req.user) {
return
}
try {
const user = req.user;
const cookie = req.headers.uuid as string;
const tempToken = await fileService.getDownloadTokenVideo(user, cookie);
res.send({tempToken});
const ipAddress = req.clientIp;
const streamVideoAccessToken = await user.generateAuthTokenStreamVideo(ipAddress);
createStreamVideoCookie(res, streamVideoAccessToken);
res.send();
} catch (e) {
const code = e.code || 500;
console.log(e);
res.status(code).send()
console.log("\nGet Access Token Stream Video Fle Route Error:", e.message);
const code = !e.code ? 500 : e.code >= 400 && e.code <= 599 ? e.code : 500;
res.status(code).send();
}
}
// getDownloadTokenVideo = async(req: RequestTypeFullUser, res: Response) => {
// if (!req.user) {
// return
// }
// try {
// const user = req.user;
// const cookie = req.headers.uuid as string;
// const tempToken = await fileService.getDownloadTokenVideo(user, cookie);
// res.send({tempToken});
// } catch (e) {
// const code = e.code || 500;
// console.log(e);
// res.status(code).send()
// }
// }
removeTempToken = async(req: RequestTypeFullUser, res: Response) => {
if (!req.user) {
+11
View File
@@ -2,6 +2,7 @@ import {Response } from "express";
const maxAgeAccess = 1000 * 5;//60 * 1000 * 20;
const maxAgeRefresh = 60 * 1000 * 60 * 24 * 30;
const maxAgeStreamVideo = 60 * 1000 * 60 * 24;
export const createLoginCookie = (res: Response, accessToken: string, refreshToken: string) => {
@@ -35,4 +36,14 @@ export const createLogoutCookie = (res: Response) => {
sameSite: "strict",
secure: process.env.NODE_ENV === "production"
})
}
export const createStreamVideoCookie = (res: Response, streamVideoAccessToken: string) => {
res.cookie("video-access-token", streamVideoAccessToken, {
httpOnly: true,
maxAge: maxAgeStreamVideo,
sameSite: "strict",
secure: process.env.NODE_ENV === "production"
})
}
+13 -4
View File
@@ -7,6 +7,7 @@ import FileSystemService from "../services/ChunkService/FileSystemService";
import S3Service from "../services/ChunkService/S3Service";
import ChunkInterface from "../services/ChunkService/utils/ChunkInterface";
import authFullUser from "../middleware/authFullUser";
import authStreamVideo from "../middleware/authStreamVideo";
let fileController: FileController;
let chunkService: ChunkInterface;
@@ -48,11 +49,17 @@ router.get("/file-service/quick-list", auth, fileController.getQuickList);
router.get("/file-service/list", auth, fileController.getList);
router.get("/file-service/download/get-token", authFullUser, fileController.getDownloadToken);
//router.get("/file-service/download/get-token", authFullUser, fileController.getDownloadToken);
router.get("/file-service/download/get-token-video", auth, fileController.getDownloadTokenVideo);
//router.get("/file-service/download/get-token-video", auth, fileController.getDownloadTokenVideo);
router.get("/file-service/stream-video/:id/:tempToken/:uuid", auth, fileController.streamVideo);
router.get("/file-service/download/access-token-stream-video", authFullUser, fileController.getAccessTokenStreamVideo)
router.get("/file-service/stream-video/:id", authStreamVideo, fileController.streamVideo);
//router.get("/file-service/stream-video/:id/:tempToken/:uuid", auth, fileController.streamVideo);
//router.get("/file-service/stream-video/:id", auth, fileController.streamVideo);
router.get("/file-service/download/:id", authFullUser, fileController.downloadFile);
@@ -68,7 +75,9 @@ router.patch("/file-service/move", auth, fileController.moveFile);
router.delete("/file-service/remove-link/:id", auth, fileController.removeLink);
router.delete("/file-service/remove/token-video/:tempToken/:uuid", auth, fileController.removeTempToken);
//router.delete("/file-service/remove/token-video/:tempToken/:uuid", auth, fileController.removeTempToken);
router.delete("/file-service/remove/token-video/:id", auth, fileController.removeTempToken);
router.delete("/file-service/remove", auth, fileController.deleteFile);
+96
View File
@@ -0,0 +1,96 @@
import jwt from "jsonwebtoken";
import User, {UserInterface} from "../models/user";
import env from "../enviroment/env";
import {Request, Response, NextFunction} from "express";
import { ObjectID } from "mongodb";
interface RequestType extends Request {
user?: UserInterface,
token?: string,
encryptedToken?: string,
}
// type jwtType = {
// iv: Buffer,
// user: userAccessType
// }
type jwtType = {
iv: Buffer,
_id: string,
time: number
}
type userAccessType = {
_id: string,
emailVerified: boolean,
email: string,
admin: boolean,
botChecked: boolean,
username: string,
}
const removeOldTokens = async(userID: string, ipAddress: string | undefined, oldTime: number) => {
try {
const minusTime = oldTime - (60 * 1000 * 60 * 24);
ipAddress = ipAddress ? ipAddress : "";
if (ipAddress === "") return;
await User.updateOne({_id: userID}, {$pull: {tempTokens: {ipAddress, time: {$lt: minusTime}}}})
} catch (e) {
console.log("cannot remove old tokens", e);
}
}
const authStreamVideo = async(req: RequestType, res: Response, next: NextFunction) => {
try {
const accessTokenStreamVideo = req.cookies["video-access-token"];
if (!accessTokenStreamVideo) throw new Error("No Stream Video Access Token");
const decoded = jwt.verify(accessTokenStreamVideo, env.passwordAccess!) as jwtType;
const time = decoded.time;
const user = await User.findById(new ObjectID(decoded._id));
if (!user) throw new Error("No User Stream Video");
const encrpytionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(accessTokenStreamVideo, encrpytionKey, decoded.iv);
let tokenFound = false;
for (let i = 0; i < user.tempTokens.length; i++) {
const currentEncryptedToken = user.tempTokens[i].token;
if (currentEncryptedToken === encryptedToken) {
tokenFound = true;
removeOldTokens(user._id, req.clientIp, time);
break;
}
}
if (!tokenFound) throw new Error("Refresh Token Not Found");
req.user = user;
next();
} catch (e) {
console.log("\nAuthorization Middleware Error:", e.message);
res.status(401).send("Error Authenticating");
}
}
export default authStreamVideo;
+34 -1
View File
@@ -51,6 +51,14 @@ const userSchema = new mongoose.Schema({
token: {
type: String,
required: true
},
ipAddress: {
type: String,
required: true,
},
time: {
type: Number,
required: true
}
}],
privateKey: {
@@ -187,11 +195,12 @@ export interface UserInterface extends Document {
getEncryptionKey: () => Buffer | undefined;
generateTempAuthToken: () => any;
generateTempAuthTokenVideo: (cookie: string) => any;
// generateTempAuthTokenVideo: (cookie: string) => any;
encryptToken: (tempToken: any, key: any, publicKey: any) => any;
decryptToken: (encryptedToken: any, key: any, publicKey: any) => any;
findByCreds: (email: string, password: string) => UserInterface;
generateAuthToken: (ipAddress: string | undefined) => Promise<{accessToken: string, refreshToken: string}>
generateAuthTokenStreamVideo: (ipAddress: string | undefined) => Promise<string>
generateEncryptionKeys: () => Promise<void>;
changeEncryptionKey: (randomKey: Buffer) => void;
generateEmailVerifyToken: () => string;
@@ -211,6 +220,8 @@ export interface UserInterface extends Document {
const maxAgeAccess = 1000 * 5 * 10; //60 * 1000 * 20 + (1000 * 60);
const maxAgeRefresh = 60 * 1000 * 60 * 24 * 30 + (1000 * 60);
const maxAgeAccessStreamVideo = 60 * 1000 * 60 * 24;
userSchema.pre("save", async function(this: any, next: any) {
const user = this;
@@ -254,6 +265,28 @@ userSchema.methods.toJSON = function() {
return userObject;
}
userSchema.methods.generateAuthTokenStreamVideo = async function(ipAddress: string | undefined) {
const iv = crypto.randomBytes(16);
const user = this;
const date = new Date();
const time = date.getTime();
let accessTokenStreamVideo = jwt.sign({_id:user._id.toString(), iv, time}, env.passwordAccess!, {expiresIn: maxAgeAccessStreamVideo.toString()});
const encryptionKey = user.getEncryptionKey();
const encryptedToken = user.encryptToken(accessTokenStreamVideo, encryptionKey, iv);
ipAddress = ipAddress ? ipAddress : "";
await User.updateOne({_id: user._id}, {$push: {"tempTokens": {token: encryptedToken, ipAddress, time}}});
return accessTokenStreamVideo;
}
userSchema.methods.generateAuthToken = async function(ipAddress: string | undefined) {
const iv = crypto.randomBytes(16);
+13 -9
View File
@@ -161,17 +161,21 @@ class MongoFileService {
return tempToken;
}
getDownloadTokenVideo = async(user: UserInterface, cookie: string) => {
if (!cookie) throw new NotAuthorizedError("Get Download Token Video Cookie Not Authorized Error");
const tempToken = await user.generateTempAuthTokenVideo(cookie);
if (!tempToken) throw new NotAuthorizedError("Get Download Token Video Not Authorized Error");
return tempToken;
getAccessTokenStreamVideo = async() => {
}
// getDownloadTokenVideo = async(user: UserInterface, cookie: string) => {
// if (!cookie) throw new NotAuthorizedError("Get Download Token Video Cookie Not Authorized Error");
// const tempToken = await user.generateTempAuthTokenVideo(cookie);
// if (!tempToken) throw new NotAuthorizedError("Get Download Token Video Not Authorized Error");
// return tempToken;
// }
removeTempToken = async(user: UserInterface, tempToken: any, currentUUID: string) => {
const key = user.getEncryptionKey();
+40 -18
View File
@@ -5,6 +5,7 @@ import env from "../../enviroment/envFrontEnd";
import {connect} from "react-redux";
import React from "react";
import { setPhotoID } from "../../actions/photoViewer";
import axiosNonInterceptor from "axios";
const currentURL = env.url;
@@ -94,36 +95,57 @@ class PopupWindowContainer extends React.Component {
getVideo = () => {
const config = {
headers: {
uuid: window.sessionStorage.getItem("uuid")
}
// const config = {
// headers: {
// uuid: window.sessionStorage.getItem("uuid")
// }
};
// };
axios.get("/file-service/download/access-token-stream-video").then(() => {
axios.get(currentURL +'/file-service/download/get-token-video',config)
.then((response) => {
this.tempToken = response.data.tempToken;
const uuidID = window.sessionStorage.getItem("uuid");
console.log("stream video got token")
const isDrive = this.props.popupFile.metadata.drive;
const isPersonal = this.props.popupFile.metadata.personalFile;
const finalUrl = isDrive ?
currentURL + `/file-service-google/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
: !isPersonal ? currentURL + `/file-service/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
: currentURL + `/file-service-personal/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
currentURL + `/file-service-google/stream-video/${this.props.popupFile._id}`
: !isPersonal ? currentURL + `/file-service/stream-video/${this.props.popupFile._id}`
: currentURL + `/file-service-personal/stream-video/${this.props.popupFile._id}`
this.setState(() => ({
...this.state,
video: finalUrl
}))
}).catch((err) => {
console.log(err)
}).catch((e) => {
console.log("Stream Video Error", e.message);
})
// axios.get(currentURL +'/file-service/download/get-token-video',config)
// .then((response) => {
// this.tempToken = response.data.tempToken;
// const uuidID = window.sessionStorage.getItem("uuid");
// const isDrive = this.props.popupFile.metadata.drive;
// const isPersonal = this.props.popupFile.metadata.personalFile;
// const finalUrl = isDrive ?
// currentURL + `/file-service-google/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// : !isPersonal ? currentURL + `/file-service/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// : currentURL + `/file-service-personal/stream-video/${this.props.popupFile._id}/${this.tempToken}/${uuidID}`
// this.setState(() => ({
// ...this.state,
// video: finalUrl
// }))
// }).catch((err) => {
// console.log(err)
// })
}
componentWillUnmount = () => {