pico-auth 0.0.32 → 0.0.34

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.
@@ -2,6 +2,7 @@ declare module "core/auth" {
2
2
  export interface JWTSpecs {
3
3
  secretKey: string;
4
4
  expiryTimeMs: any;
5
+ refreshExpiryTimeMs?: any;
5
6
  }
6
7
  export interface UserProvider {
7
8
  getUser(login: string): Promise<BaseUser>;
@@ -23,10 +24,17 @@ declare module "core/auth" {
23
24
  blocked?: boolean;
24
25
  [key: string]: any;
25
26
  }
27
+ export const issueJwtToken: (user: BaseUser, userProvider: UserProvider, jwtSpecs: JWTSpecs, issueRefreshToken: boolean) => Promise<{
28
+ token: any;
29
+ clearedUser: BaseUser;
30
+ }>;
26
31
  /**
27
32
  * When mfaToken is provided
28
33
  */
29
- export const authenticate: (login: string, password: string, mfaToken: string, impersonateEntity: string, userProvider: UserProvider, impersonateProvider: any, jwtSpecs: JWTSpecs) => Promise<any>;
34
+ export const authenticate: (login: string, password: string, mfaToken: string, impersonateEntity: string, userProvider: UserProvider, impersonateProvider: any, jwtSpecs: JWTSpecs) => Promise<{
35
+ token: any;
36
+ refreshToken: any;
37
+ }>;
30
38
  /**
31
39
  * Allows to login using scratch card. As scratch card may provide a different user to log in to
32
40
  * it also allows to impersonate someone using scratch card.
@@ -39,8 +47,21 @@ declare module "core/auth" {
39
47
  */
40
48
  export const authenticateWithScratchCard: (cardCode: string, userProvider: UserProvider, scratchCardProvider: ScratchCardProvider, jwtSpecs: JWTSpecs, requesterLogin?: string) => Promise<{
41
49
  token: any;
50
+ refreshToken: any;
42
51
  user: any;
43
52
  }>;
53
+ /**
54
+ * When new short lived token is requested
55
+ * Provides longer lived refresh token to obtain new short lived token
56
+ * @param login
57
+ * @param refreshToken
58
+ * @param userProvider
59
+ * @param jwtSpecs
60
+ * @returns short lived token
61
+ */
62
+ export const refreshToken: (login: string, refreshToken: string, userProvider: UserProvider, jwtSpecs: JWTSpecs) => Promise<{
63
+ token: any;
64
+ }>;
44
65
  /**
45
66
  * Will prepare user for MFA activation. Next step is to call verify with token generated in MFA app by the user.
46
67
  */
@@ -52,6 +73,6 @@ declare module "core/auth" {
52
73
  export const mfaEnabled: (login: string, userProvider: UserProvider) => Promise<any>;
53
74
  }
54
75
  declare module "pico-auth" {
55
- export { mfaRegister, mfaVerify, mfaEnabled, authenticate, authenticateWithScratchCard } from "core/auth";
76
+ export { mfaRegister, mfaVerify, mfaEnabled, authenticate, authenticateWithScratchCard, refreshToken, issueJwtToken } from "core/auth";
56
77
  export type { UserProvider, ImpersonateProvider, JWTSpecs, ScratchCardProvider, BaseUser } from "core/auth";
57
78
  }
@@ -2,6 +2,26 @@ const speakeasy = require('speakeasy');
2
2
  const qrcode = require('qrcode');
3
3
  const md5 = require("md5");
4
4
  const jwt = require('jsonwebtoken');
5
+ const issueJwtToken = async (user, userProvider, jwtSpecs, issueRefreshToken) => {
6
+ let jwtSecretKey = jwtSpecs.secretKey;
7
+ let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(user) : user;
8
+ clearedUser = userProvider.getUserPostAuthenticate ? await userProvider.getUserPostAuthenticate(clearedUser) : clearedUser;
9
+ let data = {
10
+ time: Date.now(),
11
+ user: clearedUser
12
+ };
13
+ let token;
14
+ if (issueRefreshToken && jwtSpecs.refreshExpiryTimeMs) {
15
+ token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.refreshExpiryTimeMs });
16
+ }
17
+ else if (jwtSpecs.expiryTimeMs) {
18
+ token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
19
+ }
20
+ return {
21
+ token,
22
+ clearedUser
23
+ };
24
+ };
5
25
  /**
6
26
  * When mfaToken is provided
7
27
  */
