@semapps/auth 0.4.0-alpha.19 → 0.4.0-alpha.21
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.
- package/index.js +2 -1
- package/package.json +6 -4
- package/services/account.js +43 -0
- package/services/auth.local.js +64 -4
- package/services/mail.js +49 -0
- package/templates/reset-password/en-EN/subject.hbs +1 -0
- package/templates/reset-password/en-EN/text.hbs +7 -0
- package/templates/reset-password/fr-FR/subject.hbs +1 -0
- package/templates/reset-password/fr-FR/text.hbs +7 -0
package/index.js
CHANGED
@@ -4,5 +4,6 @@ module.exports = {
|
|
4
4
|
AuthOIDCService: require('./services/auth.oidc'),
|
5
5
|
AuthAccountService: require('./services/account'),
|
6
6
|
AuthJWTService: require('./services/jwt'),
|
7
|
-
AuthMigrationService: require('./services/migration')
|
7
|
+
AuthMigrationService: require('./services/migration'),
|
8
|
+
AuthMailService: require('./services/mail')
|
8
9
|
};
|
package/package.json
CHANGED
@@ -1,26 +1,28 @@
|
|
1
1
|
{
|
2
2
|
"name": "@semapps/auth",
|
3
|
-
"version": "0.4.0-alpha.
|
3
|
+
"version": "0.4.0-alpha.21",
|
4
4
|
"description": "Authentification module for SemApps",
|
5
5
|
"license": "Apache-2.0",
|
6
6
|
"author": "Virtual Assembly",
|
7
7
|
"dependencies": {
|
8
|
-
"@semapps/mime-types": "0.4.0-alpha.
|
9
|
-
"@semapps/triplestore": "0.4.0-alpha.
|
8
|
+
"@semapps/mime-types": "0.4.0-alpha.21",
|
9
|
+
"@semapps/triplestore": "0.4.0-alpha.21",
|
10
10
|
"bcrypt": "^5.0.1",
|
11
11
|
"express-session": "^1.17.0",
|
12
12
|
"jsonwebtoken": "^8.5.1",
|
13
13
|
"moleculer": "^0.14.17",
|
14
14
|
"moleculer-db": "^0.8.16",
|
15
15
|
"moleculer-web": "^0.10.0-beta1",
|
16
|
+
"node-sass": "^7.0.1",
|
16
17
|
"openid-client": "^4.7.4",
|
17
18
|
"passport": "^0.4.1",
|
18
19
|
"passport-cas2": "0.0.12",
|
19
20
|
"passport-local": "^1.0.0",
|
21
|
+
"pug": "^3.0.2",
|
20
22
|
"url-join": "^4.0.1"
|
21
23
|
},
|
22
24
|
"publishConfig": {
|
23
25
|
"access": "public"
|
24
26
|
},
|
25
|
-
"gitHead": "
|
27
|
+
"gitHead": "03a76d610ac2abb85d8889afcdaa15fceab5039f"
|
26
28
|
}
|
package/services/account.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
const bcrypt = require('bcrypt');
|
2
2
|
const DbService = require('moleculer-db');
|
3
3
|
const { TripleStoreAdapter } = require('@semapps/triplestore');
|
4
|
+
const crypto = require('crypto');
|
4
5
|
|
5
6
|
module.exports = {
|
6
7
|
name: 'auth.account',
|
@@ -88,6 +89,11 @@ module.exports = {
|
|
88
89
|
const accounts = await this._find(ctx, { query: { webId } });
|
89
90
|
return accounts.length > 0 ? accounts[0] : null;
|
90
91
|
},
|
92
|
+
async findByEmail(ctx) {
|
93
|
+
const { email } = ctx.params;
|
94
|
+
const accounts = await this._find(ctx, { query: { email } });
|
95
|
+
return accounts.length > 0 ? accounts[0] : null;
|
96
|
+
},
|
91
97
|
async setPassword(ctx) {
|
92
98
|
const { webId, password } = ctx.params;
|
93
99
|
const hashedPassword = await this.hashPassword(password);
|
@@ -97,6 +103,33 @@ module.exports = {
|
|
97
103
|
'@id': account['@id'],
|
98
104
|
hashedPassword
|
99
105
|
});
|
106
|
+
},
|
107
|
+
async setNewPassword(ctx) {
|
108
|
+
const { webId, token, password } = ctx.params;
|
109
|
+
const hashedPassword = await this.hashPassword(password);
|
110
|
+
const account = await ctx.call('auth.account.findByWebId', { webId });
|
111
|
+
|
112
|
+
if (account.resetPasswordToken !== token) {
|
113
|
+
throw new Error('auth.password.invalid_reset_token');
|
114
|
+
}
|
115
|
+
|
116
|
+
return await this._update(ctx, {
|
117
|
+
'@id': account['@id'],
|
118
|
+
hashedPassword,
|
119
|
+
resetPasswordToken: undefined
|
120
|
+
});
|
121
|
+
},
|
122
|
+
async generateResetPasswordToken(ctx) {
|
123
|
+
const { webId } = ctx.params;
|
124
|
+
const resetPasswordToken = await this.generateResetPasswordToken();
|
125
|
+
const account = await ctx.call('auth.account.findByWebId', { webId });
|
126
|
+
|
127
|
+
await this._update(ctx, {
|
128
|
+
'@id': account['@id'],
|
129
|
+
resetPasswordToken
|
130
|
+
});
|
131
|
+
|
132
|
+
return resetPasswordToken;
|
100
133
|
}
|
101
134
|
},
|
102
135
|
methods: {
|
@@ -138,6 +171,16 @@ module.exports = {
|
|
138
171
|
}
|
139
172
|
});
|
140
173
|
});
|
174
|
+
},
|
175
|
+
async generateResetPasswordToken() {
|
176
|
+
return new Promise(resolve => {
|
177
|
+
crypto.randomBytes(32, function(ex, buf) {
|
178
|
+
if (ex) {
|
179
|
+
reject(ex);
|
180
|
+
}
|
181
|
+
resolve(buf.toString('hex'));
|
182
|
+
});
|
183
|
+
});
|
141
184
|
}
|
142
185
|
}
|
143
186
|
};
|
package/services/auth.local.js
CHANGED
@@ -2,6 +2,7 @@ const { Strategy } = require('passport-local');
|
|
2
2
|
const AuthMixin = require('../mixins/auth');
|
3
3
|
const sendToken = require('../middlewares/sendToken');
|
4
4
|
const { MoleculerError } = require('moleculer').Errors;
|
5
|
+
const AuthMailService = require('../services/mail');
|
5
6
|
|
6
7
|
const AuthLocalService = {
|
7
8
|
name: 'auth',
|
@@ -12,10 +13,29 @@ const AuthLocalService = {
|
|
12
13
|
registrationAllowed: true,
|
13
14
|
reservedUsernames: [],
|
14
15
|
webIdSelection: [],
|
15
|
-
accountSelection: []
|
16
|
+
accountSelection: [],
|
17
|
+
mail: {
|
18
|
+
from: null,
|
19
|
+
transport: {
|
20
|
+
host: null,
|
21
|
+
port: null
|
22
|
+
},
|
23
|
+
defaults: {
|
24
|
+
locale: null,
|
25
|
+
frontUrl: null
|
26
|
+
}
|
27
|
+
}
|
16
28
|
},
|
17
|
-
created() {
|
29
|
+
async created() {
|
30
|
+
const { mail } = this.settings;
|
31
|
+
|
18
32
|
this.passportId = 'local';
|
33
|
+
|
34
|
+
await this.broker.createService(AuthMailService, {
|
35
|
+
settings: {
|
36
|
+
...mail
|
37
|
+
}
|
38
|
+
});
|
19
39
|
},
|
20
40
|
actions: {
|
21
41
|
async signup(ctx) {
|
@@ -50,6 +70,33 @@ const AuthLocalService = {
|
|
50
70
|
const token = await ctx.call('auth.jwt.generateToken', { payload: { webId: accountData.webId } });
|
51
71
|
|
52
72
|
return { token, webId: accountData.webId, newUser: true };
|
73
|
+
},
|
74
|
+
async resetPassword(ctx) {
|
75
|
+
const { email } = ctx.params;
|
76
|
+
|
77
|
+
const account = await ctx.call('auth.account.findByEmail', { email });
|
78
|
+
|
79
|
+
if (!account) {
|
80
|
+
throw new Error('email.not.exists');
|
81
|
+
}
|
82
|
+
|
83
|
+
const token = await ctx.call('auth.account.generateResetPasswordToken', { webId: account.webId });
|
84
|
+
|
85
|
+
await ctx.call('auth.mail.sendResetPasswordEmail', {
|
86
|
+
account,
|
87
|
+
token
|
88
|
+
});
|
89
|
+
},
|
90
|
+
async setNewPassword(ctx) {
|
91
|
+
const { email, token, password } = ctx.params;
|
92
|
+
|
93
|
+
const account = await ctx.call('auth.account.findByEmail', { email });
|
94
|
+
|
95
|
+
if (!account) {
|
96
|
+
throw new Error('email.not.exists');
|
97
|
+
}
|
98
|
+
|
99
|
+
await ctx.call('auth.account.setNewPassword', { webId: account.webId, token, password });
|
53
100
|
}
|
54
101
|
},
|
55
102
|
methods: {
|
@@ -89,10 +136,23 @@ const AuthLocalService = {
|
|
89
136
|
}
|
90
137
|
};
|
91
138
|
|
139
|
+
const resetPasswordRoute = {
|
140
|
+
path: '/auth/reset_password',
|
141
|
+
aliases: {
|
142
|
+
'POST /': 'auth.resetPassword'
|
143
|
+
}
|
144
|
+
};
|
145
|
+
const setNewPasswordRoute = {
|
146
|
+
path: '/auth/new_password',
|
147
|
+
aliases: {
|
148
|
+
'POST /': 'auth.setNewPassword'
|
149
|
+
}
|
150
|
+
};
|
151
|
+
|
92
152
|
if (this.settings.registrationAllowed) {
|
93
|
-
return [loginRoute, signupRoute];
|
153
|
+
return [loginRoute, signupRoute, resetPasswordRoute, setNewPasswordRoute];
|
94
154
|
} else {
|
95
|
-
return [loginRoute];
|
155
|
+
return [loginRoute, resetPasswordRoute, setNewPasswordRoute];
|
96
156
|
}
|
97
157
|
}
|
98
158
|
}
|
package/services/mail.js
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
const MailService = require('moleculer-mail');
|
2
|
+
const path = require('path');
|
3
|
+
|
4
|
+
module.exports = {
|
5
|
+
name: 'auth.mail',
|
6
|
+
mixins: [MailService],
|
7
|
+
settings: {
|
8
|
+
defaults: {
|
9
|
+
locale: 'en',
|
10
|
+
frontUrl: null
|
11
|
+
},
|
12
|
+
templateFolder: path.join(__dirname, '../templates'),
|
13
|
+
from: null,
|
14
|
+
transport: null
|
15
|
+
},
|
16
|
+
actions: {
|
17
|
+
async sendResetPasswordEmail(ctx) {
|
18
|
+
const { account, token } = ctx.params;
|
19
|
+
|
20
|
+
await this.actions.send(
|
21
|
+
{
|
22
|
+
to: account.email,
|
23
|
+
template: 'reset-password',
|
24
|
+
locale: this.getTemplateLocale(account.preferredLocale || this.settings.defaults.locale),
|
25
|
+
data: {
|
26
|
+
account,
|
27
|
+
token,
|
28
|
+
frontUrl: account.preferredFrontUrl || this.settings.defaults.frontUrl
|
29
|
+
}
|
30
|
+
},
|
31
|
+
{
|
32
|
+
parentCtx: ctx
|
33
|
+
}
|
34
|
+
);
|
35
|
+
}
|
36
|
+
},
|
37
|
+
methods: {
|
38
|
+
getTemplateLocale(userLocale) {
|
39
|
+
switch (userLocale) {
|
40
|
+
case 'fr':
|
41
|
+
return 'fr-FR';
|
42
|
+
case 'en':
|
43
|
+
return 'en-EN';
|
44
|
+
default:
|
45
|
+
return 'en-EN';
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
Password reset for {{account.username}} account
|
@@ -0,0 +1 @@
|
|
1
|
+
Réinitialisation du mot de passe du compte {{account.username}}
|