mirror of
https://github.com/logto-io/logto.git
synced 2025-03-10 22:22:45 -05:00
feat(core): add sign-in timestamp in session interaction (#2325)
This commit is contained in:
parent
960fbc38c4
commit
ec36266a1b
6 changed files with 65 additions and 26 deletions
|
@ -1,3 +1,5 @@
|
|||
import { conditional } from '@silverhand/essentials';
|
||||
import { getUnixTime } from 'date-fns';
|
||||
import type { Context } from 'koa';
|
||||
import type { InteractionResults, Provider } from 'oidc-provider';
|
||||
|
||||
|
@ -14,20 +16,32 @@ export const assignInteractionResults = async (
|
|||
// have to do it manually
|
||||
// refer to: https://github.com/panva/node-oidc-provider/blob/c243bf6b6663c41ff3e75c09b95fb978eba87381/lib/actions/authorization/interactions.js#L106
|
||||
const details = merge ? await provider.interactionDetails(ctx.req, ctx.res) : undefined;
|
||||
|
||||
const ts = getUnixTime(new Date());
|
||||
const mergedResult = {
|
||||
// Merge with current result
|
||||
...details?.result,
|
||||
...result,
|
||||
};
|
||||
const redirectTo = await provider.interactionResult(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
{
|
||||
// Merge with current result
|
||||
...details?.result,
|
||||
...result,
|
||||
...mergedResult,
|
||||
...conditional(
|
||||
mergedResult.login && {
|
||||
login: {
|
||||
...mergedResult.login,
|
||||
// Update ts(timestamp) if the accountId has been set in result
|
||||
ts: result.login?.accountId ? ts : mergedResult.login.ts,
|
||||
},
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
mergeWithLastSubmission: merge,
|
||||
}
|
||||
);
|
||||
ctx.body = { redirectTo };
|
||||
ctx.body = { redirectTo, ts };
|
||||
};
|
||||
|
||||
export const saveUserFirstConsentedAppId = async (userId: string, applicationId: string) => {
|
||||
|
|
|
@ -89,7 +89,8 @@ describe('session -> continueRoutes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: mockUser.id } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: mockUser.id, ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -121,7 +122,8 @@ describe('session -> continueRoutes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: mockUser.id } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: mockUser.id, ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -156,7 +158,8 @@ describe('session -> continueRoutes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: mockUser.id } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: mockUser.id, ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -188,7 +191,8 @@ describe('session -> continueRoutes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: mockUser.id } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: mockUser.id, ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
|
|
@ -111,7 +111,8 @@ describe('session -> password routes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: 'id' } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: 'id', ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -127,7 +128,8 @@ describe('session -> password routes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: 'id' } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: 'id', ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -143,7 +145,8 @@ describe('session -> password routes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: 'id' } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: 'id', ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -172,7 +175,8 @@ describe('session -> password routes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: 'user1' } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: 'user1', ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
jest.useRealTimers();
|
||||
|
|
|
@ -392,7 +392,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: mockUser.id },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: mockUser.id, ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
@ -414,7 +415,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: mockUser.id },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: mockUser.id, ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
@ -555,7 +557,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: mockUser.id },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: mockUser.id, ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
@ -579,7 +582,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: mockUser.id },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: mockUser.id, ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
@ -688,7 +692,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: 'user1' },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: 'user1', ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
@ -710,7 +715,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: 'user1' },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: 'user1', ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
@ -816,7 +822,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: 'user1' },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: 'user1', ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
@ -838,7 +845,8 @@ describe('session -> passwordlessRoutes', () => {
|
|||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
login: { accountId: 'user1' },
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
login: { accountId: 'user1', ts: expect.any(Number) },
|
||||
}),
|
||||
expect.anything()
|
||||
);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable max-lines */
|
||||
import { ConnectorType } from '@logto/connector-kit';
|
||||
import type { User } from '@logto/schemas';
|
||||
import { SignUpIdentifier } from '@logto/schemas';
|
||||
|
@ -233,7 +234,8 @@ describe('session -> socialRoutes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: mockUser.id } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: mockUser.id, ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -316,7 +318,8 @@ describe('session -> socialRoutes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: 'user1' } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: 'user1', ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -350,7 +353,8 @@ describe('session -> socialRoutes', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: 'user1' } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: 'user1', ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
@ -364,7 +368,10 @@ describe('session -> socialRoutes', () => {
|
|||
});
|
||||
|
||||
it('throw error if result parsing fails', async () => {
|
||||
interactionDetails.mockResolvedValueOnce({ result: { login: { accountId: mockUser.id } } });
|
||||
interactionDetails.mockResolvedValueOnce({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
result: { login: { accountId: mockUser.id, ts: expect.any(Number) } },
|
||||
});
|
||||
const response = await sessionRequest
|
||||
.post(`${registerRoute}`)
|
||||
.send({ connectorId: 'connectorId' });
|
||||
|
@ -436,3 +443,4 @@ describe('session -> socialRoutes', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
/* eslint-enable max-lines */
|
||||
|
|
|
@ -121,7 +121,8 @@ describe('signInWithPassword()', () => {
|
|||
expect(interactionResult).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.objectContaining({ login: { accountId: mockUser.id } }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
expect.objectContaining({ login: { accountId: mockUser.id, ts: expect.any(Number) } }),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue