feat: added last login time

This commit is contained in:
=
2025-08-07 13:37:06 +05:30
parent be20a507ac
commit eebf080e3c
29 changed files with 156 additions and 63 deletions

View File

@@ -3,39 +3,63 @@ import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const lastUserLoggedInAuthMethod = await knex.schema.hasColumn(TableName.OrgMembership, "lastLoggedInAuthMethod");
const lastUserLoggedInAuthMethod = await knex.schema.hasColumn(TableName.OrgMembership, "lastLoginAuthMethod");
const lastIdentityLoggedInAuthMethod = await knex.schema.hasColumn(
TableName.IdentityOrgMembership,
"lastLoggedInAuthMethod"
"lastLoginAuthMethod"
);
if (!lastUserLoggedInAuthMethod) {
const lastUserLoggedInTime = await knex.schema.hasColumn(TableName.OrgMembership, "lastLoginTime");
const lastIdentityLoggedInTime = await knex.schema.hasColumn(TableName.IdentityOrgMembership, "lastLoginTime");
if (!lastUserLoggedInAuthMethod || !lastUserLoggedInTime) {
await knex.schema.alterTable(TableName.OrgMembership, (t) => {
t.string("lastLoggedInAuthMethod").nullable();
if (!lastUserLoggedInAuthMethod) {
t.string("lastLoginAuthMethod").nullable();
}
if (!lastUserLoggedInTime) {
t.datetime("lastLoginTime").nullable();
}
});
}
if (!lastIdentityLoggedInAuthMethod) {
if (!lastIdentityLoggedInAuthMethod || !lastIdentityLoggedInTime) {
await knex.schema.alterTable(TableName.IdentityOrgMembership, (t) => {
t.string("lastLoggedInAuthMethod").nullable();
if (!lastIdentityLoggedInAuthMethod) {
t.string("lastLoginAuthMethod").nullable();
}
if (!lastIdentityLoggedInTime) {
t.datetime("lastLoginTime").nullable();
}
});
}
}
export async function down(knex: Knex): Promise<void> {
const lastUserLoggedInAuthMethod = await knex.schema.hasColumn(TableName.OrgMembership, "lastLoggedInAuthMethod");
const lastUserLoggedInAuthMethod = await knex.schema.hasColumn(TableName.OrgMembership, "lastLoginAuthMethod");
const lastIdentityLoggedInAuthMethod = await knex.schema.hasColumn(
TableName.IdentityOrgMembership,
"lastLoggedInAuthMethod"
"lastLoginAuthMethod"
);
if (lastUserLoggedInAuthMethod) {
const lastUserLoggedInTime = await knex.schema.hasColumn(TableName.OrgMembership, "lastLoginTime");
const lastIdentityLoggedInTime = await knex.schema.hasColumn(TableName.IdentityOrgMembership, "lastLoginTime");
if (lastUserLoggedInAuthMethod || lastUserLoggedInTime) {
await knex.schema.alterTable(TableName.OrgMembership, (t) => {
t.dropColumn("lastLoggedInAuthMethod");
if (lastUserLoggedInAuthMethod) {
t.dropColumn("lastLoginAuthMethod");
}
if (lastUserLoggedInTime) {
t.dropColumn("lastLoginTime");
}
});
}
if (lastIdentityLoggedInAuthMethod) {
if (lastIdentityLoggedInAuthMethod || lastIdentityLoggedInTime) {
await knex.schema.alterTable(TableName.IdentityOrgMembership, (t) => {
t.dropColumn("lastLoggedInAuthMethod");
if (lastIdentityLoggedInAuthMethod) {
t.dropColumn("lastLoginAuthMethod");
}
if (lastIdentityLoggedInTime) {
t.dropColumn("lastLoginTime");
}
});
}
}

View File

@@ -15,7 +15,8 @@ export const IdentityOrgMembershipsSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
identityId: z.string().uuid(),
lastLoggedInAuthMethod: z.string().nullable().optional()
lastLoginAuthMethod: z.string().nullable().optional(),
lastLoginTime: z.date().nullable().optional()
});
export type TIdentityOrgMemberships = z.infer<typeof IdentityOrgMembershipsSchema>;

View File

@@ -20,7 +20,8 @@ export const OrgMembershipsSchema = z.object({
projectFavorites: z.string().array().nullable().optional(),
isActive: z.boolean().default(true),
lastInvitedAt: z.date().nullable().optional(),
lastLoggedInAuthMethod: z.string().nullable().optional()
lastLoginAuthMethod: z.string().nullable().optional(),
lastLoginTime: z.date().nullable().optional()
});
export type TOrgMemberships = z.infer<typeof OrgMembershipsSchema>;

View File

@@ -45,6 +45,8 @@ import { groupServiceFactory } from "@app/ee/services/group/group-service";
import { userGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
import { HsmModule } from "@app/ee/services/hsm/hsm-types";
import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal";
import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service";
import { identityProjectAdditionalPrivilegeDALFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-dal";
import { identityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
import { identityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service";
@@ -179,8 +181,6 @@ import { identityAccessTokenDALFactory } from "@app/services/identity-access-tok
import { identityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
import { identityAliCloudAuthDALFactory } from "@app/services/identity-alicloud-auth/identity-alicloud-auth-dal";
import { identityAliCloudAuthServiceFactory } from "@app/services/identity-alicloud-auth/identity-alicloud-auth-service";
import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal";
import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service";
import { identityAwsAuthDALFactory } from "@app/services/identity-aws-auth/identity-aws-auth-dal";
import { identityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
import { identityAzureAuthDALFactory } from "@app/services/identity-azure-auth/identity-azure-auth-dal";

View File

@@ -149,7 +149,10 @@ export const authLoginServiceFactory = ({
if (organizationId) {
const org = await orgDAL.findById(organizationId);
if (org) {
await orgMembershipDAL.update({ userId: user.id, orgId: org.id }, { lastLoggedInAuthMethod: authMethod });
await orgMembershipDAL.update(
{ userId: user.id, orgId: org.id },
{ lastLoginAuthMethod: authMethod, lastLoginTime: new Date() }
);
if (org.userTokenExpiration) {
tokenSessionExpiresIn = getMinExpiresIn(cfg.JWT_AUTH_LIFETIME, org.userTokenExpiration);
refreshTokenExpiresIn = org.userTokenExpiration;

View File

@@ -32,8 +32,8 @@ import {
keyAlgorithmToAlgCfg
} from "../certificate-authority-fns";
import { TCertificateAuthoritySecretDALFactory } from "../certificate-authority-secret-dal";
import { TIssueCertWithTemplateDTO } from "./internal-certificate-authority-types";
import { validateAndMapAltNameType } from "../certificate-authority-validators";
import { TIssueCertWithTemplateDTO } from "./internal-certificate-authority-types";
type TInternalCertificateAuthorityFnsDeps = {
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findByIdWithAssociatedCa" | "findById">;

View File

@@ -52,6 +52,7 @@ import {
} from "../certificate-authority-fns";
import { TCertificateAuthorityQueueFactory } from "../certificate-authority-queue";
import { TCertificateAuthoritySecretDALFactory } from "../certificate-authority-secret-dal";
import { validateAndMapAltNameType } from "../certificate-authority-validators";
import { TInternalCertificateAuthorityDALFactory } from "./internal-certificate-authority-dal";
import {
TCreateCaDTO,
@@ -68,7 +69,6 @@ import {
TSignIntermediateDTO,
TUpdateCaDTO
} from "./internal-certificate-authority-types";
import { validateAndMapAltNameType } from "../certificate-authority-validators";
type TInternalCertificateAuthorityServiceFactoryDep = {
certificateAuthorityDAL: Pick<

View File

@@ -64,6 +64,8 @@ export const identityAliCloudAuthServiceFactory = ({
identityId: identityAliCloudAuth.identityId
});
if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" });
const requestUrl = new URL("https://sts.aliyuncs.com");
for (const key of Object.keys(params)) {
@@ -90,7 +92,8 @@ export const identityAliCloudAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.ALICLOUD_AUTH
lastLoginAuthMethod: IdentityAuthMethod.ALICLOUD_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -91,6 +91,7 @@ export const identityAwsAuthServiceFactory = ({
}
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityAwsAuth.identityId });
if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" });
const headers: TAwsGetCallerIdentityHeaders = JSON.parse(Buffer.from(iamRequestHeaders, "base64").toString());
const body: string = Buffer.from(iamRequestBody, "base64").toString();
@@ -155,7 +156,8 @@ export const identityAwsAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.AWS_AUTH
lastLoginAuthMethod: IdentityAuthMethod.AWS_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -83,7 +83,8 @@ export const identityAzureAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.AZURE_AUTH
lastLoginAuthMethod: IdentityAuthMethod.AZURE_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -122,7 +122,8 @@ export const identityGcpAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.GCP_AUTH
lastLoginAuthMethod: IdentityAuthMethod.GCP_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -212,7 +212,8 @@ export const identityJwtAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.JWT_AUTH
lastLoginAuthMethod: IdentityAuthMethod.JWT_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -383,7 +383,8 @@ export const identityKubernetesAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH
lastLoginAuthMethod: IdentityAuthMethod.KUBERNETES_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -147,7 +147,8 @@ export const identityLdapAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.LDAP_AUTH
lastLoginAuthMethod: IdentityAuthMethod.LDAP_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -57,6 +57,7 @@ export const identityOciAuthServiceFactory = ({
}
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: identityOciAuth.identityId });
if (!identityMembershipOrg) throw new UnauthorizedError({ message: "Identity not attached to a organization" });
// Validate OCI host format. Ensures that the host is in "identity.<region>.oraclecloud.com" format.
if (!headers.host || !new RE2("^identity\\.([a-z]{2}-[a-z]+-[1-9])\\.oraclecloud\\.com$").test(headers.host)) {
@@ -94,7 +95,8 @@ export const identityOciAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.OCI_AUTH
lastLoginAuthMethod: IdentityAuthMethod.OCI_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -181,7 +181,8 @@ export const identityOidcAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.OIDC_AUTH
lastLoginAuthMethod: IdentityAuthMethod.OIDC_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -121,7 +121,8 @@ export const identityTlsCertAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH
lastLoginAuthMethod: IdentityAuthMethod.TLS_CERT_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -348,7 +348,8 @@ export const identityTokenAuthServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.TOKEN_AUTH
lastLoginAuthMethod: IdentityAuthMethod.TOKEN_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -135,7 +135,8 @@ export const identityUaServiceFactory = ({
await identityOrgMembershipDAL.updateById(
identityMembershipOrg.id,
{
lastLoggedInAuthMethod: IdentityAuthMethod.UNIVERSAL_AUTH
lastLoginAuthMethod: IdentityAuthMethod.UNIVERSAL_AUTH,
lastLoginTime: new Date()
},
tx
);

View File

@@ -254,7 +254,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
db.ref("role").withSchema("paginatedIdentity"),
db.ref("roleId").withSchema("paginatedIdentity"),
db.ref("orgId").withSchema("paginatedIdentity"),
db.ref("lastLoggedInAuthMethod").withSchema("paginatedIdentity"),
db.ref("lastLoginAuthMethod").withSchema("paginatedIdentity"),
db.ref("lastLoginTime").withSchema("paginatedIdentity"),
db.ref("createdAt").withSchema("paginatedIdentity"),
db.ref("updatedAt").withSchema("paginatedIdentity"),
db.ref("identityId").withSchema("paginatedIdentity").as("identityId"),
@@ -321,7 +322,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
tlsCertId,
createdAt,
updatedAt,
lastLoggedInAuthMethod
lastLoginAuthMethod,
lastLoginTime
}) => ({
role,
roleId,
@@ -330,7 +332,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
orgId,
createdAt,
updatedAt,
lastLoggedInAuthMethod,
lastLoginAuthMethod,
lastLoginTime,
customRole: roleId
? {
id: crId,
@@ -500,7 +503,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
db.ref("orgId").withSchema(TableName.IdentityOrgMembership),
db.ref("createdAt").withSchema(TableName.IdentityOrgMembership),
db.ref("updatedAt").withSchema(TableName.IdentityOrgMembership),
db.ref("lastLoggedInAuthMethod").withSchema(TableName.IdentityOrgMembership),
db.ref("lastLoginAuthMethod").withSchema(TableName.IdentityOrgMembership),
db.ref("lastLoginTime").withSchema(TableName.IdentityOrgMembership),
db.ref("identityId").withSchema(TableName.IdentityOrgMembership).as("identityId"),
db.ref("name").withSchema(TableName.Identity).as("identityName"),
db.ref("hasDeleteProtection").withSchema(TableName.Identity),
@@ -535,10 +539,10 @@ export const identityOrgDALFactory = (db: TDbClient) => {
} else if (orderBy === OrgIdentityOrderBy.Role) {
void query.orderByRaw(
`
CASE
WHEN ??.role = ?
THEN ??.slug
ELSE ??.role
CASE
WHEN ??.role = ?
THEN ??.slug
ELSE ??.role
END ?
`,
[
@@ -581,7 +585,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
ldapId,
createdAt,
updatedAt,
lastLoggedInAuthMethod
lastLoginTime,
lastLoginAuthMethod
}) => ({
role,
roleId,
@@ -591,7 +596,8 @@ export const identityOrgDALFactory = (db: TDbClient) => {
orgId,
createdAt,
updatedAt,
lastLoggedInAuthMethod,
lastLoginTime,
lastLoginAuthMethod,
customRole: roleId
? {
id: crId,

View File

@@ -32,7 +32,8 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
db.ref("roleId").withSchema(TableName.OrgMembership),
db.ref("status").withSchema(TableName.OrgMembership),
db.ref("isActive").withSchema(TableName.OrgMembership),
db.ref("lastLoggedInAuthMethod").withSchema(TableName.OrgMembership),
db.ref("lastLoginAuthMethod").withSchema(TableName.OrgMembership),
db.ref("lastLoginTime").withSchema(TableName.OrgMembership),
db.ref("email").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users),
db.ref("firstName").withSchema(TableName.Users),
@@ -66,7 +67,8 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
status,
isActive,
inviteEmail,
lastLoggedInAuthMethod
lastLoginAuthMethod,
lastLoginTime
}) => ({
roleId,
orgId,
@@ -75,7 +77,8 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
status,
isActive,
inviteEmail,
lastLoggedInAuthMethod,
lastLoginAuthMethod,
lastLoginTime,
user: {
id: userId,
email,

View File

@@ -285,7 +285,8 @@ export const orgDALFactory = (db: TDbClient) => {
db.ref("roleId").withSchema(TableName.OrgMembership),
db.ref("status").withSchema(TableName.OrgMembership),
db.ref("isActive").withSchema(TableName.OrgMembership),
db.ref("lastLoggedInAuthMethod").withSchema(TableName.OrgMembership),
db.ref("lastLoginAuthMethod").withSchema(TableName.OrgMembership),
db.ref("lastLoginTime").withSchema(TableName.OrgMembership),
db.ref("email").withSchema(TableName.Users),
db.ref("isEmailVerified").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users),

View File

@@ -1,22 +1,33 @@
import { faShield } from "@fortawesome/free-solid-svg-icons";
import { faShield, faClock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
type Props = {
lastLoggedInAuthMethod: string;
lastLoginAuthMethod: string;
lastLoginTime: string;
};
export const LastLoginSection = ({ lastLoggedInAuthMethod }: Props) => (
export const LastLoginSection = ({ lastLoginTime, lastLoginAuthMethod }: Props) => (
<div>
<div className="mb-2 flex items-center gap-2 border-b border-mineshaft-600 pb-1">
<div className="font-medium">Last Login Details</div>
<div className="font-medium">Last Login</div>
</div>
<div className="flex items-center gap-2 text-sm">
<div className="mb-2 flex items-center gap-2 text-sm">
<div className="rounded bg-mineshaft-700 p-1 px-2">
<FontAwesomeIcon icon={faShield} />
</div>
<div className="flex flex-col">
<div className="text-sm font-medium">Authentication Method</div>
<div className="text-sm">{lastLoggedInAuthMethod}</div>
<div className="text-sm">{lastLoginAuthMethod}</div>
</div>
</div>
<div className="flex items-center gap-2 text-sm">
<div className="rounded bg-mineshaft-700 p-1 px-2">
<FontAwesomeIcon icon={faClock} />
</div>
<div className="flex flex-col">
<div className="text-sm font-medium">Time</div>
<div className="text-sm">{format(lastLoginTime, "PPpp")} </div>
</div>
</div>
</div>

View File

@@ -41,7 +41,8 @@ export type IdentityMembershipOrg = {
id: string;
identity: Identity;
organization: string;
lastLoggedInAuthMethod?: IdentityAuthMethod;
lastLoginAuthMethod?: IdentityAuthMethod;
lastLoginTime?: string;
metadata: { key: string; value: string; id: string }[];
role: "admin" | "member" | "viewer" | "no-access" | "custom";
customRole?: TOrgRole;

View File

@@ -68,7 +68,8 @@ export type OrgUser = {
deniedPermissions: any[];
roleId: string;
isActive: boolean;
lastLoggedInAuthMethod?: AuthMethod;
lastLoginAuthMethod?: AuthMethod;
lastLoginTime?: string;
};
export type TProjectMembership = {

View File

@@ -293,7 +293,13 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
{isPending && <TableSkeleton columns={3} innerKey="org-identities" />}
{!isPending &&
data?.identities?.map(
({ identity: { id, name }, role, customRole, lastLoggedInAuthMethod }) => {
({
identity: { id, name },
role,
customRole,
lastLoginAuthMethod,
lastLoginTime
}) => {
return (
<Tr
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
@@ -309,14 +315,13 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
>
<Td className="group">
{name}
{lastLoggedInAuthMethod && (
{lastLoginAuthMethod && lastLoginTime && (
<Tooltip
className="min-w-52 max-w-96"
content={
<LastLoginSection
lastLoggedInAuthMethod={
identityAuthToNameMap[lastLoggedInAuthMethod]
}
lastLoginAuthMethod={identityAuthToNameMap[lastLoginAuthMethod]}
lastLoginTime={lastLoginTime}
/>
}
>

View File

@@ -481,7 +481,8 @@ export const OrgMembersTable = ({
id: orgMembershipId,
status,
isActive,
lastLoggedInAuthMethod
lastLoginAuthMethod,
lastLoginTime
}) => {
const name =
u && u.firstName ? `${u.firstName} ${u.lastName ?? ""}`.trim() : null;
@@ -530,11 +531,14 @@ export const OrgMembersTable = ({
</Tooltip>
</Badge>
)}
{lastLoggedInAuthMethod && (
{lastLoginAuthMethod && lastLoginTime && (
<Tooltip
className="min-w-52 max-w-96"
content={
<LastLoginSection lastLoggedInAuthMethod={lastLoggedInAuthMethod} />
<LastLoginSection
lastLoginAuthMethod={lastLoginAuthMethod}
lastLoginTime={lastLoginTime}
/>
}
>
<FontAwesomeIcon

View File

@@ -24,6 +24,7 @@ import { OrgPermissionIdentityActions, OrgPermissionSubjects } from "@app/contex
import { useTimedReset } from "@app/hooks";
import { identityAuthToNameMap, useGetIdentityById } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { format } from "date-fns";
type Props = {
identityId: string;
@@ -141,7 +142,13 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen }: Props) =
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Last Login Auth Method</p>
<p className="text-sm text-mineshaft-300">
{data.lastLoggedInAuthMethod ? identityAuthToNameMap[data.lastLoggedInAuthMethod] : "-"}
{data.lastLoginAuthMethod ? identityAuthToNameMap[data.lastLoginAuthMethod] : "-"}
</p>
</div>
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Last Login Time</p>
<p className="text-sm text-mineshaft-300">
{data.lastLoginTime ? format(data.lastLoginTime, "PPpp") : "-"}
</p>
</div>
<div className="mb-4">

View File

@@ -22,6 +22,7 @@ import { useFetchServerStatus, useGetOrgMembership, useGetOrgRoles } from "@app/
import { OrgUser } from "@app/hooks/api/types";
import { useResendOrgMemberInvitation } from "@app/hooks/api/users/mutation";
import { UsePopUpState } from "@app/hooks/usePopUp";
import { format } from "date-fns";
type Props = {
membershipId: string;
@@ -163,7 +164,15 @@ export const UserDetailsSection = ({ membershipId, handlePopUpOpen }: Props) =>
<p className="text-sm font-semibold text-mineshaft-300">Last Login Auth Method</p>
<div className="group flex align-top">
<p className="break-all text-sm text-mineshaft-300">
{membership.lastLoggedInAuthMethod || "-"}
{membership.lastLoginAuthMethod || "-"}
</p>
</div>
</div>
<div className="mb-4">
<p className="text-sm font-semibold text-mineshaft-300">Last Login Time</p>
<div className="group flex align-top">
<p className="break-all text-sm text-mineshaft-300">
{membership.lastLoginTime ? format(membership.lastLoginTime, "PPpp") : "-"}
</p>
</div>
</div>