mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 20:27:43 +00:00
feat: reverted lockout in login completely
This commit is contained in:
@@ -45,10 +45,10 @@ type TIdentityUaServiceFactoryDep = {
|
|||||||
|
|
||||||
export type TIdentityUaServiceFactory = ReturnType<typeof identityUaServiceFactory>;
|
export type TIdentityUaServiceFactory = ReturnType<typeof identityUaServiceFactory>;
|
||||||
|
|
||||||
type LockoutObject = {
|
// type LockoutObject = {
|
||||||
lockedOut: boolean;
|
// lockedOut: boolean;
|
||||||
failedAttempts: number;
|
// failedAttempts: number;
|
||||||
};
|
// };
|
||||||
|
|
||||||
export const identityUaServiceFactory = ({
|
export const identityUaServiceFactory = ({
|
||||||
identityUaDAL,
|
identityUaDAL,
|
||||||
@@ -62,8 +62,15 @@ export const identityUaServiceFactory = ({
|
|||||||
const login = async (clientId: string, clientSecret: string, ip: string) => {
|
const login = async (clientId: string, clientSecret: string, ip: string) => {
|
||||||
const identityUa = await identityUaDAL.findOne({ clientId });
|
const identityUa = await identityUaDAL.findOne({ clientId });
|
||||||
if (!identityUa) {
|
if (!identityUa) {
|
||||||
throw new UnauthorizedError({
|
throw new NotFoundError({
|
||||||
message: "Invalid credentials"
|
message: "No identity with specified client ID was found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityUa.identityId });
|
||||||
|
if (!identityMembershipOrg) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: "No identity with the org membership was found"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,42 +78,15 @@ export const identityUaServiceFactory = ({
|
|||||||
ipAddress: ip,
|
ipAddress: ip,
|
||||||
trustedIps: identityUa.clientSecretTrustedIps as TIp[]
|
trustedIps: identityUa.clientSecretTrustedIps as TIp[]
|
||||||
});
|
});
|
||||||
|
|
||||||
const LOCKOUT_KEY = `lockout:identity:${identityUa.identityId}:${IdentityAuthMethod.UNIVERSAL_AUTH}:${clientId}`;
|
|
||||||
|
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityUa.identityId });
|
|
||||||
if (!identityMembershipOrg) {
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "Invalid credentials"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const identityTx = await identityUaDAL.transaction(async (tx) => {
|
|
||||||
// await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.IdentityLogin(identityUa.identityId, clientId)]);
|
|
||||||
|
|
||||||
// Lockout Check
|
|
||||||
const lockoutRaw = await keyStore.getItem(LOCKOUT_KEY);
|
|
||||||
|
|
||||||
let lockout: LockoutObject | undefined;
|
|
||||||
if (lockoutRaw) {
|
|
||||||
lockout = JSON.parse(lockoutRaw) as LockoutObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lockout && lockout.lockedOut) {
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "This identity auth method is temporarily locked, please try again later"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientSecretPrefix = clientSecret.slice(0, 4);
|
const clientSecretPrefix = clientSecret.slice(0, 4);
|
||||||
const clientSecretInfo = await identityUaClientSecretDAL.find({
|
const clientSecrtInfo = await identityUaClientSecretDAL.find({
|
||||||
identityUAId: identityUa.id,
|
identityUAId: identityUa.id,
|
||||||
isClientSecretRevoked: false,
|
isClientSecretRevoked: false,
|
||||||
clientSecretPrefix
|
clientSecretPrefix
|
||||||
});
|
});
|
||||||
|
|
||||||
let validClientSecretInfo: (typeof clientSecretInfo)[0] | null = null;
|
let validClientSecretInfo: (typeof clientSecrtInfo)[0] | null = null;
|
||||||
for await (const info of clientSecretInfo) {
|
for await (const info of clientSecrtInfo) {
|
||||||
const isMatch = await crypto.hashing().compareHash(clientSecret, info.clientSecretHash);
|
const isMatch = await crypto.hashing().compareHash(clientSecret, info.clientSecretHash);
|
||||||
|
|
||||||
if (isMatch) {
|
if (isMatch) {
|
||||||
@@ -115,31 +95,7 @@ export const identityUaServiceFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validClientSecretInfo) {
|
if (!validClientSecretInfo) throw new UnauthorizedError({ message: "Invalid credentials" });
|
||||||
if (identityUa.lockoutEnabled) {
|
|
||||||
if (!lockout) {
|
|
||||||
lockout = {
|
|
||||||
lockedOut: false,
|
|
||||||
failedAttempts: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
lockout.failedAttempts += 1;
|
|
||||||
if (lockout.failedAttempts >= identityUa.lockoutThreshold) {
|
|
||||||
lockout.lockedOut = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await keyStore.setItemWithExpiry(
|
|
||||||
LOCKOUT_KEY,
|
|
||||||
lockout.lockedOut ? identityUa.lockoutDurationSeconds : identityUa.lockoutCounterResetSeconds,
|
|
||||||
JSON.stringify(lockout)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UnauthorizedError({ message: "Invalid credentials" });
|
|
||||||
} else if (lockout) {
|
|
||||||
await keyStore.deleteItem(LOCKOUT_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
|
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
|
||||||
if (Number(clientSecretTTL) > 0) {
|
if (Number(clientSecretTTL) > 0) {
|
||||||
@@ -183,7 +139,8 @@ export const identityUaServiceFactory = ({
|
|||||||
accessTokenMaxTTL: 1000000000
|
accessTokenMaxTTL: 1000000000
|
||||||
};
|
};
|
||||||
|
|
||||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo.id, tx);
|
const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
|
||||||
|
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx);
|
||||||
await identityOrgMembershipDAL.updateById(
|
await identityOrgMembershipDAL.updateById(
|
||||||
identityMembershipOrg.id,
|
identityMembershipOrg.id,
|
||||||
{
|
{
|
||||||
@@ -206,33 +163,33 @@ export const identityUaServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
return { newToken, validClientSecretInfo, accessTokenTTLParams };
|
return newToken;
|
||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const accessToken = crypto.jwt().sign(
|
const accessToken = crypto.jwt().sign(
|
||||||
{
|
{
|
||||||
identityId: identityUa.identityId,
|
identityId: identityUa.identityId,
|
||||||
clientSecretId: identityTx.validClientSecretInfo.id,
|
clientSecretId: validClientSecretInfo.id,
|
||||||
identityAccessTokenId: identityTx.newToken.id,
|
identityAccessTokenId: identityAccessToken.id,
|
||||||
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
|
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
|
||||||
} as TIdentityAccessTokenJwtPayload,
|
} as TIdentityAccessTokenJwtPayload,
|
||||||
appCfg.AUTH_SECRET,
|
appCfg.AUTH_SECRET,
|
||||||
// akhilmhdh: for non-expiry tokens you should not even set the value, including undefined. Even for undefined jsonwebtoken throws error
|
// akhilmhdh: for non-expiry tokens you should not even set the value, including undefined. Even for undefined jsonwebtoken throws error
|
||||||
Number(identityTx.newToken.accessTokenTTL) === 0
|
Number(identityAccessToken.accessTokenTTL) === 0
|
||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
expiresIn: Number(identityTx.newToken.accessTokenTTL)
|
expiresIn: Number(identityAccessToken.accessTokenTTL)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessToken,
|
accessToken,
|
||||||
identityUa,
|
identityUa,
|
||||||
validClientSecretInfo: identityTx.validClientSecretInfo,
|
validClientSecretInfo,
|
||||||
identityAccessToken: identityTx.newToken,
|
identityAccessToken,
|
||||||
identityMembershipOrg,
|
identityMembershipOrg,
|
||||||
...identityTx.accessTokenTTLParams
|
...accessTokenTTLParams
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user