@things-factory/auth-base 8.0.0-alpha.8 → 8.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/client/actions/auth.ts +5 -4
  2. package/client/index.ts +1 -0
  3. package/client/verify-webauthn.ts +86 -0
  4. package/dist-client/actions/auth.d.ts +5 -4
  5. package/dist-client/actions/auth.js.map +1 -1
  6. package/dist-client/index.d.ts +1 -0
  7. package/dist-client/index.js +1 -0
  8. package/dist-client/index.js.map +1 -1
  9. package/dist-client/tsconfig.tsbuildinfo +1 -1
  10. package/dist-client/verify-webauthn.d.ts +13 -0
  11. package/dist-client/verify-webauthn.js +72 -0
  12. package/dist-client/verify-webauthn.js.map +1 -0
  13. package/dist-server/constants/error-code.d.ts +2 -0
  14. package/dist-server/constants/error-code.js +3 -1
  15. package/dist-server/constants/error-code.js.map +1 -1
  16. package/dist-server/controllers/change-pwd.js +2 -2
  17. package/dist-server/controllers/change-pwd.js.map +1 -1
  18. package/dist-server/controllers/delete-user.js +13 -12
  19. package/dist-server/controllers/delete-user.js.map +1 -1
  20. package/dist-server/controllers/invitation.d.ts +2 -1
  21. package/dist-server/controllers/invitation.js +30 -5
  22. package/dist-server/controllers/invitation.js.map +1 -1
  23. package/dist-server/controllers/profile.d.ts +4 -3
  24. package/dist-server/controllers/profile.js +20 -2
  25. package/dist-server/controllers/profile.js.map +1 -1
  26. package/dist-server/controllers/signin.d.ts +4 -1
  27. package/dist-server/controllers/signin.js +17 -1
  28. package/dist-server/controllers/signin.js.map +1 -1
  29. package/dist-server/controllers/signup.js +13 -4
  30. package/dist-server/controllers/signup.js.map +1 -1
  31. package/dist-server/controllers/unlock-user.js +1 -0
  32. package/dist-server/controllers/unlock-user.js.map +1 -1
  33. package/dist-server/controllers/verification.js +1 -0
  34. package/dist-server/controllers/verification.js.map +1 -1
  35. package/dist-server/index.d.ts +1 -0
  36. package/dist-server/index.js +1 -0
  37. package/dist-server/index.js.map +1 -1
  38. package/dist-server/middlewares/signin-middleware.js +3 -3
  39. package/dist-server/middlewares/signin-middleware.js.map +1 -1
  40. package/dist-server/middlewares/webauthn-middleware.js.map +1 -1
  41. package/dist-server/migrations/1548206416130-SeedUser.js +2 -1
  42. package/dist-server/migrations/1548206416130-SeedUser.js.map +1 -1
  43. package/dist-server/router/auth-checkin-router.js +8 -2
  44. package/dist-server/router/auth-checkin-router.js.map +1 -1
  45. package/dist-server/router/auth-private-process-router.js +12 -7
  46. package/dist-server/router/auth-private-process-router.js.map +1 -1
  47. package/dist-server/router/auth-public-process-router.js +20 -9
  48. package/dist-server/router/auth-public-process-router.js.map +1 -1
  49. package/dist-server/router/auth-signin-router.js +3 -3
  50. package/dist-server/router/auth-signin-router.js.map +1 -1
  51. package/dist-server/router/webauthn-router.js +51 -1
  52. package/dist-server/router/webauthn-router.js.map +1 -1
  53. package/dist-server/service/appliance/appliance.js +4 -1
  54. package/dist-server/service/appliance/appliance.js.map +1 -1
  55. package/dist-server/service/application/application.js +11 -3
  56. package/dist-server/service/application/application.js.map +1 -1
  57. package/dist-server/service/invitation/invitation-mutation.d.ts +3 -2
  58. package/dist-server/service/invitation/invitation-mutation.js +20 -8
  59. package/dist-server/service/invitation/invitation-mutation.js.map +1 -1
  60. package/dist-server/service/user/user-mutation.d.ts +10 -9
  61. package/dist-server/service/user/user-mutation.js +135 -61
  62. package/dist-server/service/user/user-mutation.js.map +1 -1
  63. package/dist-server/service/user/user-types.d.ts +1 -0
  64. package/dist-server/service/user/user-types.js +4 -0
  65. package/dist-server/service/user/user-types.js.map +1 -1
  66. package/dist-server/service/user/user.d.ts +1 -0
  67. package/dist-server/service/user/user.js +57 -17
  68. package/dist-server/service/user/user.js.map +1 -1
  69. package/dist-server/service/verification-token/verification-token.js +7 -2
  70. package/dist-server/service/verification-token/verification-token.js.map +1 -1
  71. package/dist-server/templates/account-unlock-email.d.ts +2 -1
  72. package/dist-server/templates/account-unlock-email.js +1 -1
  73. package/dist-server/templates/account-unlock-email.js.map +1 -1
  74. package/dist-server/templates/invitation-email.d.ts +2 -1
  75. package/dist-server/templates/invitation-email.js +1 -1
  76. package/dist-server/templates/invitation-email.js.map +1 -1
  77. package/dist-server/templates/verification-email.d.ts +2 -1
  78. package/dist-server/templates/verification-email.js +1 -1
  79. package/dist-server/templates/verification-email.js.map +1 -1
  80. package/dist-server/tsconfig.tsbuildinfo +1 -1
  81. package/dist-server/utils/check-user-has-role.d.ts +12 -0
  82. package/dist-server/utils/check-user-has-role.js +28 -0
  83. package/dist-server/utils/check-user-has-role.js.map +1 -0
  84. package/package.json +6 -6
  85. package/server/constants/error-code.ts +2 -0
  86. package/server/controllers/change-pwd.ts +3 -2
  87. package/server/controllers/delete-user.ts +16 -13
  88. package/server/controllers/invitation.ts +36 -5
  89. package/server/controllers/profile.ts +29 -2
  90. package/server/controllers/signin.ts +21 -2
  91. package/server/controllers/signup.ts +16 -4
  92. package/server/controllers/unlock-user.ts +1 -0
  93. package/server/controllers/verification.ts +1 -0
  94. package/server/index.ts +1 -0
  95. package/server/middlewares/signin-middleware.ts +3 -3
  96. package/server/middlewares/webauthn-middleware.ts +7 -8
  97. package/server/migrations/1548206416130-SeedUser.ts +2 -1
  98. package/server/router/auth-checkin-router.ts +11 -5
  99. package/server/router/auth-private-process-router.ts +14 -7
  100. package/server/router/auth-public-process-router.ts +22 -10
  101. package/server/router/auth-signin-router.ts +3 -3
  102. package/server/router/webauthn-router.ts +71 -9
  103. package/server/service/appliance/appliance.ts +4 -1
  104. package/server/service/application/application.ts +12 -3
  105. package/server/service/invitation/invitation-mutation.ts +24 -9
  106. package/server/service/user/user-mutation.ts +152 -62
  107. package/server/service/user/user-types.ts +3 -0
  108. package/server/service/user/user.ts +74 -18
  109. package/server/service/verification-token/verification-token.ts +9 -3
  110. package/server/templates/account-unlock-email.ts +1 -1
  111. package/server/templates/invitation-email.ts +1 -1
  112. package/server/templates/verification-email.ts +1 -1
  113. package/server/utils/check-user-has-role.ts +29 -0
  114. package/translations/en.json +5 -1
  115. package/translations/ja.json +5 -1
  116. package/translations/ko.json +6 -3
  117. package/translations/ms.json +5 -1
  118. package/translations/zh.json +5 -1