@@ -66,18 +86,15 @@ const authenticate = async (login, password, mfaToken, impersonateEntity, userPr
66
86
  }
67
87
  console.info(`Impersonate success. From: ${originalUser.login} into ${target}`);
68
88
  }
69
- // let jwtSecretKey = process.env.JWT_SECRET_KEY;
70
- let jwtSecretKey = jwtSpecs.secretKey;
71
- let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(user) : user;
72
- clearedUser = userProvider.getUserPostAuthenticate ? await userProvider.getUserPostAuthenticate(clearedUser) : clearedUser;
73
- let data = {
74
- time: Date.now(),
75
- user: clearedUser
76
- };
77
- // const token = jwt.sign(data, jwtSecretKey, {expiresIn: process.env.JWT_EXPIRY_TIME});
78
- const token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
89
+ const token = (await issueJwtToken(user, userProvider, jwtSpecs, false)).token;
90
+ let refreshToken;
91
+ if (jwtSpecs.refreshExpiryTimeMs)
92
+ refreshToken = (await issueJwtToken(user, userProvider, jwtSpecs, true)).token;
79
93
  console.log(`Successful login: ${user.id}`);
80
- return token;
94
+ return {
95
+ token,
96
+ refreshToken
97
+ };
81
98
  }
82
99
  else {
83
100
  throw new Error(`Failed authentication attempt ${login}`);
@@ -115,19 +132,15 @@ const authenticateWithScratchCard = async (cardCode, userProvider, scratchCardPr
115
132
  throw new Error(`Failed card authentication attempt ${requesterLogin} (Blocked as Target)`);
116
133
  // ok so we will use targetUser as a user that will be actually logged in
117
134
  // in impersonation scenario targetUser may be different then the user.
118
- let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(targetUser) : targetUser;
119
- clearedUser = userProvider.getUserPostAuthenticate ? await userProvider.getUserPostAuthenticate(clearedUser) : clearedUser;
120
- let jwtSecretKey = jwtSpecs.secretKey;
121
- let data = {
122
- time: Date.now(),
123
- user: clearedUser
124
- };
125
- // const token = jwt.sign(data, jwtSecretKey, {expiresIn: process.env.JWT_EXPIRY_TIME});
126
- const token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
135
+ const token = (await issueJwtToken(targetUser, userProvider, jwtSpecs, false)).token;
136
+ let refreshToken;
137
+ if (jwtSpecs.refreshExpiryTimeMs)
138
+ refreshToken = (await issueJwtToken(targetUser, userProvider, jwtSpecs, true)).token;
127
139
  console.info(`Card authentication success. Requester:${requesterLogin} Target:${targetUser.id}`);
128
140
  return {
129
141
  token,
130
- user: clearedUser // just in case its impersonation so the actual resultin user will be different that the requester login user
142
+ refreshToken,
143
+ user: token.clearedUser // just in case its impersonation so the actual resulting user will be different that the requester login user
131
144
  };
132
145
  }
133
146
  catch (error) {
@@ -135,6 +148,37 @@ const authenticateWithScratchCard = async (cardCode, userProvider, scratchCardPr
135
148
  throw new Error(`Failed card authentication attempt ${requesterLogin}`);
136
149
  }
137
150
  };
151
+ /**
152
+ * When new short lived token is requested
153
+ * Provides longer lived refresh token to obtain new short lived token
154
+ * @param login
155
+ * @param refreshToken
156
+ * @param userProvider
157
+ * @param jwtSpecs
158
+ * @returns short lived token
159
+ */
160
+ const refreshToken = async (login, refreshToken, userProvider, jwtSpecs) => {
161
+ let user = await userProvider.getUser(login);
162
+ if (user.blocked)
163
+ throw new Error(`Failed refresh token attempt ${login} (Blocked)`);
164
+ // validate refresh token
165
+ if (refreshToken) {
166
+ // let jwtSecretKey = process.env.JWT_SECRET_KEY;
167
+ let jwtSecretKey = jwtSpecs.secretKey;
168
+ var decoded = jwt.verify(refreshToken, jwtSecretKey);
169
+ // put decoded auth context into request
170
+ const refreshTokenUser = decoded.user;
171
+ if (!refreshTokenUser || refreshTokenUser.id != user.id) {
172
+ throw new Error(`Failed refresh token attempt ${login} (Invalid Token)`);
173
+ }
174
+ const token = (await issueJwtToken(user, userProvider, jwtSpecs, false)).token;
175
+ console.log(`Successful token refresh: ${user.id}`);
176
+ return { token };
177
+ }
178
+ else {
179
+ throw new Error(`Failed refresh token attempt ${login}`);
180
+ }
181
+ };
138
182
  /**
139
183
  * Will prepare user for MFA activation. Next step is to call verify with token generated in MFA app by the user.
140
184
  */
@@ -209,4 +253,4 @@ const mfaEnabled = async (login, userProvider) => {
209
253
  return (mfaInfo === null || mfaInfo === void 0 ? void 0 : mfaInfo.enabled) || false;
210
254
  };
211
255
 
212
- export { authenticate, authenticateWithScratchCard, mfaEnabled, mfaRegister, mfaVerify };
256
+ export { authenticate, authenticateWithScratchCard, issueJwtToken, mfaEnabled, mfaRegister, mfaVerify, refreshToken };
@@ -1 +1 @@
1
- const speakeasy=require("speakeasy"),qrcode=require("qrcode"),md5=require("md5"),jwt=require("jsonwebtoken"),authenticate=async(e,t,a,r,i,o,s)=>{var n;let c=await i.getUser(e);const d=i.userSecretPath?c[i.userSecretPath]:c.mfa,l=i.userPasswordPath?c[i.userPasswordPath]:c.password;if(null==d?void 0:d.enabled){if(!speakeasy.totp.verify({secret:null===(n=null==d?void 0:d.secret)||void 0===n?void 0:n.actual,encoding:"base32",token:a,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(c.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(md5(t||"")==l){const e=r,t=c;if(e){let a=!1;if(e.startsWith("@")){if(a=a||await o.canImpersonate(c,e),!a)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await o.impersonateOrg(c,e)}else{const r=await i.getUser(e);if(a=a||await o.canImpersonate(c,e),!a)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);c=r}console.info(`Impersonate success. From: ${t.login} into ${e}`)}let a=s.secretKey,n=i.getSafeUser?await i.getSafeUser(c):c;n=i.getUserPostAuthenticate?await i.getUserPostAuthenticate(n):n;let d={time:Date.now(),user:n};const l=jwt.sign(d,a,{expiresIn:s.expiryTimeMs});return console.log(`Successful login: ${c.id}`),l}throw new Error(`Failed authentication attempt ${e}`)},authenticateWithScratchCard=async(e,t,a,r,i)=>{let o,s=i?await t.getUser(i):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked)`);if(i&&!s)throw new Error(`Failed card authentication attempt ${i} (Missing user)`);try{o=await a.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`)}try{if(!o)throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`);if(o.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked as Target)`);let e=t.getSafeUser?await t.getSafeUser(o):o;e=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(e):e;let a=r.secretKey,s={time:Date.now(),user:e};const n=jwt.sign(s,a,{expiresIn:r.expiryTimeMs});return console.info(`Card authentication success. Requester:${i} Target:${o.id}`),{token:n,user:e}}catch(e){throw new Error(`Failed card authentication attempt ${i}`)}},mfaRegister=async(e,t,a)=>new Promise(async(r,i)=>{let o=await a.getUser(t),s=a.userSecretPath?o[a.userSecretPath]:o.mfa;const n=speakeasy.generateSecret({name:`${e}: ${t}`});if(!s){s={secret:{temp:void 0,actual:void 0},enabled:!1};o[a.userSecretPath?a.userSecretPath:"mfa"]=s}s.secret.temp=n.base32,s.secret.actual=void 0,await a.putUser(o),qrcode.toDataURL(n.otpauth_url,(e,t)=>{if(e)throw new Error("Error generating QR code");r({qr_code:t,secret:n.base32})})}),mfaVerify=async(e,t,a)=>{var r,i;const o=t;let s=await a.getUser(e);const n=a.userSecretPath?s[a.userSecretPath]:s.mfa;return speakeasy.totp.verify({secret:null===(r=null==n?void 0:n.secret)||void 0===r?void 0:r.temp,encoding:"base32",token:o})?(n.secret.actual=null===(i=null==n?void 0:n.secret)||void 0===i?void 0:i.temp,n.enabled=!0,await a.putUser(s),!0):(console.log(`Failed mfa verification for ${e}`),!1)},mfaEnabled=async(e,t)=>{let a=await t.getUser(e);const r=t.userSecretPath?a[t.userSecretPath]:a.mfa;return(null==r?void 0:r.enabled)||!1};export{authenticate,authenticateWithScratchCard,mfaEnabled,mfaRegister,mfaVerify};
1
+ const speakeasy=require("speakeasy"),qrcode=require("qrcode"),md5=require("md5"),jwt=require("jsonwebtoken"),issueJwtToken=async(e,t,r,a)=>{let i=r.secretKey,o=t.getSafeUser?await t.getSafeUser(e):e;o=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(o):o;let s,n={time:Date.now(),user:o};return a&&r.refreshExpiryTimeMs?s=jwt.sign(n,i,{expiresIn:r.refreshExpiryTimeMs}):r.expiryTimeMs&&(s=jwt.sign(n,i,{expiresIn:r.expiryTimeMs})),{token:s,clearedUser:o}},authenticate=async(e,t,r,a,i,o,s)=>{var n;let c=await i.getUser(e);const l=i.userSecretPath?c[i.userSecretPath]:c.mfa,d=i.userPasswordPath?c[i.userPasswordPath]:c.password;if(null==l?void 0:l.enabled){if(!speakeasy.totp.verify({secret:null===(n=null==l?void 0:l.secret)||void 0===n?void 0:n.actual,encoding:"base32",token:r,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(c.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(md5(t||"")==d){const e=a,t=c;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await o.canImpersonate(c,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await o.impersonateOrg(c,e)}else{const a=await i.getUser(e);if(r=r||await o.canImpersonate(c,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);c=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}const r=(await issueJwtToken(c,i,s,!1)).token;let n;return s.refreshExpiryTimeMs&&(n=(await issueJwtToken(c,i,s,!0)).token),console.log(`Successful login: ${c.id}`),{token:r,refreshToken:n}}throw new Error(`Failed authentication attempt ${e}`)},authenticateWithScratchCard=async(e,t,r,a,i)=>{let o,s=i?await t.getUser(i):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked)`);if(i&&!s)throw new Error(`Failed card authentication attempt ${i} (Missing user)`);try{o=await r.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`)}try{if(!o)throw new Error(`Failed card authentication attempt ${i} (Consume Failed)`);if(o.blocked)throw new Error(`Failed card authentication attempt ${i} (Blocked as Target)`);const e=(await issueJwtToken(o,t,a,!1)).token;let r;return a.refreshExpiryTimeMs&&(r=(await issueJwtToken(o,t,a,!0)).token),console.info(`Card authentication success. Requester:${i} Target:${o.id}`),{token:e,refreshToken:r,user:e.clearedUser}}catch(e){throw new Error(`Failed card authentication attempt ${i}`)}},refreshToken=async(e,t,r,a)=>{let i=await r.getUser(e);if(i.blocked)throw new Error(`Failed refresh token attempt ${e} (Blocked)`);if(t){let o=a.secretKey;const s=jwt.verify(t,o).user;if(!s||s.id!=i.id)throw new Error(`Failed refresh token attempt ${e} (Invalid Token)`);const n=(await issueJwtToken(i,r,a,!1)).token;return console.log(`Successful token refresh: ${i.id}`),{token:n}}throw new Error(`Failed refresh token attempt ${e}`)},mfaRegister=async(e,t,r)=>new Promise(async(a,i)=>{let o=await r.getUser(t),s=r.userSecretPath?o[r.userSecretPath]:o.mfa;const n=speakeasy.generateSecret({name:`${e}: ${t}`});if(!s){s={secret:{temp:void 0,actual:void 0},enabled:!1};o[r.userSecretPath?r.userSecretPath:"mfa"]=s}s.secret.temp=n.base32,s.secret.actual=void 0,await r.putUser(o),qrcode.toDataURL(n.otpauth_url,(e,t)=>{if(e)throw new Error("Error generating QR code");a({qr_code:t,secret:n.base32})})}),mfaVerify=async(e,t,r)=>{var a,i;const o=t;let s=await r.getUser(e);const n=r.userSecretPath?s[r.userSecretPath]:s.mfa;return speakeasy.totp.verify({secret:null===(a=null==n?void 0:n.secret)||void 0===a?void 0:a.temp,encoding:"base32",token:o})?(n.secret.actual=null===(i=null==n?void 0:n.secret)||void 0===i?void 0:i.temp,n.enabled=!0,await r.putUser(s),!0):(console.log(`Failed mfa verification for ${e}`),!1)},mfaEnabled=async(e,t)=>{let r=await t.getUser(e);const a=t.userSecretPath?r[t.userSecretPath]:r.mfa;return(null==a?void 0:a.enabled)||!1};export{authenticate,authenticateWithScratchCard,issueJwtToken,mfaEnabled,mfaRegister,mfaVerify,refreshToken};
@@ -8,6 +8,26 @@
8
8
  const qrcode = require('qrcode');
9
9
  const md5 = require("md5");
10
10
  const jwt = require('jsonwebtoken');
11
+ const issueJwtToken = async (user, userProvider, jwtSpecs, issueRefreshToken) => {
12
+ let jwtSecretKey = jwtSpecs.secretKey;
13
+ let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(user) : user;
14
+ clearedUser = userProvider.getUserPostAuthenticate ? await userProvider.getUserPostAuthenticate(clearedUser) : clearedUser;
15
+ let data = {
16
+ time: Date.now(),
17
+ user: clearedUser
18
+ };
19
+ let token;
20
+ if (issueRefreshToken && jwtSpecs.refreshExpiryTimeMs) {
21
+ token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.refreshExpiryTimeMs });
22
+ }
23
+ else if (jwtSpecs.expiryTimeMs) {
24
+ token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
25
+ }
26
+ return {
27
+ token,
28
+ clearedUser
29
+ };
30
+ };
11
31
  /**
12
32
  * When mfaToken is provided
13
33
  */
@@ -72,18 +92,15 @@
72
92
  }
73
93
  console.info(`Impersonate success. From: ${originalUser.login} into ${target}`);
74
94
  }
75
- // let jwtSecretKey = process.env.JWT_SECRET_KEY;
76
- let jwtSecretKey = jwtSpecs.secretKey;
77
- let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(user) : user;
78
- clearedUser = userProvider.getUserPostAuthenticate ? await userProvider.getUserPostAuthenticate(clearedUser) : clearedUser;
79
- let data = {
80
- time: Date.now(),
81
- user: clearedUser
82
- };
83
- // const token = jwt.sign(data, jwtSecretKey, {expiresIn: process.env.JWT_EXPIRY_TIME});
84
- const token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
95
+ const token = (await issueJwtToken(user, userProvider, jwtSpecs, false)).token;
96
+ let refreshToken;
97
+ if (jwtSpecs.refreshExpiryTimeMs)
98
+ refreshToken = (await issueJwtToken(user, userProvider, jwtSpecs, true)).token;
85
99
  console.log(`Successful login: ${user.id}`);
86
- return token;
100
+ return {
101
+ token,
102
+ refreshToken
103
+ };
87
104
  }
88
105
  else {
89
106
  throw new Error(`Failed authentication attempt ${login}`);
@@ -121,19 +138,15 @@
121
138
  throw new Error(`Failed card authentication attempt ${requesterLogin} (Blocked as Target)`);
122
139
  // ok so we will use targetUser as a user that will be actually logged in
123
140
  // in impersonation scenario targetUser may be different then the user.
124
- let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(targetUser) : targetUser;
125
- clearedUser = userProvider.getUserPostAuthenticate ? await userProvider.getUserPostAuthenticate(clearedUser) : clearedUser;
126
- let jwtSecretKey = jwtSpecs.secretKey;
127
- let data = {
128
- time: Date.now(),
129
- user: clearedUser
130
- };
131
- // const token = jwt.sign(data, jwtSecretKey, {expiresIn: process.env.JWT_EXPIRY_TIME});
132
- const token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
141
+ const token = (await issueJwtToken(targetUser, userProvider, jwtSpecs, false)).token;
142
+ let refreshToken;
143
+ if (jwtSpecs.refreshExpiryTimeMs)
144
+ refreshToken = (await issueJwtToken(targetUser, userProvider, jwtSpecs, true)).token;
133
145
  console.info(`Card authentication success. Requester:${requesterLogin} Target:${targetUser.id}`);
134
146
  return {
135
147
  token,
136
- user: clearedUser // just in case its impersonation so the actual resultin user will be different that the requester login user
148
+ refreshToken,
149
+ user: token.clearedUser // just in case its impersonation so the actual resulting user will be different that the requester login user
137
150
  };
138
151
  }
