pico-auth 0.0.22 → 0.0.25

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.
@@ -4,15 +4,20 @@ declare module "core/auth" {
4
4
  expiryTimeMs: any;
5
5
  }
6
6
  export interface UserProvider {
7
- getUser(login: string): Promise<any>;
7
+ getUser(login: string): Promise<BaseUser>;
8
8
  putUser(user: any): Promise<any>;
9
9
  userSecretPath?: string;
10
10
  userPasswordPath?: string;
11
+ getSafeUser?(user: any): Promise<BaseUser>;
11
12
  }
12
13
  export interface ImpersonateProvider {
13
14
  canImpersonate(user: any, target: string): Promise<any>;
14
15
  impersonateOrg(user: any, target: string): Promise<any>;
15
16
  }
17
+ export interface BaseUser {
18
+ blocked?: boolean;
19
+ [key: string]: any;
20
+ }
16
21
  /**
17
22
  * When mfaToken is provided
18
23
  */
@@ -22,6 +22,8 @@ const authenticate = async (login, password, mfaToken, impersonateEntity, userPr
22
22
  if (!validated)
23
23
  throw new Error(`Failed authentication attempt ${login} (MFA Enabled)`);
24
24
  }
25
+ if (user.blocked)
26
+ throw new Error(`Failed authentication attempt ${login} (Blocked)`);
25
27
  if (md5(password || '') == userPassword) {
26
28
  // check if impersonate mode - this is not yet implemented fully just copy pasta from GRM project
27
29
  const target = impersonateEntity; // either target user login or @organizationId
@@ -66,9 +68,10 @@ const authenticate = async (login, password, mfaToken, impersonateEntity, userPr
66
68
  }
67
69
  // let jwtSecretKey = process.env.JWT_SECRET_KEY;
68
70
  let jwtSecretKey = jwtSpecs.secretKey;
71
+ let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(user) : user;
69
72
  let data = {
70
73
  time: Date.now(),
71
- user: user
74
+ user: clearedUser
72
75
  };
73
76
  // const token = jwt.sign(data, jwtSecretKey, {expiresIn: process.env.JWT_EXPIRY_TIME});
74
77
  const token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
@@ -1 +1 @@
1
- const speakeasy=require("speakeasy"),qrcode=require("qrcode"),md5=require("md5"),jwt=require("jsonwebtoken"),authenticate=async(e,t,a,r,s,o,i)=>{var n;let c=await s.getUser(e);const l=s.userSecretPath?c[s.userSecretPath]:c.mfa,d=s.userPasswordPath?c[s.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:a,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(md5(t||"")==d){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 s.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=i.secretKey,n={time:Date.now(),user:c};const l=jwt.sign(n,a,{expiresIn:i.expiryTimeMs});return console.log(`Successful login: ${c.id}`),l}throw new Error(`Failed authentication attempt ${e}`)},mfaRegister=async(e,t,a)=>new Promise((async(r,s)=>{let o=await a.getUser(t),i=a.userSecretPath?o[a.userSecretPath]:o.mfa;const n=speakeasy.generateSecret({name:`${e}: ${t}`});if(!i){i={secret:{temp:void 0,actual:void 0},enabled:!1};o[a.userSecretPath?a.userSecretPath:"mfa"]=i}i.secret.temp=n.base32,i.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,s;const o=t;let i=await a.getUser(e);const n=a.userSecretPath?i[a.userSecretPath]:i.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===(s=null==n?void 0:n.secret)||void 0===s?void 0:s.temp,n.enabled=!0,await a.putUser(i),!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,mfaEnabled,mfaRegister,mfaVerify};
1
+ const speakeasy=require("speakeasy"),qrcode=require("qrcode"),md5=require("md5"),jwt=require("jsonwebtoken"),authenticate=async(e,t,a,r,s,o,i)=>{var n;let c=await s.getUser(e);const l=s.userSecretPath?c[s.userSecretPath]:c.mfa,d=s.userPasswordPath?c[s.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: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||"")==d){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 s.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=i.secretKey,n=s.getSafeUser?await s.getSafeUser(c):c,l={time:Date.now(),user:n};const d=jwt.sign(l,a,{expiresIn:i.expiryTimeMs});return console.log(`Successful login: ${c.id}`),d}throw new Error(`Failed authentication attempt ${e}`)},mfaRegister=async(e,t,a)=>new Promise((async(r,s)=>{let o=await a.getUser(t),i=a.userSecretPath?o[a.userSecretPath]:o.mfa;const n=speakeasy.generateSecret({name:`${e}: ${t}`});if(!i){i={secret:{temp:void 0,actual:void 0},enabled:!1};o[a.userSecretPath?a.userSecretPath:"mfa"]=i}i.secret.temp=n.base32,i.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,s;const o=t;let i=await a.getUser(e);const n=a.userSecretPath?i[a.userSecretPath]:i.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===(s=null==n?void 0:n.secret)||void 0===s?void 0:s.temp,n.enabled=!0,await a.putUser(i),!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,mfaEnabled,mfaRegister,mfaVerify};
@@ -28,6 +28,8 @@
28
28
  if (!validated)
29
29
  throw new Error(`Failed authentication attempt ${login} (MFA Enabled)`);
30
30
  }
31
+ if (user.blocked)
32
+ throw new Error(`Failed authentication attempt ${login} (Blocked)`);
31
33
  if (md5(password || '') == userPassword) {
32
34
  // check if impersonate mode - this is not yet implemented fully just copy pasta from GRM project
33
35
  const target = impersonateEntity; // either target user login or @organizationId
@@ -72,9 +74,10 @@
72
74
  }
73
75
  // let jwtSecretKey = process.env.JWT_SECRET_KEY;
74
76
  let jwtSecretKey = jwtSpecs.secretKey;
77
+ let clearedUser = userProvider.getSafeUser ? await userProvider.getSafeUser(user) : user;
75
78
  let data = {
76
79
  time: Date.now(),
77
- user: user
80
+ user: clearedUser
78
81
  };
79
82
  // const token = jwt.sign(data, jwtSecretKey, {expiresIn: process.env.JWT_EXPIRY_TIME});
80
83
  const token = jwt.sign(data, jwtSecretKey, { expiresIn: jwtSpecs.expiryTimeMs });
@@ -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"),r=require("qrcode"),a=require("md5"),o=require("jsonwebtoken");e.authenticate=async(e,r,i,n,s,c,l)=>{var u;let d=await s.getUser(e);const f=s.userSecretPath?d[s.userSecretPath]:d.mfa,m=s.userPasswordPath?d[s.userPasswordPath]:d.password;if(null==f?void 0:f.enabled){if(!t.totp.verify({secret:null===(u=null==f?void 0:f.secret)||void 0===u?void 0:u.actual,encoding:"base32",token:i,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(a(r||"")==m){const e=n,t=d;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await c.canImpersonate(d,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await c.impersonateOrg(d,e)}else{const a=await s.getUser(e);if(r=r||await c.canImpersonate(d,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);d=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}let r=l.secretKey,a={time:Date.now(),user:d};const i=o.sign(a,r,{expiresIn:l.expiryTimeMs});return console.log(`Successful login: ${d.id}`),i}throw new Error(`Failed authentication attempt ${e}`)},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)},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");e.authenticate=async(e,r,i,n,s,c,l)=>{var u;let d=await s.getUser(e);const f=s.userSecretPath?d[s.userSecretPath]:d.mfa,m=s.userPasswordPath?d[s.userPasswordPath]:d.password;if(null==f?void 0:f.enabled){if(!t.totp.verify({secret:null===(u=null==f?void 0:f.secret)||void 0===u?void 0:u.actual,encoding:"base32",token:i,window:1}))throw new Error(`Failed authentication attempt ${e} (MFA Enabled)`)}if(d.blocked)throw new Error(`Failed authentication attempt ${e} (Blocked)`);if(a(r||"")==m){const e=n,t=d;if(e){let r=!1;if(e.startsWith("@")){if(r=r||await c.canImpersonate(d,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);await c.impersonateOrg(d,e)}else{const a=await s.getUser(e);if(r=r||await c.canImpersonate(d,e),!r)throw new Error(`Failed impersonate attempt. From: ${t.id} into ${e}`);d=a}console.info(`Impersonate success. From: ${t.login} into ${e}`)}let r=l.secretKey,a=s.getSafeUser?await s.getSafeUser(d):d,i={time:Date.now(),user:a};const u=o.sign(i,r,{expiresIn:l.expiryTimeMs});return console.log(`Successful login: ${d.id}`),u}throw new Error(`Failed authentication attempt ${e}`)},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)},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.22",
3
+ "version": "0.0.25",
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",
@@ -37,7 +37,8 @@
37
37
  "build:types": "tsc -t esnext --moduleResolution node -d --emitDeclarationOnly --outFile dist/pico-auth.d.ts src/pico-auth.ts",
38
38
  "test": "env TS_NODE_PROJECT=\"tsconfig-test.json\" mocha -r ts-node/register --require source-map-support/register --recursive **/test/**/*.test.ts",
39
39
  "coverage": "nyc --reporter html --reporter text npm test",
40
- "release-minor": "npm install && npm update && npm run build && npm run coverage && npm version minor && git push origin && git push origin --tags && npm publish"
40
+ "release-minor": "npm install && npm update && npm run build && npm run coverage && npm version minor && git push origin && git push origin --tags && npm publish",
41
+ "release-patch": "npm install && npm update && npm run build && npm version patch && npm publish"
41
42
  },
42
43
  "files": [
43
44
  "dist"
package/readme.MD CHANGED
@@ -1,3 +1,124 @@
1
+ # Pico Auth
1
2
 
3
+ Pico Auth is a minimal authentication library that provides user/password authentication, multi-factor authentication (MFA), and impersonation capabilities.
2
4
 
3
- # Pico Auth
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install pico-auth
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Main Methods
14
+
15
+ #### `authenticate`
16
+
17
+ Authenticates a user with login, password, and optionally an MFA token and impersonation entity.
18
+
19
+ ```ts
20
+ import { authenticate } from 'pico-auth';
21
+
22
+ const token = await authenticate(login, password, mfaToken, impersonateEntity, userProvider, impersonateProvider, jwtSpecs);
23
+ ```
24
+
25
+ - `login`: The user's login.
26
+ - `password`: The user's password.
27
+ - `mfaToken`: The MFA token (optional).
28
+ - `impersonateEntity`: The entity to impersonate (optional).
29
+ - `userProvider`: An object implementing the `UserProvider` interface.
30
+ - `impersonateProvider`: An object implementing the `ImpersonateProvider` interface.
31
+ - `jwtSpecs`: An object containing JWT specifications.
32
+
33
+ #### `mfaRegister`
34
+
35
+ Prepares a user for MFA activation by generating a secret and a QR code.
36
+
37
+ ```ts
38
+ import { mfaRegister } from 'pico-auth';
39
+
40
+ const { qr_code, secret } = await mfaRegister(appName, login, userProvider);
41
+ ```
42
+
43
+ - `appName`: The name of the application.
44
+ - `login`: The user's login.
45
+ - `userProvider`: An object implementing the `UserProvider` interface.
46
+
47
+ #### `mfaVerify`
48
+
49
+ Verifies the MFA token and fully initializes MFA for the user if the token is valid.
50
+
51
+ ```ts
52
+ import { mfaVerify } from 'pico-auth';
53
+
54
+ const isVerified = await mfaVerify(login, mfaToken, userProvider);
55
+ ```
56
+
57
+ - `login`: The user's login.
58
+ - `mfaToken`: The MFA token.
59
+ - `userProvider`: An object implementing the `UserProvider` interface.
60
+
61
+ #### `mfaEnabled`
62
+
63
+ Checks if MFA is enabled for a user.
64
+
65
+ ```ts
66
+ import { mfaEnabled } from 'pico-auth';
67
+
68
+ const isEnabled = await mfaEnabled(login, userProvider);
69
+ ```
70
+
71
+ - `login`: The user's login.
72
+ - `userProvider`: An object implementing the `UserProvider` interface.
73
+
74
+ ### Types
75
+
76
+ #### `UserProvider`
77
+
78
+ An interface for user-related operations.
79
+
80
+ ```ts
81
+ interface UserProvider {
82
+ getUser(login: string): Promise<BaseUser>;
83
+ putUser(user: any): Promise<any>;
84
+ userSecretPath?: string;
85
+ userPasswordPath?: string;
86
+ }
87
+ ```
88
+
89
+ #### `ImpersonateProvider`
90
+
91
+ An interface for impersonation-related operations.
92
+
93
+ ```ts
94
+ interface ImpersonateProvider {
95
+ canImpersonate(user: any, target: string): Promise<any>;
96
+ impersonateOrg(user: any, target: string): Promise<any>;
97
+ }
98
+ ```
99
+
100
+ #### `JWTSpecs`
101
+
102
+ An interface for JWT specifications.
103
+
104
+ ```ts
105
+ interface JWTSpecs {
106
+ secretKey: string;
107
+ expiryTimeMs: any;
108
+ }
109
+ ```
110
+
111
+ #### `BaseUser`
112
+
113
+ An interface representing a basic user.
114
+
115
+ ```ts
116
+ interface BaseUser {
117
+ blocked?: boolean;
118
+ [key: string]: any;
119
+ }
120
+ ```
121
+
122
+ ## License
123
+
124
+ This project is licensed under the MIT License.