@@ -0,0 +1,12 @@
1
+ import { Domain } from '@things-factory/shell';
2
+ import { User } from '../service/user/user';
3
+ /**
4
+ * @description 사용자가 특정 도메인 또는 상위 도메인에서 특정 역할을 가지고 있는지 확인합니다.
5
+ *
6
+ * @param roleId 확인할 역할의 ID
7
+ * @param domain 역할을 확인할 도메인
8
+ * @param user 역할을 확인할 사용자
9
+ *
10
+ * @returns 사용자가 도메인 또는 상위 도메인에서 역할을 가지고 있는지 여부를 나타내는 boolean을 반환하는 Promise
11
+ */
12
+ export declare function checkUserHasRole(roleId: string, domain: Domain, user: User): Promise<Boolean>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkUserHasRole = checkUserHasRole;
4
+ const shell_1 = require("@things-factory/shell");
5
+ const user_1 = require("../service/user/user");
6
+ /**
7
+ * @description 사용자가 특정 도메인 또는 상위 도메인에서 특정 역할을 가지고 있는지 확인합니다.
8
+ *
9
+ * @param roleId 확인할 역할의 ID
10
+ * @param domain 역할을 확인할 도메인
11
+ * @param user 역할을 확인할 사용자
12
+ *
13
+ * @returns 사용자가 도메인 또는 상위 도메인에서 역할을 가지고 있는지 여부를 나타내는 boolean을 반환하는 Promise
14
+ */
15
+ async function checkUserHasRole(roleId, domain, user) {
16
+ if (!roleId) {
17
+ return true;
18
+ }
19
+ const me = await (0, shell_1.getRepository)(user_1.User).findOne({
20
+ where: { id: user.id },
21
+ relations: ['roles']
22
+ });
23
+ return me.roles
24
+ .filter(role => role.domainId === domain.id || (domain.parentId && role.domainId === domain.parentId))
25
+ .map(role => role.id)
26
+ .includes(roleId);
27
+ }
28
+ //# sourceMappingURL=check-user-has-role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-user-has-role.js","sourceRoot":"","sources":["../../server/utils/check-user-has-role.ts"],"names":[],"mappings":";;AAcA,4CAcC;AA5BD,iDAA6D;AAE7D,+CAA2C;AAG3C;;;;;;;;GAQG;AACI,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,MAAc,EAAE,IAAU;IAC/E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,IAAA,qBAAa,EAAC,WAAI,CAAC,CAAC,OAAO,CAAC;QAC3C,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;QACtB,SAAS,EAAE,CAAC,OAAO,CAAC;KACrB,CAAC,CAAA;IAEF,OAAO,EAAE,CAAC,KAAK;SACZ,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC;SACrG,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;SACpB,QAAQ,CAAC,MAAM,CAAC,CAAA;AACrB,CAAC","sourcesContent":["import { Domain, getRepository } from '@things-factory/shell'\n\nimport { User } from '../service/user/user'\nimport { Role } from 'service'\n\n/**\n * @description 사용자가 특정 도메인 또는 상위 도메인에서 특정 역할을 가지고 있는지 확인합니다.\n *\n * @param roleId 확인할 역할의 ID\n * @param domain 역할을 확인할 도메인\n * @param user 역할을 확인할 사용자\n *\n * @returns 사용자가 도메인 또는 상위 도메인에서 역할을 가지고 있는지 여부를 나타내는 boolean을 반환하는 Promise\n */\nexport async function checkUserHasRole(roleId: string, domain: Domain, user: User): Promise<Boolean> {\n if (!roleId) {\n return true\n }\n\n const me = await getRepository(User).findOne({\n where: { id: user.id },\n relations: ['roles']\n })\n\n return me.roles\n .filter(role => role.domainId === domain.id || (domain.parentId && role.domainId === domain.parentId))\n .map(role => role.id)\n .includes(roleId)\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/auth-base",
3
- "version": "8.0.0-alpha.8",
3
+ "version": "8.0.0-beta.0",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -32,10 +32,10 @@
32
32
  "dependencies": {
33
33
  "@simplewebauthn/browser": "^10.0.0",
34
34
  "@simplewebauthn/server": "^10.0.0",
35
- "@things-factory/email-base": "^8.0.0-alpha.8",
36
- "@things-factory/env": "^8.0.0-alpha.8",
37
- "@things-factory/shell": "^8.0.0-alpha.8",
38
- "@things-factory/utils": "^8.0.0-alpha.0",
35
+ "@things-factory/email-base": "^8.0.0-beta.0",
36
+ "@things-factory/env": "^8.0.0-beta.0",
37
+ "@things-factory/shell": "^8.0.0-beta.0",
38
+ "@things-factory/utils": "^8.0.0-beta.0",
39
39
  "@types/webappsec-credential-management": "^0.6.8",
40
40
  "jsonwebtoken": "^9.0.0",
41
41
  "koa-passport": "^6.0.0",
@@ -46,5 +46,5 @@
46
46
  "passport-jwt": "^4.0.0",
47
47
  "passport-local": "^1.0.0"
48
48
  },
