diff --git a/packages/core/src/lib/session.ts b/packages/core/src/lib/session.ts index e55d13001..86ff51ba4 100644 --- a/packages/core/src/lib/session.ts +++ b/packages/core/src/lib/session.ts @@ -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) => { diff --git a/packages/core/src/routes/session/continue.test.ts b/packages/core/src/routes/session/continue.test.ts index 76d4456fe..05ae2f17b 100644 --- a/packages/core/src/routes/session/continue.test.ts +++ b/packages/core/src/routes/session/continue.test.ts @@ -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() ); }); diff --git a/packages/core/src/routes/session/password.test.ts b/packages/core/src/routes/session/password.test.ts index a3c0af8c8..212cd7645 100644 --- a/packages/core/src/routes/session/password.test.ts +++ b/packages/core/src/routes/session/password.test.ts @@ -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(); diff --git a/packages/core/src/routes/session/passwordless.test.ts b/packages/core/src/routes/session/passwordless.test.ts index 47dc04c74..77d428e20 100644 --- a/packages/core/src/routes/session/passwordless.test.ts +++ b/packages/core/src/routes/session/passwordless.test.ts @@ -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() ); diff --git a/packages/core/src/routes/session/social.test.ts b/packages/core/src/routes/session/social.test.ts index e00a6a8cd..d67f2b3f9 100644 --- a/packages/core/src/routes/session/social.test.ts +++ b/packages/core/src/routes/session/social.test.ts @@ -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 */ diff --git a/packages/core/src/routes/session/utils.test.ts b/packages/core/src/routes/session/utils.test.ts index 3bceb3391..49d6e2255 100644 --- a/packages/core/src/routes/session/utils.test.ts +++ b/packages/core/src/routes/session/utils.test.ts @@ -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() ); });