139
152
  catch (error) {
@@ -141,6 +154,37 @@
141
154
  throw new Error(`Failed card authentication attempt ${requesterLogin}`);
142
155
  }
143
156
  };
157
+ /**
158
+ * When new short lived token is requested
159
+ * Provides longer lived refresh token to obtain new short lived token
160
+ * @param login
161
+ * @param refreshToken
162
+ * @param userProvider
163
+ * @param jwtSpecs
164
+ * @returns short lived token
165
+ */
166
+ const refreshToken = async (login, refreshToken, userProvider, jwtSpecs) => {
167
+ let user = await userProvider.getUser(login);
168
+ if (user.blocked)
169
+ throw new Error(`Failed refresh token attempt ${login} (Blocked)`);
170
+ // validate refresh token
171
+ if (refreshToken) {
172
+ // let jwtSecretKey = process.env.JWT_SECRET_KEY;
173
+ let jwtSecretKey = jwtSpecs.secretKey;
174
+ var decoded = jwt.verify(refreshToken, jwtSecretKey);
175
+ // put decoded auth context into request
176
+ const refreshTokenUser = decoded.user;
177
+ if (!refreshTokenUser || refreshTokenUser.id != user.id) {
178
+ throw new Error(`Failed refresh token attempt ${login} (Invalid Token)`);
179
+ }
180
+ const token = (await issueJwtToken(user, userProvider, jwtSpecs, false)).token;
181
+ console.log(`Successful token refresh: ${user.id}`);
182
+ return { token };
183
+ }
184
+ else {
185
+ throw new Error(`Failed refresh token attempt ${login}`);
186
+ }
187
+ };
144
188
  /**
145
189
  * Will prepare user for MFA activation. Next step is to call verify with token generated in MFA app by the user.
146
190
  */
