pico-auth 0.0.21 → 0.0.24
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/dist/pico-auth.d.ts +5 -1
- package/dist/pico-auth.esm.js +3 -1
- package/dist/pico-auth.esm.min.js +1 -1
- package/dist/pico-auth.umd.js +3 -1
- package/dist/pico-auth.umd.min.js +1 -1
- package/package.json +3 -2
- package/readme.MD +122 -1
package/dist/pico-auth.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare module "core/auth" {
|
|
|
4
4
|
expiryTimeMs: any;
|
|
5
5
|
}
|
|
6
6
|
export interface UserProvider {
|
|
7
|
-
getUser(login: string): Promise<
|
|
7
|
+
getUser(login: string): Promise<BaseUser>;
|
|
8
8
|
putUser(user: any): Promise<any>;
|
|
9
9
|
userSecretPath?: string;
|
|
10
10
|
userPasswordPath?: string;
|
|
@@ -13,6 +13,10 @@ declare module "core/auth" {
|
|
|
13
13
|
canImpersonate(user: any, target: string): Promise<any>;
|
|
14
14
|
impersonateOrg(user: any, target: string): Promise<any>;
|
|
15
15
|
}
|
|
16
|
+
export interface BaseUser {
|
|
17
|
+
blocked?: boolean;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
16
20
|
/**
|
|
17
21
|
* When mfaToken is provided
|
|
18
22
|
*/
|
package/dist/pico-auth.esm.js
CHANGED
|
@@ -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
|
|
@@ -150,7 +152,7 @@ const mfaVerify = async (login, mfaToken, userProvider) => {
|
|
|
150
152
|
const mfaEnabled = async (login, userProvider) => {
|
|
151
153
|
let user = await userProvider.getUser(login);
|
|
152
154
|
const mfaInfo = userProvider.userSecretPath ? user[userProvider.userSecretPath] : user.mfa;
|
|
153
|
-
return mfaInfo === null || mfaInfo === void 0 ? void 0 : mfaInfo.enabled;
|
|
155
|
+
return (mfaInfo === null || mfaInfo === void 0 ? void 0 : mfaInfo.enabled) || false;
|
|
154
156
|
};
|
|
155
157
|
|
|
156
158
|
export { authenticate, mfaEnabled, mfaRegister, mfaVerify };
|
|
@@ -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
|
|
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={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};
|
package/dist/pico-auth.umd.js
CHANGED
|
@@ -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
|
|
@@ -156,7 +158,7 @@
|
|
|
156
158
|
const mfaEnabled = async (login, userProvider) => {
|
|
157
159
|
let user = await userProvider.getUser(login);
|
|
158
160
|
const mfaInfo = userProvider.userSecretPath ? user[userProvider.userSecretPath] : user.mfa;
|
|
159
|
-
return mfaInfo === null || mfaInfo === void 0 ? void 0 : mfaInfo.enabled;
|
|
161
|
+
return (mfaInfo === null || mfaInfo === void 0 ? void 0 : mfaInfo.enabled) || false;
|
|
160
162
|
};
|
|
161
163
|
|
|
162
164
|
exports.authenticate = authenticate;
|
|
@@ -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
|
|
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={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})}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pico-auth",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
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
|
-
|
|
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.
|