0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-20 21:32:31 -05:00

feat(core,schemas): log sign-in (#594)

* feat(schemas): sign-in log types

* feat(core): log username-password sign-in

* feat(core): log passwordless sign-in

* feat(core): log social sign-in
This commit is contained in:
IceHe.xyz 2022-04-21 15:07:54 +08:00 committed by GitHub
parent a04f472fe0
commit 4ba37e7e73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 6 deletions

View file

@ -153,7 +153,7 @@ describe('sessionRoutes', () => {
provider: new Provider(''),
middlewares: [
async (ctx, next) => {
ctx.userLog = {};
ctx.log = {};
return next();
},

View file

@ -2,7 +2,7 @@
import path from 'path';
import { LogtoErrorCode } from '@logto/phrases';
import { PasscodeType, userInfoSelectFields } from '@logto/schemas';
import { LogType, PasscodeType, userInfoSelectFields } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import pick from 'lodash.pick';
import { Provider } from 'oidc-provider';
@ -68,9 +68,12 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
}),
async (ctx, next) => {
const { username, password } = ctx.guard.body;
ctx.log.type = LogType.SignInUsernamePassword;
ctx.log.username = username;
assertThat(password, 'session.insufficient_info');
const { id } = await findUserByUsernameAndPassword(username, password);
ctx.log.userId = id;
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
return next();
@ -81,8 +84,12 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
'/session/sign-in/passwordless/sms/send-passcode',
koaGuard({ body: object({ phone: string().regex(phoneRegEx) }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { phone } = ctx.guard.body;
ctx.log.type = LogType.SignInSmsSendPasscode;
ctx.log.phone = phone;
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
ctx.log.sessionId = jti;
assertThat(
await hasUserWithPhone(phone),
@ -90,6 +97,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
);
const passcode = await createPasscode(jti, PasscodeType.SignIn, { phone });
ctx.log.passcode = passcode;
await sendPasscode(passcode);
ctx.status = 204;
@ -101,8 +110,13 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
'/session/sign-in/passwordless/sms/verify-passcode',
koaGuard({ body: object({ phone: string().regex(phoneRegEx), code: string() }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { phone, code } = ctx.guard.body;
ctx.log.type = LogType.SignInSms;
ctx.log.phone = phone;
ctx.log.passcode = code;
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
ctx.log.sessionId = jti;
assertThat(
await hasUserWithPhone(phone),
@ -111,6 +125,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
await verifyPasscode(jti, PasscodeType.SignIn, code, { phone });
const { id } = await findUserByPhone(phone);
ctx.log.userId = id;
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
@ -122,8 +137,12 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
'/session/sign-in/passwordless/email/send-passcode',
koaGuard({ body: object({ email: string().regex(emailRegEx) }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { email } = ctx.guard.body;
ctx.log.type = LogType.SignInEmailSendPasscode;
ctx.log.email = email;
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
ctx.log.sessionId = jti;
assertThat(
await hasUserWithEmail(email),
@ -131,6 +150,8 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
);
const passcode = await createPasscode(jti, PasscodeType.SignIn, { email });
ctx.log.passcode = passcode;
await sendPasscode(passcode);
ctx.status = 204;
@ -142,8 +163,13 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
'/session/sign-in/passwordless/email/verify-passcode',
koaGuard({ body: object({ email: string().regex(emailRegEx), code: string() }) }),
async (ctx, next) => {
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
const { email, code } = ctx.guard.body;
ctx.log.type = LogType.SignInEmail;
ctx.log.email = email;
ctx.log.passcode = code;
const { jti } = await provider.interactionDetails(ctx.req, ctx.res);
ctx.log.sessionId = jti;
assertThat(
await hasUserWithEmail(email),
@ -152,6 +178,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
await verifyPasscode(jti, PasscodeType.SignIn, code, { email });
const { id } = await findUserByEmail(email);
ctx.log.userId = id;
await assignInteractionResults(ctx, provider, { login: { accountId: id } });
@ -171,6 +198,11 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
}),
async (ctx, next) => {
const { connectorId, code, state, redirectUri } = ctx.guard.body;
ctx.log.type = LogType.SignInSocial;
ctx.log.connectorId = connectorId;
ctx.log.code = code;
ctx.log.state = state;
ctx.log.redirectUri = redirectUri;
if (!code) {
assertThat(state && redirectUri, 'session.insufficient_info');
@ -178,11 +210,13 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
assertThat(connector.connector.enabled, 'connector.not_enabled');
const redirectTo = await connector.getAuthorizationUri(redirectUri, state);
ctx.body = { redirectTo };
ctx.log.redirectTo = redirectTo;
return next();
}
const userInfo = await getUserInfoByAuthCode(connectorId, code, redirectUri);
ctx.log.userInfo = userInfo;
if (!(await hasUserWithIdentity(connectorId, userInfo.id))) {
await assignInteractionResults(ctx, provider, { connectorId, userInfo }, true);
@ -197,6 +231,7 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
}
const { id, identities } = await findUserByIdentity(connectorId, userInfo.id);
ctx.log.userId = id;
// Update social connector's user info
await updateUserById(id, {
@ -215,14 +250,21 @@ export default function sessionRoutes<T extends AnonymousRouter>(router: T, prov
}),
async (ctx, next) => {
const { connectorId } = ctx.guard.body;
ctx.log.type = LogType.SignInSocialBind;
ctx.log.connectorId = connectorId;
const { result } = await provider.interactionDetails(ctx.req, ctx.res);
assertThat(result, 'session.connector_session_not_found');
const userInfo = await getUserInfoFromInteractionResult(connectorId, result);
ctx.log.userInfo = userInfo;
const relatedInfo = await findSocialRelatedUser(userInfo);
assertThat(relatedInfo, 'session.connector_session_not_found');
const { id, identities } = relatedInfo[1];
ctx.log.userId = id;
await updateUserById(id, {
identities: { ...identities, [connectorId]: { userId: userInfo.id, details: userInfo } },
});

View file

@ -1,5 +1,11 @@
export enum LogType {
SignInUsernamePassword = 'SignInUsernamePassword',
SignInEmail = 'SignInEmail',
SignInEmailSendPasscode = 'SignInEmailSendPasscode',
SignInSms = 'SignInSms',
SignInSmsSendPasscode = 'SignInSmsSendPasscode',
SignInSocial = 'SignInSocial',
SignInSocialBind = 'SignInSocialBind',
}
export enum LogResult {