@@ -217,9 +261,11 @@
217
261
 
218
262
  exports.authenticate = authenticate;
219
263
  exports.authenticateWithScratchCard = authenticateWithScratchCard;
264
+ exports.issueJwtToken = issueJwtToken;
220
265
  exports.mfaEnabled = mfaEnabled;
221
266
  exports.mfaRegister = mfaRegister;
222
267
  exports.mfaVerify = mfaVerify;
268
+ exports.refreshToken = refreshToken;
223
269
 
224
270
  Object.defineProperty(exports, '__esModule', { value: true });
225
271
 
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).picoAuth={})}(this,function(e){"use strict";const t=require("speakeasy"),a=require("qrcode"),r=require("md5"),i=require("jsonwebtoken");e.authenticate=async(e,a,o,n,s,c,l)=>{var d;let u=await s.getUser(e);const h=s.userSecretPath?u[s.userSecretPath]:u.mfa,f=s.userPasswordPath?u[s.userPasswordPath]:u.password;if(null==h?void 0:h.enabled){if(!t.totp.verify({secret:null===(d=null==h?void 0:h.secret)||void 0===d?void 0:d.actual,encoding:"base32",token:o,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(u.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(r(a||"")==f){const e=n,t=u;if(e){let a=!1;if(e.startsWith("@")){if(a=a||await c.canImpersonate(u,e),!a)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await c.impersonateOrg(u,e)}else{const r=await s.getUser(e);if(a=a||await c.canImpersonate(u,e),!a)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);u=r}console.info(`Impersonate success. From: ${t.login} into ${e}`)}let a=l.secretKey,r=s.getSafeUser?await s.getSafeUser(u):u;r=s.getUserPostAuthenticate?await s.getUserPostAuthenticate(r):r;let o={time:Date.now(),user:r};const d=i.sign(o,a,{expiresIn:l.expiryTimeMs});return console.log(`Successful login: ${u.id}`),d}throw new Error(`Failed authentication attempt ${e}`)},e.authenticateWithScratchCard=async(e,t,a,r,o)=>{let n,s=o?await t.getUser(o):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked)`);if(o&&!s)throw new Error(`Failed card authentication attempt ${o} (Missing user)`);try{n=await a.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`)}try{if(!n)throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`);if(n.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked as Target)`);let e=t.getSafeUser?await t.getSafeUser(n):n;e=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(e):e;let a=r.secretKey,s={time:Date.now(),user:e};const c=i.sign(s,a,{expiresIn:r.expiryTimeMs});return console.info(`Card authentication success. Requester:${o} Target:${n.id}`),{token:c,user:e}}catch(e){throw new Error(`Failed card authentication attempt ${o}`)}},e.mfaEnabled=async(e,t)=>{let a=await t.getUser(e);const r=t.userSecretPath?a[t.userSecretPath]:a.mfa;return(null==r?void 0:r.enabled)||!1},e.mfaRegister=async(e,r,i)=>new Promise(async(o,n)=>{let s=await i.getUser(r),c=i.userSecretPath?s[i.userSecretPath]:s.mfa;const l=t.generateSecret({name:`${e}: ${r}`});if(!c){c={secret:{temp:void 0,actual:void 0},enabled:!1};s[i.userSecretPath?i.userSecretPath:"mfa"]=c}c.secret.temp=l.base32,c.secret.actual=void 0,await i.putUser(s),a.toDataURL(l.otpauth_url,(e,t)=>{if(e)throw new Error("Error generating QR code");o({qr_code:t,secret:l.base32})})}),e.mfaVerify=async(e,a,r)=>{var i,o;const n=a;let s=await r.getUser(e);const c=r.userSecretPath?s[r.userSecretPath]:s.mfa;return t.totp.verify({secret:null===(i=null==c?void 0:c.secret)||void 0===i?void 0:i.temp,encoding:"base32",token:n})?(c.secret.actual=null===(o=null==c?void 0:c.secret)||void 0===o?void 0:o.temp,c.enabled=!0,await r.putUser(s),!0):(console.log(`Failed mfa verification for ${e}`),!1)},Object.defineProperty(e,"__esModule",{value:!0})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).picoAuth={})}(this,function(e){"use strict";const t=require("speakeasy"),r=require("qrcode"),a=require("md5"),o=require("jsonwebtoken"),i=async(e,t,r,a)=>{let i=r.secretKey,n=t.getSafeUser?await t.getSafeUser(e):e;n=t.getUserPostAuthenticate?await t.getUserPostAuthenticate(n):n;let s,c={time:Date.now(),user:n};return a&&r.refreshExpiryTimeMs?s=o.sign(c,i,{expiresIn:r.refreshExpiryTimeMs}):r.expiryTimeMs&&(s=o.sign(c,i,{expiresIn:r.expiryTimeMs})),{token:s,clearedUser:n}};e.authenticate=async(e,r,o,n,s,c,l)=>{var d;let u=await s.getUser(e);const f=s.userSecretPath?u[s.userSecretPath]:u.mfa,h=s.userPasswordPath?u[s.userPasswordPath]:u.password;if(null==f?void 0:f.enabled){if(!t.totp.verify({secret:null===(d=null==f?void 0:f.secret)||void 0===d?void 0:d.actual,encoding:"base32",token:o,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(u.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(a(r||"")==h){const e=n,t=u;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await c.canImpersonate(u,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await c.impersonateOrg(u,e)}else{const a=await s.getUser(e);if(r=r||await c.canImpersonate(u,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);u=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}const r=(await i(u,s,l,!1)).token;let a;return l.refreshExpiryTimeMs&&(a=(await i(u,s,l,!0)).token),console.log(`Successful login: ${u.id}`),{token:r,refreshToken:a}}throw new Error(`Failed authentication attempt ${e}`)},e.authenticateWithScratchCard=async(e,t,r,a,o)=>{let n,s=o?await t.getUser(o):void 0;if(s&&s.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked)`);if(o&&!s)throw new Error(`Failed card authentication attempt ${o} (Missing user)`);try{n=await r.consume(e,s)}catch(e){throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`)}try{if(!n)throw new Error(`Failed card authentication attempt ${o} (Consume Failed)`);if(n.blocked)throw new Error(`Failed card authentication attempt ${o} (Blocked as Target)`);const e=(await i(n,t,a,!1)).token;let r;return a.refreshExpiryTimeMs&&(r=(await i(n,t,a,!0)).token),console.info(`Card authentication success. Requester:${o} Target:${n.id}`),{token:e,refreshToken:r,user:e.clearedUser}}catch(e){throw new Error(`Failed card authentication attempt ${o}`)}},e.issueJwtToken=i,e.mfaEnabled=async(e,t)=>{let r=await t.getUser(e);const a=t.userSecretPath?r[t.userSecretPath]:r.mfa;return(null==a?void 0:a.enabled)||!1},e.mfaRegister=async(e,a,o)=>new Promise(async(i,n)=>{let s=await o.getUser(a),c=o.userSecretPath?s[o.userSecretPath]:s.mfa;const l=t.generateSecret({name:`${e}: ${a}`});if(!c){c={secret:{temp:void 0,actual:void 0},enabled:!1};s[o.userSecretPath?o.userSecretPath:"mfa"]=c}c.secret.temp=l.base32,c.secret.actual=void 0,await o.putUser(s),r.toDataURL(l.otpauth_url,(e,t)=>{if(e)throw new Error("Error generating QR code");i({qr_code:t,secret:l.base32})})}),e.mfaVerify=async(e,r,a)=>{var o,i;const n=r;let s=await a.getUser(e);const c=a.userSecretPath?s[a.userSecretPath]:s.mfa;return t.totp.verify({secret:null===(o=null==c?void 0:c.secret)||void 0===o?void 0:o.temp,encoding:"base32",token:n})?(c.secret.actual=null===(i=null==c?void 0:c.secret)||void 0===i?void 0:i.temp,c.enabled=!0,await a.putUser(s),!0):(console.log(`Failed mfa verification for ${e}`),!1)},e.refreshToken=async(e,t,r,a)=>{let n=await r.getUser(e);if(n.blocked)throw new Error(`Failed refresh token attempt ${e} (Blocked)`);if(t){let s=a.secretKey;const c=o.verify(t,s).user;if(!c||c.id!=n.id)throw new Error(`Failed refresh token attempt ${e} (Invalid Token)`);const l=(await i(n,r,a,!1)).token;return console.log(`Successful token refresh: ${n.id}`),{token:l}}throw new Error(`Failed refresh token attempt ${e}`)},Object.defineProperty(e,"__esModule",{value:!0})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pico-auth",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "description": "Minimal auth with user/pass, impersonation and mfa authentication",
5
5
  "main": "dist/pico-auth.umd.js",
6
6
  "types": "dist/pico-auth.d.ts",
package/readme.MD CHANGED
@@ -19,7 +19,7 @@ Authenticates a user with login, password, and optionally an MFA token and imper
19
19
  ```ts
20
20
  import { authenticate } from 'pico-auth';
21
21
 
22
- const token = await authenticate(login, password, mfaToken, impersonateEntity, userProvider, impersonateProvider, jwtSpecs);
22
+ const tokens = await authenticate(login, password, mfaToken, impersonateEntity, userProvider, impersonateProvider, jwtSpecs);
23
23
  ```
24
24
 
25
25
  - `login`: The user's login.
@@ -29,6 +29,22 @@ const token = await authenticate(login, password, mfaToken, impersonateEntity, u
29
29
  - `userProvider`: An object implementing the `UserProvider` interface.
30
30
  - `impersonateProvider`: An object implementing the `ImpersonateProvider` interface.
31
31
  - `jwtSpecs`: An object containing JWT specifications.
32
+ - `tokens`: An returned object containing fields: short lived `token` and long lived `refreshToken`
33
+
34
+ #### `refreshToken`
35
+
36
+ Refresh token when new short lived token is requested
37
+
38
+ ```ts
39
+ import { refreshToken } from 'pico-auth';
40
+
41
+ const token = await refreshToken(login, refreshToken, userProvider, jwtSpecs);
42
+ ```
43
+
44
+ - `login`: The user's login.
45
+ - `userProvider`: An object implementing the `UserProvider` interface.
46
+ - `jwtSpecs`: An object containing JWT specifications.
47
+ - `token`: An returned object containing field: short lived `token`
32
48
 
33
49
  #### `mfaRegister`
34
50
 
@@ -71,6 +87,21 @@ const isEnabled = await mfaEnabled(login, userProvider);
71
87
  - `login`: The user's login.
72
88
  - `userProvider`: An object implementing the `UserProvider` interface.
73
89
 
90
+ #### `issueJwtToken`
91
+
92
+ Issues JWT token (access-token or refresh-token) for given user under provided specs.
93
+ ```ts
94
+ import { issueJwtToken } from 'pico-auth';
95
+
96
+ const token = await issueJwtToken(user, userProvider, jwtSpecs, issueRefreshToken);
97
+ ```
98
+
99
+ - `user`: The user object.
100
+ - `userProvider`: An object implementing the `UserProvider` interface.
101
+ - `jwtSpecs`: An object containing JWT specifications.
102
+ - `issueRefreshToken`: Boolean value that controls wheter returned token should be created using base specs (false) or refresh specs (true).
103
+ - `token`: An returned object containing short lived `token` or long lived `refreshToken`
104
+
74
105
  ### Types
75
106
 
76
107
  #### `UserProvider`