49
- "gitHead": "0a9fb3ab431934982294b58c743d01b6f782a15f"
49
+ "gitHead": "add6fb8224b2cb19cbea47bed6a5ecb0424c9a28"
50
50
  }
@@ -17,4 +17,6 @@ export const PASSWORD_USED_PAST = 'password used in the past'
17
17
  export const VERIFICATION_ERROR = 'user or verification token not found'
18
18
  export const AUTHN_VERIFICATION_FAILED = 'authn verification failed'
19
19
  export const USER_CREDENTIAL_NOT_FOUND = 'user credential not found'
20
+ export const EMAIL_ALREADY_EXISTS = 'email already exists'
21
+ export const USERNAME_ALREADY_EXISTS = 'email already exists'
20
22
  export const AUTH_ERROR = 'auth error'
@@ -1,4 +1,3 @@
1
- import { ILike } from 'typeorm'
2
1
  import { config } from '@things-factory/env'
3
2
  import { getRepository } from '@things-factory/shell'
4
3
 
@@ -19,7 +18,8 @@ export async function changePwd(attrs, currentPass, newPass, confirmPass, contex
19
18
 
20
19
  // TODO 이 사용자가 이 도메인에 속한 사용자인지 확인해야함.
21
20
  const repository = getRepository(User)
22
- const user: User = await repository.findOne({ where: { email: ILike(attrs.email) } })
21
+
22
+ const user: User = await repository.findOne({ where: { id: attrs.id } })
23
23
 
24
24
  if (!user) {
25
25
  throw new AuthError({
@@ -37,6 +37,7 @@ export async function changePwd(attrs, currentPass, newPass, confirmPass, contex
37
37
  throw new AuthError({
38
38
  errorCode: PASSWORD_NOT_MATCHED,
39
39
  detail: {
40
+ username: user.username,
40
41
  email: user.email,
41
42
  failCount: user.failCount
42
43
  }
@@ -8,7 +8,20 @@ export async function deleteUser(attrs, tx?: EntityManager) {
8
8
  // TODO 다른 도메인에도 포함되어있다면, domains-users 관게와 해당 도메인 관련 정보만 삭제해야 함.
9
9
 
10
10
  const repository = tx?.getRepository(User)
11
- const user = await repository.findOne({ where: { email: ILike(attrs.email) } })
11
+ const { username } = attrs
12
+
13
+ var user = await repository.findOne({
14
+ where: { username },
15
+ relations: ['domains']
16
+ })
17
+
18
+ if (!user && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
19
+ user = await repository.findOne({
20
+ where: { email: ILike(username) },
21
+ relations: ['domains']
22
+ })
23
+ }
24
+
12
25
  if (!user) {
13
26
  throw new AuthError({
14
27
  errorCode: USER_NOT_FOUND
@@ -19,29 +32,19 @@ export async function deleteUser(attrs, tx?: EntityManager) {
19
32
  user.domains = []
20
33
 
21
34
  await repository.save(user)
22
-
23
- // repository api는 작동하지 않음.
24
- // await txManager
25
- // .createQueryBuilder()
26
- // .delete()
27
- // .from('users_domains')
28
- // .where({
29
- // usersId: user.id
30
- // })
31
- // .execute()
32
35
  }
33
36
 
34
37
  export async function deleteUsers(attrs, tx?: EntityManager) {
35
38
  // TODO 이 사용자가 이 도메인에 속한 사용자인지 확인해야함.
36
39
  // TODO 다른 도메인에도 포함되어있다면, domains-users 관게와 해당 도메인 관련 정보만 삭제해야 함.
37
40
 
38
- const { emails } = attrs
41
+ const { usernames } = attrs
39
42
 
40
43
  const repo = tx?.getRepository(User)
41
44
 
42
45
  const users = await repo.find({
43
46
  where: {
44
- email: In(emails)
47
+ username: In(usernames)
45
48
  }
46
49
  })
47
50
 
@@ -5,15 +5,27 @@ import { sendEmail } from '@things-factory/email-base'
5
5
  import { Domain, getRepository } from '@things-factory/shell'
6
6
 
7
7
  import { Invitation } from '../service/invitation/invitation'
8
- import { User } from '../service/user/user'
8
+ import { User, UserStatus } from '../service/user/user'
9
9
  import { getInvitationEmailForm } from '../templates/invitation-email'
10
10
  import { makeInvitationToken } from './utils/make-invitation-token'
11
11
  import { saveInvitationToken } from './utils/save-invitation-token'
12
12
 
13
13
  export async function invite(attrs, withEmailInvitation?: Boolean) {
14
- const { email, reference, type, context } = attrs
14
+ const { username, reference, type, context } = attrs
15
+ const repository = getRepository(User)
16
+
17
+ var user = await repository.findOne({
18
+ where: { username },
19
+ relations: ['domains']
20
+ })
21
+
22
+ if (!user && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
23
+ user = await repository.findOne({
24
+ where: { email: ILike(username) },
25
+ relations: ['domains']
26
+ })
27
+ }
15
28
 
16
- var user = await getRepository(User).findOne({ where: { email: ILike(email) }, relations: ['domains'] })
17
29
  var domains = user.domains
18
30
 
19
31
  // TODO reference should not be a domain.id (security reason)
@@ -28,6 +40,9 @@ export async function invite(attrs, withEmailInvitation?: Boolean) {
28
40
  }
29
41
 
30
42
  if (withEmailInvitation) {
43
+ const email = user.email
44
+
45
+ // TODO 초대장의 유효기간을 설정할 수 있어야 함.
31
46
  var invitation = await getRepository(Invitation).findOneBy({
32
47
  email: ILike(email),
33
48
  reference,
@@ -44,6 +59,7 @@ export async function invite(attrs, withEmailInvitation?: Boolean) {
44
59
 
45
60
  return await sendInvitationEmail({
46
61
  invitation,
62
+ user,
47
63
  context
48
64
  })
49
65
  }
@@ -89,7 +105,7 @@ export async function acceptInvitation(token) {
89
105
  return true
90
106
  }
91
107
 
92
- export async function sendInvitationEmail({ invitation, context }) {
108
+ export async function sendInvitationEmail({ invitation, user, context }) {
93
109
  try {
94
110
  var token = makeInvitationToken()
95
111
  var verifaction = await saveInvitationToken(invitation.id, token)
@@ -101,6 +117,7 @@ export async function sendInvitationEmail({ invitation, context }) {
101
117
  receiver: invitation.email,
102
118
  subject: 'Invitation',
103
119
  content: getInvitationEmailForm({
120
+ username: user.username,
104
121
  email: invitation.email,
105
122
  acceptUrl: serviceUrl
106
123
  })
@@ -123,10 +140,24 @@ export async function resendInvitationEmail(
123
140
  type
124
141
  })
125
142
 
126
- if (!invitation) return false
143
+ if (!invitation) {
144
+ throw new Error(`not found invitation.`)
145
+ }
146
+
147
+ var user = await getRepository(User).findOne({
148
+ where: {
149
+ email: ILike(email),
150
+ status: UserStatus.ACTIVATED
151
+ }
152
+ })
153
+
154
+ if (!user) {
155
+ throw new Error(`user not found: ${email}`)
156
+ }
127
157
 
128
158
  return await sendInvitationEmail({
129
159
  invitation,
160
+ user,
130
161
  context
131
162
  })
132
163
  }
@@ -1,3 +1,5 @@
1
+ import { ILike } from 'typeorm'
2
+
1
3
  import { getRepository } from '@things-factory/shell'
2
4
 
3
5
  import { USER_NOT_FOUND } from '../constants/error-code'
@@ -13,14 +15,39 @@ export async function updateProfile({ id }, newProfiles) {
13
15
  })
14
16
  }
15
17
 
16
- /* only 'name', 'email' and 'locale' attributes can be changed */
17
- var allowed = ['name', 'email', 'locale']
18
+ /* only 'username', 'name', 'email' and 'locale' attributes can be changed */
19
+ var allowed: {
20
+ username?: string
21
+ name?: string
22
+ email?: string
23
+ locale?: string
24
+ } = ['username', 'name', 'email', 'locale']
18
25
  .filter(attr => attr in newProfiles)
19
26
  .reduce((sum, attr) => {
20
27
  sum[attr] = newProfiles[attr]
21
28
  return sum
22
29
  }, {})
23
30
 
31
+ /* check if email and username is unique */
32
+ if ('email' in allowed) {
33
+ var found: User = await repository.findOne({ where: { email: ILike(allowed.email) } })
34
+
35
+ if (found && found.id != id) {
36
+ throw new AuthError({
37
+ errorCode: AuthError.ERROR_CODES.EMAIL_ALREADY_EXISTS
38
+ })
39
+ }
40
+ }
41
+
42
+ if ('username' in allowed) {
43
+ var found: User = await repository.findOne({ where: { username: allowed.username } })
44
+ if (found && found.id != id) {
45
+ throw new AuthError({
46
+ errorCode: AuthError.ERROR_CODES.USERNAME_ALREADY_EXISTS
47
+ })
48
+ }
49
+ }
50
+
24
51
  return await repository.save({
25
52
  ...user,
26
53
  ...allowed
@@ -5,11 +5,26 @@ import { sendUnlockUserEmail } from '../controllers/unlock-user'
5
5
  import { AuthError } from '../errors/auth-error'
6
6
  import { User, UserStatus } from '../service/user/user'
7
7
 
8
- export async function signin(attrs, context?) {
8
+ export async function signin(attrs: { username: string; password: string }, context?) {
9
9
  const { domain } = context?.state || {}
10
+ const { username } = attrs
10
11
 
11
12
  const repository = getRepository(User)
12
- const user = await repository.findOne({ where: { email: ILike(attrs.email) }, relations: ['domains'] })
13
+
14
+ var user = await repository.findOne({
15
+ where: { username },
16
+ relations: ['domains']
17
+ })
18
+
19
+ if (!user && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
20
+ user = await repository.findOne({
21
+ where: {
22
+ email: ILike(username)
23
+ },
24
+ relations: ['domains']
25
+ })
26
+ }
27
+
13
28
  if (!user)
14
29
  throw new AuthError({
15
30
  errorCode: AuthError.ERROR_CODES.USER_NOT_FOUND
@@ -29,6 +44,7 @@ export async function signin(attrs, context?) {
29
44
  throw new AuthError({
30
45
  errorCode: AuthError.ERROR_CODES.USER_LOCKED,
31
46
  detail: {
47
+ username: user.username,
32
48
  email: user.email
33
49
  }
34
50
  })
@@ -46,6 +62,7 @@ export async function signin(attrs, context?) {
46
62
  throw new AuthError({
47
63
  errorCode: AuthError.ERROR_CODES.USER_LOCKED,
48
64
  detail: {
65
+ username: user.username,
49
66
  email: user.email
50
67
  }
51
68
  })
@@ -53,6 +70,7 @@ export async function signin(attrs, context?) {
53
70
  throw new AuthError({
54
71
  errorCode: AuthError.ERROR_CODES.PASSWORD_NOT_MATCHED,
55
72
  detail: {
73
+ username: user.username,
56
74
  email: user.email,
57
75
  failCount: user.failCount
58
76
  }
@@ -66,6 +84,7 @@ export async function signin(attrs, context?) {
66
84
  throw new AuthError({
67
85
  errorCode: AuthError.ERROR_CODES.USER_NOT_ACTIVATED,
68
86
  detail: {
87
+ username: user.username,
69
88
  email: user.email
70
89
  }
71
90
  })
@@ -8,19 +8,31 @@ import { signin } from './signin'
8
8
  import { sendVerificationEmail } from './verification'
9
9
 
10
10
  export async function signup(attrs, withEmailVerification?: Boolean) {
11
- const { name, email, password, domain, context } = attrs
11
+ const { name, username, password, domain, context } = attrs
12
12
 
13
13
  /* check if password is following the rule */
14
14
  User.validatePasswordByRule(password, context.lng)
15
15
 
16
16
  const repository = getRepository(User)
17
- const duplicated = await repository.findOneBy({ email: ILike(email) })
17
+
18
+ var duplicated = await repository.findOne({
19
+ where: { username },
20
+ relations: ['domains']
21
+ })
22
+
23
+ if (!duplicated && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
24
+ user = await repository.findOne({
25
+ where: { email: ILike(username) },
26
+ relations: ['domains']
27
+ })
28
+ }
29
+
18
30
  if (duplicated) {
19
31
  throw new AuthError({
20
32
  errorCode: USER_DUPLICATED,
21
33
  detail: {
22
34
  name,
23
- email
35
+ username
24
36
  }
25
37
  })
26
38
  }
@@ -48,7 +60,7 @@ export async function signup(attrs, withEmailVerification?: Boolean) {
48
60
  return {
49
61
  token: await signin(
50
62
  {
51
- email,
63
+ username,
52
64
  password
53
65
  },
54
66
  { domain }
@@ -20,6 +20,7 @@ export async function sendUnlockUserEmail({ user, context }) {
20
20
  receiver: user.email,
21
21
  subject: 'Your account is locked',
22
22
  content: getUnlockUserEmailForm({
23
+ username: user.username,
23
24
  name: user.name,
24
25
  resetUrl: serviceUrl
25
26
  })
@@ -21,6 +21,7 @@ export async function sendVerificationEmail({ user, context }) {
21
21
  receiver: user.email,
22
22
  subject: 'Verify your email',
23
23
  content: getVerificationEmailForm({
24
+ username: user.username,
24
25
  name: user.name,
25
26
  verifyUrl: serviceUrl
26
27
  })
package/server/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from './utils/check-user-belongs-domain'
17
17
  export * from './utils/access-token-cookie'
18
18
  export * from './utils/encrypt-state'
19
19
  export * from './utils/check-permission'
20
+ export * from './utils/check-user-has-role'
20
21
 
21
22
  export * from './errors'
22
23
 
@@ -7,17 +7,17 @@ passport.use(
7
7
  'signin',
8
8
  new localStrategy(
9
9
  {
10
- usernameField: 'email',
10
+ usernameField: 'username',
11
11
  passwordField: 'password'
12
12
  },
13
- async (email, password, done) => {
13
+ async (username, password, done) => {
14
14
  try {
15
15
  const {
16
16
  user: userInfo,
17
17
  token,
18
18
  domains
19
19
  } = await signin({
20
- email,
20
+ username,
21
21
  password
22
22
  })
23
23
 
@@ -3,7 +3,6 @@ import { Strategy as CustomStrategy } from 'passport-custom'
3
3
 
4
4
  import { getRepository } from '@things-factory/shell'
5
5
 
6
- import { User } from '../service/user/user'
7
6
  import { AuthError } from '../errors/auth-error'
8
7
 
9
8
  import { WebAuthCredential } from '../service/web-auth-credential/web-auth-credential'
@@ -57,23 +56,23 @@ passport.use(
57
56
  const { body, session, origin, hostname } = context as any
58
57
 
59
58
  const challenge = session.challenge
60
-
59
+
61
60
  const assertionResponse = body as {
62
61
  id: string
63
62
  response: AuthenticatorAssertionResponse
64
63
  }
65
-
64
+
66
65
  const credential = await getRepository(WebAuthCredential).findOne({
67
66
  where: {
68
67
  credentialId: assertionResponse.id
69
68
  },
70
69
  relations: ['user']
71
70
  })
72
-
71
+
73
72
  if (!credential) {
74
73
  return done(null, false)
75
74
  }
76
-
75
+
77
76
  const verification = await verifyAuthenticationResponse({
78
77
  response: body,
79
78
  expectedChallenge: challenge,
@@ -86,18 +85,18 @@ passport.use(
86
85
  counter: credential.counter
87
86
  }
88
87
  })
89
-
88
+
90
89
  if (verification.verified) {
91
90
  const { authenticationInfo } = verification
92
91
  credential.counter = authenticationInfo.newCounter
93
92
  await getRepository(WebAuthCredential).save(credential)
94
-
93
+
95
94
  const user = credential.user
96
95
  return done(null, user)
97
96
  } else {
98
97
  return done(verification, false)
99
98
  }
100
- } catch(error) {
99
+ } catch (error) {
101
100
  return done(error, false)
102
101
  }
103
102
  })
@@ -6,6 +6,7 @@ import { Domain, getRepository } from '@things-factory/shell'
6
6
  import { User, UserStatus } from '../service/user/user'
7
7
 
8
8
  const ADMIN_ACCOUNT = config.get('adminAccount', {
9
+ username: 'admin',
9
10
  name: 'Admin',
10
11
  email: 'admin@hatiolab.com',
11
12
  password: 'admin'
@@ -42,7 +43,7 @@ export class SeedUsers1548206416130 implements MigrationInterface {
42
43
  logger.error(e)
43
44
  }
44
45
 
45
- const admin = await userRepository.findOne({ where: { email: ILike('admin@hatiolab.com') } })
46
+ const admin = await userRepository.findOne({ where: { email: ILike(ADMIN_ACCOUNT.email) } })
46
47
  domain.owner = admin.id
47
48
 
48
49
  await domainRepository.save(domain)
@@ -63,7 +63,13 @@ authCheckinRouter.get('/auth/checkin/:subdomain?', async (context, next) => {
63
63
  pageElement: 'auth-checkin',
64
64
  elementScript: '/auth/checkin.js',
65
65
  data: {
66
- user: { email: user.email, locale: user.locale, name: user.name, userType: user.userType },
66
+ user: {
67
+ username: user.username,
68
+ email: user.email,
69
+ locale: user.locale,
70
+ name: user.name,
71
+ userType: user.userType
72
+ },
67
73
  domains,
68
74
  domainType,
69
75
  redirectTo,
@@ -73,7 +79,7 @@ authCheckinRouter.get('/auth/checkin/:subdomain?', async (context, next) => {
73
79
  } catch (e) {
74
80
  clearAccessTokenCookie(context)
75
81
  context.redirect(
76
- `/auth/signin?email=${encodeURIComponent(user.email)}&redirect_to=${encodeURIComponent(redirectTo)}`
82
+ `/auth/signin?username=${encodeURIComponent(user.username)}&redirect_to=${encodeURIComponent(redirectTo)}`
77
83
  )
78
84
  }
79
85
  }
@@ -95,9 +101,9 @@ async function checkIn(
95
101
  context: ResolverContext
96
102
  ): Promise<void> {
97
103
  const { user }: { user: User } = context.state
98
- const remoteAddress = context.req.headers['x-forwarded-for']
99
- ? (context.req.headers['x-forwarded-for'] as string).split(',')[0].trim()
100
- : context.req.connection.remoteAddress
104
+ const remoteAddress = context.req.headers['x-forwarded-for']
105
+ ? (context.req.headers['x-forwarded-for'] as string).split(',')[0].trim()
106
+ : context.req.connection.remoteAddress
101
107
 
102
108
  await LoginHistory.stamp(checkInDomain, user, remoteAddress)
103
109
 
@@ -43,19 +43,25 @@ authPrivateProcessRouter
43
43
  .post('/delete-user', async (context, next) => {
44
44
  const { t, session } = context
45
45
  var { user } = context.state
46
- var { email: userEmail } = user
46
+ var { id: userId } = user
47
47
 
48
- var { password, email } = context.request.body
48
+ var { password, username } = context.request.body
49
49
 
50
50
  const userRepo = getRepository(User)
51
- const userInfo = await userRepo.findOne({
52
- where: {
53
- email: ILike(userEmail)
54
- },
51
+
52
+ var userInfo = await userRepo.findOne({
53
+ where: { username },
55
54
  relations: ['domains']
56
55
  })
57
56
 
58
- if (email != userEmail || !User.verify(userInfo.password, password, userInfo.salt)) {
57
+ if (!userInfo && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username)) {
58
+ userInfo = await userRepo.findOne({
59
+ where: { email: ILike(username) },
60
+ relations: ['domains']
61
+ })
62
+ }
63
+
64
+ if (userInfo.id != userId || !User.verify(userInfo.password, password, userInfo.salt)) {
59
65
  context.status = 401
60
66
  context.body = t('error.user validation failed')
61
67
  return
@@ -89,6 +95,7 @@ authPrivateProcessRouter
89
95
 
90
96
  context.body = {
91
97
  user: {
98
+ username: user.username,
92
99
  email: user.email,
93
100
  name: user.name,
94
101
  userType: user.userType,