mirror of
https://github.com/logto-io/logto.git
synced 2025-01-13 21:30:30 -05:00
feat(core,schemas): log register (#601)
This commit is contained in:
parent
3aa4342f2e
commit
a10b427c87
3 changed files with 85 additions and 8 deletions
|
@ -690,6 +690,7 @@ describe('sessionRoutes', () => {
|
||||||
describe('POST /session/register/social', () => {
|
describe('POST /session/register/social', () => {
|
||||||
it('register with social, assign result and redirect', async () => {
|
it('register with social, assign result and redirect', async () => {
|
||||||
interactionDetails.mockResolvedValueOnce({
|
interactionDetails.mockResolvedValueOnce({
|
||||||
|
jti: 'jti',
|
||||||
result: {
|
result: {
|
||||||
socialUserInfo: { connectorId: 'connectorId', userInfo: { id: 'user1' } },
|
socialUserInfo: { connectorId: 'connectorId', userInfo: { id: 'user1' } },
|
||||||
},
|
},
|
||||||
|
@ -759,6 +760,7 @@ describe('sessionRoutes', () => {
|
||||||
});
|
});
|
||||||
it('updates user identities', async () => {
|
it('updates user identities', async () => {
|
||||||
interactionDetails.mockResolvedValueOnce({
|
interactionDetails.mockResolvedValueOnce({
|
||||||
|
jti: 'jti',
|
||||||
result: {
|
result: {
|
||||||
login: { accountId: 'user1' },
|
login: { accountId: 'user1' },
|
||||||
socialUserInfo: {
|
socialUserInfo: {
|
||||||
|
|
|
@ -306,7 +306,10 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
|
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
||||||
const { username, password } = ctx.guard.body;
|
const { username, password } = ctx.guard.body;
|
||||||
|
const type = 'RegisterUsernamePassword';
|
||||||
|
ctx.log(type, { sessionId: jti, username });
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
password,
|
password,
|
||||||
|
@ -324,6 +327,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
);
|
);
|
||||||
|
|
||||||
const id = await generateUserId();
|
const id = await generateUserId();
|
||||||
|
ctx.log(type, { userId: id });
|
||||||
|
|
||||||
const { passwordEncryptionSalt, passwordEncrypted, passwordEncryptionMethod } =
|
const { passwordEncryptionSalt, passwordEncrypted, passwordEncryptionMethod } =
|
||||||
encryptUserPassword(id, password);
|
encryptUserPassword(id, password);
|
||||||
|
|
||||||
|
@ -358,6 +363,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
||||||
const { phone } = ctx.guard.body;
|
const { phone } = ctx.guard.body;
|
||||||
|
const type = 'RegisterSmsSendPasscode';
|
||||||
|
ctx.log(type, { sessionId: jti, phone });
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
!(await hasUserWithPhone(phone)),
|
!(await hasUserWithPhone(phone)),
|
||||||
|
@ -365,6 +372,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
);
|
);
|
||||||
|
|
||||||
const passcode = await createPasscode(jti, PasscodeType.Register, { phone });
|
const passcode = await createPasscode(jti, PasscodeType.Register, { phone });
|
||||||
|
ctx.log(type, { sessionId: jti, phone, passcode });
|
||||||
|
|
||||||
await sendPasscode(passcode);
|
await sendPasscode(passcode);
|
||||||
ctx.status = 204;
|
ctx.status = 204;
|
||||||
|
|
||||||
|
@ -378,6 +387,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
||||||
const { phone, code } = ctx.guard.body;
|
const { phone, code } = ctx.guard.body;
|
||||||
|
const type = 'RegisterSms';
|
||||||
|
ctx.log(type, { sessionId: jti, phone, code });
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
!(await hasUserWithPhone(phone)),
|
!(await hasUserWithPhone(phone)),
|
||||||
|
@ -386,6 +397,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
|
|
||||||
await verifyPasscode(jti, PasscodeType.Register, code, { phone });
|
await verifyPasscode(jti, PasscodeType.Register, code, { phone });
|
||||||
const id = await generateUserId();
|
const id = await generateUserId();
|
||||||
|
ctx.log(type, { userId: id });
|
||||||
|
|
||||||
await insertUser({ id, primaryPhone: phone });
|
await insertUser({ id, primaryPhone: phone });
|
||||||
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
||||||
|
@ -400,6 +412,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
||||||
const { email } = ctx.guard.body;
|
const { email } = ctx.guard.body;
|
||||||
|
const type = 'RegisterEmailSendPasscode';
|
||||||
|
ctx.log(type, { sessionId: jti, email });
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
!(await hasUserWithEmail(email)),
|
!(await hasUserWithEmail(email)),
|
||||||
|
@ -407,6 +421,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
);
|
);
|
||||||
|
|
||||||
const passcode = await createPasscode(jti, PasscodeType.Register, { email });
|
const passcode = await createPasscode(jti, PasscodeType.Register, { email });
|
||||||
|
ctx.log(type, { passcode });
|
||||||
|
|
||||||
await sendPasscode(passcode);
|
await sendPasscode(passcode);
|
||||||
ctx.status = 204;
|
ctx.status = 204;
|
||||||
|
|
||||||
|
@ -420,6 +436,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
|
||||||
const { email, code } = ctx.guard.body;
|
const { email, code } = ctx.guard.body;
|
||||||
|
const type = 'RegisterEmail';
|
||||||
|
ctx.log(type, { sessionId: jti, email, code });
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
!(await hasUserWithEmail(email)),
|
!(await hasUserWithEmail(email)),
|
||||||
|
@ -428,6 +446,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
|
|
||||||
await verifyPasscode(jti, PasscodeType.Register, code, { email });
|
await verifyPasscode(jti, PasscodeType.Register, code, { email });
|
||||||
const id = await generateUserId();
|
const id = await generateUserId();
|
||||||
|
ctx.log(type, { userId: id });
|
||||||
|
|
||||||
await insertUser({ id, primaryEmail: email });
|
await insertUser({ id, primaryEmail: email });
|
||||||
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
||||||
|
@ -444,15 +463,18 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { connectorId } = ctx.guard.body;
|
const { jti, result } = await provider.interactionDetails(ctx.req, ctx.res);
|
||||||
const { result } = await provider.interactionDetails(ctx.req, ctx.res);
|
|
||||||
|
|
||||||
// User can not register with social directly,
|
// User can not register with social directly,
|
||||||
// need to try to sign in with social first, then confirm to register and continue,
|
// need to try to sign in with social first, then confirm to register and continue,
|
||||||
// so the result is expected to be exists.
|
// so the result is expected to be exists.
|
||||||
assertThat(result, 'session.connector_session_not_found');
|
assertThat(result, 'session.connector_session_not_found');
|
||||||
|
|
||||||
|
const { connectorId } = ctx.guard.body;
|
||||||
|
const type = 'RegisterSocial';
|
||||||
|
ctx.log(type, { sessionId: jti, connectorId });
|
||||||
|
|
||||||
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
|
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
|
||||||
|
ctx.log(type, { userInfo });
|
||||||
assertThat(!(await hasUserWithIdentity(connectorId, userInfo.id)), 'user.identity_exists');
|
assertThat(!(await hasUserWithIdentity(connectorId, userInfo.id)), 'user.identity_exists');
|
||||||
|
|
||||||
const id = await generateUserId();
|
const id = await generateUserId();
|
||||||
|
@ -467,6 +489,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
ctx.log(type, { userId: id });
|
||||||
|
|
||||||
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
|
||||||
|
|
||||||
|
@ -482,15 +505,20 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
async (ctx, next) => {
|
async (ctx, next) => {
|
||||||
const { connectorId } = ctx.guard.body;
|
const { jti, result } = await provider.interactionDetails(ctx.req, ctx.res);
|
||||||
const { result } = await provider.interactionDetails(ctx.req, ctx.res);
|
|
||||||
assertThat(result, 'session.connector_session_not_found');
|
assertThat(result, 'session.connector_session_not_found');
|
||||||
assertThat(result.login?.accountId, 'session.unauthorized');
|
const userId = result.login?.accountId;
|
||||||
|
assertThat(userId, 'session.unauthorized');
|
||||||
|
|
||||||
|
const { connectorId } = ctx.guard.body;
|
||||||
|
const type = 'RegisterSocialBind';
|
||||||
|
ctx.log(type, { sessionId: jti, connectorId, userId });
|
||||||
|
|
||||||
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
|
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
|
||||||
const user = await findUserById(result.login.accountId);
|
ctx.log(type, { userInfo });
|
||||||
|
|
||||||
const updatedUser = await updateUserById(user.id, {
|
const user = await findUserById(userId);
|
||||||
|
const updatedUser = await updateUserById(userId, {
|
||||||
identities: {
|
identities: {
|
||||||
...user.identities,
|
...user.identities,
|
||||||
[connectorId]: { userId: userInfo.id, details: userInfo },
|
[connectorId]: { userId: userInfo.id, details: userInfo },
|
||||||
|
|
|
@ -11,6 +11,46 @@ interface BaseLogPayload {
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RegisterUsernamePasswordLogPayload extends BaseLogPayload {
|
||||||
|
userId?: string;
|
||||||
|
username?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterEmailSendPasscodeLogPayload extends BaseLogPayload {
|
||||||
|
email?: string;
|
||||||
|
passcode?: Passcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterEmailLogPayload extends BaseLogPayload {
|
||||||
|
email?: string;
|
||||||
|
code?: string;
|
||||||
|
userId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterSmsSendPasscodeLogPayload extends BaseLogPayload {
|
||||||
|
phone?: string;
|
||||||
|
passcode?: Passcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterSmsLogPayload extends BaseLogPayload {
|
||||||
|
phone?: string;
|
||||||
|
code?: string;
|
||||||
|
userId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterSocialBindLogPayload extends BaseLogPayload {
|
||||||
|
connectorId?: string;
|
||||||
|
userInfo?: object;
|
||||||
|
userId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RegisterSocialLogPayload extends RegisterSocialBindLogPayload {
|
||||||
|
code?: string;
|
||||||
|
state?: string;
|
||||||
|
redirectUri?: string;
|
||||||
|
redirectTo?: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface SignInUsernamePasswordLogPayload extends BaseLogPayload {
|
interface SignInUsernamePasswordLogPayload extends BaseLogPayload {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
|
@ -52,6 +92,13 @@ interface SignInSocialLogPayload extends SignInSocialBindLogPayload {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LogPayloads = {
|
export type LogPayloads = {
|
||||||
|
RegisterUsernamePassword: RegisterUsernamePasswordLogPayload;
|
||||||
|
RegisterEmailSendPasscode: RegisterEmailSendPasscodeLogPayload;
|
||||||
|
RegisterEmail: RegisterEmailLogPayload;
|
||||||
|
RegisterSmsSendPasscode: RegisterSmsSendPasscodeLogPayload;
|
||||||
|
RegisterSms: RegisterSmsLogPayload;
|
||||||
|
RegisterSocialBind: RegisterSocialBindLogPayload;
|
||||||
|
RegisterSocial: RegisterSocialLogPayload;
|
||||||
SignInUsernamePassword: SignInUsernamePasswordLogPayload;
|
SignInUsernamePassword: SignInUsernamePasswordLogPayload;
|
||||||
SignInEmailSendPasscode: SignInEmailSendPasscodeLogPayload;
|
SignInEmailSendPasscode: SignInEmailSendPasscodeLogPayload;
|
||||||
SignInEmail: SignInEmailLogPayload;
|
SignInEmail: SignInEmailLogPayload;
|
||||||
|
|
Loading…
Add table
Reference in a new issue