260 lines
9.3 KiB
TypeScript
260 lines
9.3 KiB
TypeScript
|
|
import * as plugins from './plugins.js';
|
||
|
|
import { LoginSession } from './classes.loginsession.js';
|
||
|
|
import { Reception } from './classes.reception.js';
|
||
|
|
|
||
|
|
export class LoginSessionManager {
|
||
|
|
// refs
|
||
|
|
public receptionRef: Reception;
|
||
|
|
public get db() {
|
||
|
|
return this.receptionRef.db.smartdataDb;
|
||
|
|
}
|
||
|
|
|
||
|
|
public CLoginSession = plugins.smartdata.setDefaultManagerForDoc(this, LoginSession);
|
||
|
|
|
||
|
|
public loginSessions = new plugins.lik.ObjectMap<LoginSession>();
|
||
|
|
|
||
|
|
public typedRouter = new plugins.typedrequest.TypedRouter();
|
||
|
|
|
||
|
|
public emailTokenMap = new plugins.lik.ObjectMap<{
|
||
|
|
email: string;
|
||
|
|
token: string;
|
||
|
|
action: 'emailLogin' | 'passwordReset';
|
||
|
|
}>();
|
||
|
|
|
||
|
|
constructor(receptionRefArg: Reception) {
|
||
|
|
this.receptionRef = receptionRefArg;
|
||
|
|
this.receptionRef.typedrouter.addTypedRouter(this.typedRouter);
|
||
|
|
this.typedRouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_LoginWithEmailOrUsernameAndPassword>(
|
||
|
|
'loginWithEmailOrUsernameAndPassword',
|
||
|
|
async (requestData) => {
|
||
|
|
let user = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
data: {
|
||
|
|
username: requestData.username,
|
||
|
|
passwordHash: await this.receptionRef.userManager.CUser.hashPassword(
|
||
|
|
requestData.password
|
||
|
|
),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!user && requestData.username.includes('@')) {
|
||
|
|
user = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
data: {
|
||
|
|
email: requestData.username,
|
||
|
|
passwordHash: await this.receptionRef.userManager.CUser.hashPassword(
|
||
|
|
requestData.password
|
||
|
|
),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
if (user) {
|
||
|
|
// lets recheck
|
||
|
|
if (
|
||
|
|
(user.data.username !== requestData.username &&
|
||
|
|
user.data.email !== requestData.username) ||
|
||
|
|
user.data.passwordHash !==
|
||
|
|
(await this.receptionRef.userManager.CUser.hashPassword(requestData.password))
|
||
|
|
) {
|
||
|
|
throw new Error(
|
||
|
|
'database returned a user that does not match wanted criterea. CRITICAL!'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const loginSession = await LoginSession.createLoginSessionForUser(user);
|
||
|
|
this.loginSessions.add(loginSession);
|
||
|
|
const refreshToken = await loginSession.getRefreshToken();
|
||
|
|
|
||
|
|
return {
|
||
|
|
status: 'ok',
|
||
|
|
refreshToken: refreshToken,
|
||
|
|
twoFaNeeded: false,
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('User not found!');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
this.typedRouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_LoginWithEmail>(
|
||
|
|
'loginWithEmail',
|
||
|
|
async (requestDataArg) => {
|
||
|
|
const existingUser = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
data: {
|
||
|
|
email: requestDataArg.email,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
if (existingUser) {
|
||
|
|
this.emailTokenMap.findOneAndRemoveSync(
|
||
|
|
(itemArg) => itemArg.email === existingUser.data.email
|
||
|
|
);
|
||
|
|
const loginEmailToken = plugins.smartunique.uuid4();
|
||
|
|
this.emailTokenMap.add({
|
||
|
|
email: existingUser.data.email,
|
||
|
|
token: loginEmailToken,
|
||
|
|
action: 'emailLogin',
|
||
|
|
});
|
||
|
|
// lets make sure its only valid for 10 minutes
|
||
|
|
plugins.smartdelay.delayFor(600000, null, true).then(() => {
|
||
|
|
this.emailTokenMap.findOneAndRemoveSync(
|
||
|
|
(itemArg) => itemArg.token === loginEmailToken
|
||
|
|
);
|
||
|
|
});
|
||
|
|
this.receptionRef.receptionMailer.sendLoginWithEMailMail(existingUser, loginEmailToken);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
status: 'ok',
|
||
|
|
testOnlyToken: process.env.TEST_MODE
|
||
|
|
? this.emailTokenMap.findSync((itemArg) => itemArg.email === existingUser.data.email)
|
||
|
|
.token
|
||
|
|
: null,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
this.typedRouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_LoginWithEmailAfterEmailTokenAquired>(
|
||
|
|
'loginWithEmailAfterEmailTokenAquired',
|
||
|
|
async (requestArg) => {
|
||
|
|
const tokenObject = this.emailTokenMap.findSync((itemArg) => {
|
||
|
|
return itemArg.email === requestArg.email && itemArg.token === requestArg.token;
|
||
|
|
});
|
||
|
|
if (tokenObject) {
|
||
|
|
const user = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
data: {
|
||
|
|
email: requestArg.email,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
const loginSession = await LoginSession.createLoginSessionForUser(user);
|
||
|
|
this.loginSessions.add(loginSession);
|
||
|
|
return {
|
||
|
|
refreshToken: await loginSession.getRefreshToken(),
|
||
|
|
};
|
||
|
|
} else {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('Validation Token not found');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
this.typedRouter.addTypedHandler<plugins.lointReception.request.ILogoutRequest>(
|
||
|
|
new plugins.typedrequest.TypedHandler('logout', async (requestDataArg) => {
|
||
|
|
const loginSession = await this.CLoginSession.getLoginSessionByRefreshToken(requestDataArg.refreshToken);
|
||
|
|
await loginSession.invalidate();
|
||
|
|
return {}
|
||
|
|
})
|
||
|
|
);
|
||
|
|
|
||
|
|
this.typedRouter.addTypedHandler<plugins.lointReception.request.IReq_ExchangeRefreshTokenAndTransferToken>(
|
||
|
|
new plugins.typedrequest.TypedHandler(
|
||
|
|
'exchangeRefreshTokenAndTransferToken',
|
||
|
|
async (requestDataArg) => {
|
||
|
|
switch (true) {
|
||
|
|
case !!requestDataArg.refreshToken:
|
||
|
|
const loginSession = await this.loginSessions.find(async (loginSessionArg) => {
|
||
|
|
return loginSessionArg.validateRefreshToken(requestDataArg.refreshToken);
|
||
|
|
});
|
||
|
|
if (!loginSession) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError('your refresh token is invalid');
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
transferToken: await loginSession.getTransferToken(),
|
||
|
|
};
|
||
|
|
break;
|
||
|
|
case !!requestDataArg.transferToken:
|
||
|
|
let transferToken: string;
|
||
|
|
const loginSession2 = await this.loginSessions.find(async (loginSessionArg) => {
|
||
|
|
return loginSessionArg.validateTransferToken(requestDataArg.transferToken);
|
||
|
|
});
|
||
|
|
if (!loginSession2) {
|
||
|
|
throw new plugins.typedrequest.TypedResponseError(
|
||
|
|
'Your transfer token is not valid.'
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
refreshToken: await loginSession2.getRefreshToken(),
|
||
|
|
};
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
this.typedRouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_ResetPassword>(
|
||
|
|
'resetPassword',
|
||
|
|
async (requestDataArg) => {
|
||
|
|
const emailOfPasswordToReset = requestDataArg.email;
|
||
|
|
const existingUser = await this.receptionRef.userManager.CUser.getInstance({
|
||
|
|
data: {
|
||
|
|
email: emailOfPasswordToReset,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
if (existingUser) {
|
||
|
|
this.emailTokenMap.findOneAndRemoveSync(
|
||
|
|
(itemArg) => itemArg.email === existingUser.data.email
|
||
|
|
);
|
||
|
|
this.emailTokenMap.add({
|
||
|
|
email: existingUser.data.email,
|
||
|
|
token: plugins.smartunique.shortId(),
|
||
|
|
action: 'passwordReset',
|
||
|
|
});
|
||
|
|
plugins.smartdelay.delayFor(600000, null, true).then(() => {
|
||
|
|
this.emailTokenMap.findOneAndRemoveSync(
|
||
|
|
(itemArg) => itemArg.email === existingUser.data.email
|
||
|
|
);
|
||
|
|
});
|
||
|
|
this.receptionRef.receptionMailer.sendPasswordResetMail(
|
||
|
|
existingUser,
|
||
|
|
this.emailTokenMap.findSync((itemArg) => itemArg.email === existingUser.data.email)
|
||
|
|
.token
|
||
|
|
);
|
||
|
|
}
|
||
|
|
// note: we always return ok here, since we don't want to give any indication as to wether a user is already registered with us.
|
||
|
|
return {
|
||
|
|
status: 'ok',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
this.typedRouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_SetNewPassword>(
|
||
|
|
'setNewPassword',
|
||
|
|
async (requestData) => {
|
||
|
|
return {
|
||
|
|
status: 'ok',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* returns a device id by simply returning a uuid4
|
||
|
|
*/
|
||
|
|
this.typedRouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_ObtainDeviceId>('obtainDeviceId', async (reqData) => {
|
||
|
|
reqData;
|
||
|
|
return {
|
||
|
|
deviceId: {
|
||
|
|
id: plugins.smartunique.uuid4()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
)
|
||
|
|
|
||
|
|
this.typedRouter.addTypedHandler(
|
||
|
|
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_AttachDeviceId>('attachDeviceId', async (reqData) => {
|
||
|
|
// TODO: Blocked by proper JWT handling
|
||
|
|
reqData.jwt;
|
||
|
|
return {
|
||
|
|
ok: false
|
||
|
|
}
|
||
|
|
})
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|