@skitter/cognidesk-jwt 0.0.10
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/.dockerignore +0 -0
- package/.eslintrc.js +17 -0
- package/Dockerfile +0 -0
- package/README.md +54 -0
- package/package.json +44 -0
- package/pkg/README.md +54 -0
- package/pkg/dist-node/index.js +11 -0
- package/pkg/dist-src/constants.js +4 -0
- package/pkg/dist-src/express/middleware/sessionFromToken.js +40 -0
- package/pkg/dist-src/express/middleware/setCrossDomainCookieAccess.js +9 -0
- package/pkg/dist-src/express/signSession.js +18 -0
- package/pkg/dist-src/help.js +6 -0
- package/pkg/dist-src/helpers.js +12 -0
- package/pkg/dist-src/helpers.test.js +25 -0
- package/pkg/dist-src/index.js +9 -0
- package/pkg/dist-src/sign.js +17 -0
- package/pkg/dist-src/validations.js +67 -0
- package/pkg/dist-src/verify.js +60 -0
- package/pkg/dist-src/verify.test.js +25 -0
- package/pkg/package.json +30 -0
- package/src/constants.js +4 -0
- package/src/express/middleware/sessionFromToken.js +34 -0
- package/src/express/middleware/setCrossDomainCookieAccess.js +8 -0
- package/src/express/signSession.js +16 -0
- package/src/helpers.js +24 -0
- package/src/helpers.test.js +22 -0
- package/src/index.js +5 -0
- package/src/sign.js +25 -0
- package/src/sign.test.js +31 -0
- package/src/validations.js +89 -0
- package/src/verify.js +39 -0
- package/src/verify.test.js +40 -0
package/.dockerignore
ADDED
|
File without changes
|
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
env: {
|
|
3
|
+
browser: true,
|
|
4
|
+
commonjs: true,
|
|
5
|
+
es6: true
|
|
6
|
+
},
|
|
7
|
+
extends: "eslint:recommended",
|
|
8
|
+
globals: {
|
|
9
|
+
Atomics: "readonly",
|
|
10
|
+
Buffer: "readonly",
|
|
11
|
+
SharedArrayBuffer: "readonly"
|
|
12
|
+
},
|
|
13
|
+
parserOptions: {
|
|
14
|
+
ecmaVersion: 2018
|
|
15
|
+
},
|
|
16
|
+
rules: {}
|
|
17
|
+
};
|
package/Dockerfile
ADDED
|
File without changes
|
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# cognidesk-jwt!
|
|
2
|
+
|
|
3
|
+
Simple jwt library with custom configuration for cognidesk
|
|
4
|
+
|
|
5
|
+
# How to use
|
|
6
|
+
|
|
7
|
+
## Express middleware
|
|
8
|
+
|
|
9
|
+
### To sign JWT
|
|
10
|
+
|
|
11
|
+
*setSessionAsJWT* function transforms the given data as JWT token with expiration of two days and sets it as a httpOnly cookie under the key *session*
|
|
12
|
+
|
|
13
|
+
The function throws exception in case of errors in encoding
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const { setSessionAsJWT } = require("@skitter/cognidesk-jwt/middleware/signSession")
|
|
17
|
+
var express = require('express')
|
|
18
|
+
var app = express()
|
|
19
|
+
|
|
20
|
+
const secret = "shhh"
|
|
21
|
+
const data = { test : "data"}
|
|
22
|
+
|
|
23
|
+
app.get('/', function (req, res) {
|
|
24
|
+
setSessionAsJWT(secret,data,res)
|
|
25
|
+
res.send('hello world')
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### To decode token
|
|
31
|
+
|
|
32
|
+
The functionality has been exposed as a middleware and it decodes the http-cookie and can be accessed as `req.sesssion`
|
|
33
|
+
|
|
34
|
+
The middleware throws `401` in case of issues with token verification
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const { sessionFromToken } = require("@skitterorg/cognidesk-jwt/src/express/middleware/sessionFromToken");
|
|
38
|
+
var express = require('express')
|
|
39
|
+
var app = express()
|
|
40
|
+
|
|
41
|
+
const secret = "shhh"
|
|
42
|
+
const middlewareSetup = sessionFromToken("shhh")
|
|
43
|
+
|
|
44
|
+
app.use(middlewareSetup)
|
|
45
|
+
|
|
46
|
+
app.get('/', function (req, res) {
|
|
47
|
+
console.log("session data" , req.session)
|
|
48
|
+
res.send('hello world')
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skitter/cognidesk-jwt",
|
|
3
|
+
"version": "0.0.10",
|
|
4
|
+
"main": "src/verify.js",
|
|
5
|
+
"author": "selva.g <selva.g@skitter.in>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"crocks": "^0.11.1",
|
|
9
|
+
"jsonwebtoken": "^8.5.1",
|
|
10
|
+
"ramda": "^0.26.1"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@pika/plugin-build-node": "^0.3.14",
|
|
14
|
+
"@pika/plugin-bundle-node": "^0.3.14",
|
|
15
|
+
"@pika/plugin-copy-assets": "^0.3.14",
|
|
16
|
+
"@pika/plugin-standard-pkg": "^0.3.14",
|
|
17
|
+
"eslint": "^5.16.0",
|
|
18
|
+
"jest": "^24.7.0",
|
|
19
|
+
"microbundle": "^0.11.0",
|
|
20
|
+
"prettier": "^1.16.4"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "jest",
|
|
24
|
+
"bundle": "microbundle src/verify.js -o dist/stockroom.js",
|
|
25
|
+
"lint": "eslint src/*.js",
|
|
26
|
+
"test:watch": "jest --watch"
|
|
27
|
+
},
|
|
28
|
+
"@pika/pack": {
|
|
29
|
+
"pipeline": [
|
|
30
|
+
[
|
|
31
|
+
"@pika/plugin-standard-pkg"
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"@pika/plugin-build-node",
|
|
35
|
+
{
|
|
36
|
+
"minNodeVersion": 10
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
"@pika/plugin-bundle-node"
|
|
41
|
+
]
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
package/pkg/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# cognidesk-jwt!
|
|
2
|
+
|
|
3
|
+
Simple jwt library with custom configuration for cognidesk
|
|
4
|
+
|
|
5
|
+
# How to use
|
|
6
|
+
|
|
7
|
+
## Express middleware
|
|
8
|
+
|
|
9
|
+
### To sign JWT
|
|
10
|
+
|
|
11
|
+
*setSessionAsJWT* function transforms the given data as JWT token with expiration of two days and sets it as a httpOnly cookie under the key *session*
|
|
12
|
+
|
|
13
|
+
The function throws exception in case of errors in encoding
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const { setSessionAsJWT } = require("@skitter/cognidesk-jwt/middleware/signSession")
|
|
17
|
+
var express = require('express')
|
|
18
|
+
var app = express()
|
|
19
|
+
|
|
20
|
+
const secret = "shhh"
|
|
21
|
+
const data = { test : "data"}
|
|
22
|
+
|
|
23
|
+
app.get('/', function (req, res) {
|
|
24
|
+
setSessionAsJWT(secret,data,res)
|
|
25
|
+
res.send('hello world')
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### To decode token
|
|
31
|
+
|
|
32
|
+
The functionality has been exposed as a middleware and it decodes the http-cookie and can be accessed as `req.sesssion`
|
|
33
|
+
|
|
34
|
+
The middleware throws `401` in case of issues with token verification
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const { sessionFromToken } = require("@skitter/cognidesk-jwt/middleware/sessionFromToken")
|
|
38
|
+
var express = require('express')
|
|
39
|
+
var app = express()
|
|
40
|
+
|
|
41
|
+
const secret = "shhh"
|
|
42
|
+
const middlewareSetup = sessionFromToken("shhh")
|
|
43
|
+
|
|
44
|
+
app.use(middlewareSetup)
|
|
45
|
+
|
|
46
|
+
app.get('/', function (req, res) {
|
|
47
|
+
console.log("session data" , req.session)
|
|
48
|
+
res.send('hello world')
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
var cookie = require("cookie");
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
verify
|
|
5
|
+
} = require("../verify");
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
isError
|
|
9
|
+
} = require("../helpers");
|
|
10
|
+
|
|
11
|
+
function sessionFromJWT(secret, mockdata) {
|
|
12
|
+
return function _verifyJwt(req, res, next) {
|
|
13
|
+
if (mockdata) {
|
|
14
|
+
req.session = mockdata;
|
|
15
|
+
return next();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let cookies = req.headers.cookie;
|
|
19
|
+
|
|
20
|
+
if (!cookies) {
|
|
21
|
+
res.send(401, "No cookie found");
|
|
22
|
+
return next();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const parsedCookie = cookie.parse(cookies);
|
|
26
|
+
const session = cookies.session;
|
|
27
|
+
const data = verify(secret)(session);
|
|
28
|
+
|
|
29
|
+
if (isError(data)) {
|
|
30
|
+
res.send(401, `Invalid token : ${data.name}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
req.session = data;
|
|
34
|
+
next();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
sessionFromJWT
|
|
40
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var cookie = require("cookie");
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
sign
|
|
5
|
+
} = require("../sign");
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
isError
|
|
9
|
+
} = require("../helpers");
|
|
10
|
+
|
|
11
|
+
function setSessionAsJWT(secret, data, res) {
|
|
12
|
+
const token = sign(secret)(data);
|
|
13
|
+
let options = {
|
|
14
|
+
httpOnly: true // The cookie only accessible by the web server
|
|
15
|
+
|
|
16
|
+
};
|
|
17
|
+
res.cookie("session", token, options);
|
|
18
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//const { concat, reduce } = require("crocks/pointfree");
|
|
2
|
+
//const Either = require("crocks/Either");
|
|
3
|
+
//const { Left, Right } = Either;
|
|
4
|
+
////console.log(concat(Right("ad"), Right("asd"), Left("ssh")));
|
|
5
|
+
//const arr = [Right("ad"), Right("asd"), Left("ssh")];
|
|
6
|
+
////console.log(reduce(concat, Right(""), arr));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const R = require("ramda");
|
|
2
|
+
|
|
3
|
+
let isError = function (e) {
|
|
4
|
+
return e && e.stack && e.message && typeof e.stack === "string" && typeof e.message === "string";
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const logTag = tag => data => console.log(tag, data);
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
isError,
|
|
11
|
+
logTag
|
|
12
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const {
|
|
2
|
+
isError
|
|
3
|
+
} = require("./helpers");
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
verify
|
|
7
|
+
} = require("./verify");
|
|
8
|
+
|
|
9
|
+
test("verify iserror", () => {
|
|
10
|
+
const invalidToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC.eyJCj19qlQPasOu_JsUmJEe5yst_iTf0ECu4";
|
|
11
|
+
const secret = "shhhh";
|
|
12
|
+
const verifyOutput = verify(secret)(invalidToken);
|
|
13
|
+
expect(isError(verifyOutput)).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
test("sample error", () => {
|
|
16
|
+
const error = new Error("test error");
|
|
17
|
+
expect(isError(error)).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
test("with error like properties", () => {
|
|
20
|
+
const error2 = {
|
|
21
|
+
name: "error",
|
|
22
|
+
stack: "test"
|
|
23
|
+
};
|
|
24
|
+
expect(isError(error2)).toBeFalsy();
|
|
25
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const {
|
|
2
|
+
DEFAULT_ALGORITHM
|
|
3
|
+
} = require("./constants");
|
|
4
|
+
|
|
5
|
+
var jwt = require("jsonwebtoken");
|
|
6
|
+
|
|
7
|
+
const sign = secret => data => {
|
|
8
|
+
return jwt.sign(data, secret, {
|
|
9
|
+
algorithm: DEFAULT_ALGORITHM,
|
|
10
|
+
expiresIn: "2 days",
|
|
11
|
+
issuer: "cognidesk-jwts"
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
sign
|
|
17
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const R = require("ramda");
|
|
2
|
+
|
|
3
|
+
const Either = require("crocks/Either");
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
isString
|
|
7
|
+
} = require("crocks/predicates");
|
|
8
|
+
|
|
9
|
+
const safe = require("crocks/Maybe/safe");
|
|
10
|
+
|
|
11
|
+
const prop = require("crocks/Maybe/prop");
|
|
12
|
+
|
|
13
|
+
const Maybe = require("crocks/Maybe");
|
|
14
|
+
|
|
15
|
+
const maybeToEither = require("crocks/Either/maybeToEither");
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
head,
|
|
19
|
+
map,
|
|
20
|
+
chain,
|
|
21
|
+
concat,
|
|
22
|
+
reduce
|
|
23
|
+
} = require("crocks/pointfree");
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
logTag
|
|
27
|
+
} = require("./helpers");
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
Nothing
|
|
31
|
+
} = Maybe;
|
|
32
|
+
const {
|
|
33
|
+
Left,
|
|
34
|
+
Right
|
|
35
|
+
} = Either;
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
DEFAULT_ALGORITHM,
|
|
39
|
+
VALID_ISSUERS
|
|
40
|
+
} = require("./constants");
|
|
41
|
+
|
|
42
|
+
const secondElement = arr => arr.length >= 2 ? Maybe.of(arr[1]) : Nothing();
|
|
43
|
+
|
|
44
|
+
const createBuffer = encoding => object => Buffer.from(object, encoding);
|
|
45
|
+
|
|
46
|
+
const safeParse = toParse => {
|
|
47
|
+
try {
|
|
48
|
+
return Right(JSON.parse(toParse));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return Left("Invalid JSON");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const decodeJwtHeader = R.compose(chain(safeParse), map(createBuffer("base64")), maybeToEither("No Header"), chain(head), map(R.split(".")), safe(isString));
|
|
55
|
+
const decodePayLoad = R.compose(chain(safeParse), map(createBuffer("base64")), maybeToEither("No Payload"), chain(secondElement), map(R.split(".")), safe(isString));
|
|
56
|
+
const validAlgorithm = R.cond([[R.equals(DEFAULT_ALGORITHM), R.always(Right("Valid Algorithm"))], [R.T, R.always(Left("Invalid Algorithm"))]]);
|
|
57
|
+
const flipedIncludes = R.flip(R.includes);
|
|
58
|
+
const validIssuers = R.cond([[flipedIncludes(VALID_ISSUERS), R.always(Right("Valid Issuer"))], [R.T, R.always(Left("Invalid Issuer"))]]);
|
|
59
|
+
const checkAlg = R.compose(chain(validAlgorithm), maybeToEither("No Algorithm in Header"), prop("alg"));
|
|
60
|
+
const checkIssuer = R.compose(chain(validIssuers), maybeToEither("No Issuer in Payload"), prop("iss"));
|
|
61
|
+
const checkHeader = R.compose(chain(checkAlg), map(R.tap(logTag("head"))), decodeJwtHeader);
|
|
62
|
+
const checkPayload = R.compose(chain(checkIssuer), map(R.tap(logTag("payload"))), decodePayLoad);
|
|
63
|
+
const validations = [checkHeader, checkPayload];
|
|
64
|
+
const mergeValidations = R.compose(R.converge((...args) => reduce(concat, Right(""))(args), validations));
|
|
65
|
+
module.exports = {
|
|
66
|
+
mergeValidations
|
|
67
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
var jwt = require("jsonwebtoken");
|
|
2
|
+
|
|
3
|
+
const R = require("ramda");
|
|
4
|
+
|
|
5
|
+
const Either = require("crocks/Either");
|
|
6
|
+
|
|
7
|
+
const maybeToEither = require("crocks/Either/maybeToEither");
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
chain,
|
|
11
|
+
merge,
|
|
12
|
+
either
|
|
13
|
+
} = require("crocks/pointfree");
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
isString
|
|
17
|
+
} = require("crocks/predicates");
|
|
18
|
+
|
|
19
|
+
const safe = require("crocks/Maybe/safe");
|
|
20
|
+
|
|
21
|
+
const prop = require("crocks/Maybe/prop");
|
|
22
|
+
|
|
23
|
+
const fanout = require("crocks/helpers/fanout");
|
|
24
|
+
|
|
25
|
+
const Maybe = require("crocks/Maybe");
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
Just,
|
|
29
|
+
Nothing
|
|
30
|
+
} = Maybe;
|
|
31
|
+
const {
|
|
32
|
+
Left,
|
|
33
|
+
Right
|
|
34
|
+
} = Either;
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
mergeValidations
|
|
38
|
+
} = require("./validations");
|
|
39
|
+
|
|
40
|
+
const safeVerify = secret => token => {
|
|
41
|
+
try {
|
|
42
|
+
return Right(jwt.verify(token, secret));
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return Left(err);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const mergeEither = (e1, e2) => e1.chain(_ => e2);
|
|
49
|
+
|
|
50
|
+
const makeError = error => new Error(error);
|
|
51
|
+
|
|
52
|
+
const handleToken = secret => token => R.compose(either(makeError, R.identity), chain(safeVerify(secret)), merge(mergeEither), fanout(mergeValidations, Right))(token);
|
|
53
|
+
|
|
54
|
+
const verify = (secret, mockData) => token => {
|
|
55
|
+
return R.cond([[R.isNil, R.always(handleToken(secret)(token))], [R.T, R.identity]])(mockData);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
verify
|
|
60
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const {
|
|
2
|
+
verify
|
|
3
|
+
} = require("./verify");
|
|
4
|
+
|
|
5
|
+
const validToken = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1NTQzMzcyODR9.k6R3HV1_QB-B4CQ1PQYrXCSxM9mxnPtR6lJb-sKIHpLIbAmWgbjVAc6vGYqRSF4W";
|
|
6
|
+
const invalidToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC.eyJCj19qlQPasOu_JsUmJEe5yst_iTf0ECu4";
|
|
7
|
+
const secret = "shhhh";
|
|
8
|
+
test("verify valid token", () => {
|
|
9
|
+
const valid = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJjb2duaWRlc2stand0In0.8V-6DUJzTQpFgGEoLQxSjOaxslVdsOHLLM04zTcJhWnpsdQearWS_nhpIaM2LskP";
|
|
10
|
+
expect(verify(secret)(valid)).toHaveProperty("foo");
|
|
11
|
+
});
|
|
12
|
+
test("wrong secret", () => {
|
|
13
|
+
const wrongSecret = "sad";
|
|
14
|
+
expect(verify(wrongSecret)(validToken)).toHaveProperty("name");
|
|
15
|
+
});
|
|
16
|
+
test("wrong token", () => {
|
|
17
|
+
const wrongSecret = "sad";
|
|
18
|
+
expect(verify(secret)(invalidToken)).toHaveProperty("name");
|
|
19
|
+
});
|
|
20
|
+
test("has mock data", () => {
|
|
21
|
+
const mockData = {
|
|
22
|
+
test: "test"
|
|
23
|
+
};
|
|
24
|
+
expect(verify(secret, mockData)(invalidToken)).toHaveProperty("test");
|
|
25
|
+
});
|
package/pkg/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skitter/cognidesk-jwt",
|
|
3
|
+
"description": "Simple jwt library with custom configuration for cognidesk",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"esnext": "dist-src/index.js",
|
|
7
|
+
"main": "dist-node/index.js",
|
|
8
|
+
"pika": true,
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"files": [
|
|
11
|
+
"dist-*/",
|
|
12
|
+
"assets/",
|
|
13
|
+
"bin/"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"crocks": "^0.11.1",
|
|
17
|
+
"jsonwebtoken": "^8.5.1",
|
|
18
|
+
"ramda": "^0.26.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@pika/plugin-build-node": "^0.3.14",
|
|
22
|
+
"@pika/plugin-bundle-node": "^0.3.14",
|
|
23
|
+
"@pika/plugin-copy-assets": "^0.3.14",
|
|
24
|
+
"@pika/plugin-standard-pkg": "^0.3.14",
|
|
25
|
+
"eslint": "^5.16.0",
|
|
26
|
+
"jest": "^24.7.0",
|
|
27
|
+
"microbundle": "^0.11.0",
|
|
28
|
+
"prettier": "^1.16.4"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var cookie = require("cookie");
|
|
2
|
+
const { verify } = require("../../verify");
|
|
3
|
+
const { isError, makeError } = require("../../helpers");
|
|
4
|
+
|
|
5
|
+
function sessionFromJWT(secret, mockdata) {
|
|
6
|
+
return function _verifyJwt(req, res, next) {
|
|
7
|
+
if (mockdata) {
|
|
8
|
+
req.session = mockdata;
|
|
9
|
+
return next();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let cookies = req.headers.cookie;
|
|
13
|
+
|
|
14
|
+
if (!cookies) {
|
|
15
|
+
const cookieError = makeError("No cookie found");
|
|
16
|
+
return next(cookieError);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const parsedCookie = cookie.parse(cookies);
|
|
20
|
+
const session = parsedCookie.session;
|
|
21
|
+
const data = verify(secret)(session);
|
|
22
|
+
|
|
23
|
+
if (isError(data)) {
|
|
24
|
+
return next(data);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
req.session = data;
|
|
28
|
+
next();
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
sessionFromJWT
|
|
34
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var cookie = require("cookie");
|
|
2
|
+
const { sign } = require("../sign");
|
|
3
|
+
const { isError } = require("../helpers");
|
|
4
|
+
|
|
5
|
+
function setSessionAsJWT(secret, data, res) {
|
|
6
|
+
const token = sign(secret)(data);
|
|
7
|
+
let options = {
|
|
8
|
+
httpOnly: true,
|
|
9
|
+
maxAge: 60 * 60 * 24
|
|
10
|
+
};
|
|
11
|
+
res.cookie("session", token, options);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
setSessionAsJWT
|
|
16
|
+
};
|
package/src/helpers.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
let isError = function(e) {
|
|
2
|
+
return (
|
|
3
|
+
e &&
|
|
4
|
+
e.stack &&
|
|
5
|
+
e.message &&
|
|
6
|
+
typeof e.stack === "string" &&
|
|
7
|
+
typeof e.message === "string"
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
/* eslint-disable no-alert, no-console */
|
|
11
|
+
const logTag = tag => data => console.log(tag, data);
|
|
12
|
+
/* eslint-disable no-alert, no-console */
|
|
13
|
+
|
|
14
|
+
const makeError = error => {
|
|
15
|
+
const tokenError = new Error(error);
|
|
16
|
+
tokenError.code = "TokenValidationError";
|
|
17
|
+
return tokenError;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
isError,
|
|
22
|
+
logTag,
|
|
23
|
+
makeError
|
|
24
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
const { isError } = require("./helpers");
|
|
3
|
+
const { verify } = require("./verify");
|
|
4
|
+
|
|
5
|
+
test("verify iserror", () => {
|
|
6
|
+
const invalidToken =
|
|
7
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC.eyJCj19qlQPasOu_JsUmJEe5yst_iTf0ECu4";
|
|
8
|
+
const secret = "shhhh";
|
|
9
|
+
const verifyOutput = verify(secret)(invalidToken);
|
|
10
|
+
|
|
11
|
+
expect(isError(verifyOutput)).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("sample error", () => {
|
|
15
|
+
const error = new Error("test error");
|
|
16
|
+
expect(isError(error)).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("with error like properties", () => {
|
|
20
|
+
const error2 = { name: "error", stack: "test" };
|
|
21
|
+
expect(isError(error2)).toBeFalsy();
|
|
22
|
+
});
|
package/src/index.js
ADDED
package/src/sign.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const { DEFAULT_ALGORITHM } = require("./constants");
|
|
2
|
+
var jwt = require("jsonwebtoken");
|
|
3
|
+
const R = require("ramda");
|
|
4
|
+
|
|
5
|
+
const isObject = require("crocks/predicates/isObject");
|
|
6
|
+
|
|
7
|
+
const sign = secret => data => {
|
|
8
|
+
if (!isObject(data)) {
|
|
9
|
+
throw new Error("Data must be an object");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (R.isNil(secret)) {
|
|
13
|
+
throw new Error("Secret should not be nil");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return jwt.sign(data, secret, {
|
|
17
|
+
algorithm: DEFAULT_ALGORITHM,
|
|
18
|
+
expiresIn: "24h",
|
|
19
|
+
issuer: "cognidesk-jwt"
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
sign
|
|
25
|
+
};
|
package/src/sign.test.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
const { sign } = require("./sign");
|
|
3
|
+
|
|
4
|
+
const secret = "472D4B614E645267556B587032733576";
|
|
5
|
+
const data = { id: 1 };
|
|
6
|
+
|
|
7
|
+
test("verify valid token", () => {
|
|
8
|
+
const token = sign(secret)(data);
|
|
9
|
+
console.log(token);
|
|
10
|
+
expect(token).toBeDefined();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("invalid data", () => {
|
|
14
|
+
const invalidData = null;
|
|
15
|
+
expect(sign(secret)(invalidData)).toThrow();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("invalid secret", () => {
|
|
19
|
+
const secret = null;
|
|
20
|
+
expect(sign(secret)(data)).toThrow();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
//test("wrong token", () => {
|
|
24
|
+
//const wrongsecret = "sad";
|
|
25
|
+
//expect(verify(secret)(invalidToken)).toHaveProperty("name");
|
|
26
|
+
//});
|
|
27
|
+
|
|
28
|
+
//test("has mock data", () => {
|
|
29
|
+
//const mockData = { test: "test" };
|
|
30
|
+
//expect(verify(secret, mockData)(invalidToken)).toHaveProperty("test");
|
|
31
|
+
//});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const R = require("ramda");
|
|
2
|
+
const Either = require("crocks/Either");
|
|
3
|
+
const { isString } = require("crocks/predicates");
|
|
4
|
+
const safe = require("crocks/Maybe/safe");
|
|
5
|
+
const prop = require("crocks/Maybe/prop");
|
|
6
|
+
const Maybe = require("crocks/Maybe");
|
|
7
|
+
const maybeToEither = require("crocks/Either/maybeToEither");
|
|
8
|
+
const { head, map, chain, concat, reduce } = require("crocks/pointfree");
|
|
9
|
+
const { logTag } = require("./helpers");
|
|
10
|
+
|
|
11
|
+
const { Nothing } = Maybe;
|
|
12
|
+
const { Left, Right } = Either;
|
|
13
|
+
|
|
14
|
+
const { DEFAULT_ALGORITHM, VALID_ISSUERS } = require("./constants");
|
|
15
|
+
|
|
16
|
+
const secondElement = arr => (arr.length >= 2 ? Maybe.of(arr[1]) : Nothing());
|
|
17
|
+
const createBuffer = encoding => object => Buffer.from(object, encoding);
|
|
18
|
+
const safeParse = toParse => {
|
|
19
|
+
try {
|
|
20
|
+
return Right(JSON.parse(toParse));
|
|
21
|
+
} catch (err) {
|
|
22
|
+
return Left("Invalid JSON");
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const decodeJwtHeader = R.compose(
|
|
27
|
+
chain(safeParse),
|
|
28
|
+
map(createBuffer("base64")),
|
|
29
|
+
maybeToEither("No Header"),
|
|
30
|
+
chain(head),
|
|
31
|
+
map(R.split(".")),
|
|
32
|
+
safe(isString)
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const decodePayLoad = R.compose(
|
|
36
|
+
chain(safeParse),
|
|
37
|
+
map(createBuffer("base64")),
|
|
38
|
+
maybeToEither("No Payload"),
|
|
39
|
+
chain(secondElement),
|
|
40
|
+
map(R.split(".")),
|
|
41
|
+
safe(isString)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const validAlgorithm = R.cond([
|
|
45
|
+
[R.equals(DEFAULT_ALGORITHM), R.always(Right("Valid Algorithm"))],
|
|
46
|
+
[R.T, R.always(Left("Invalid Algorithm"))]
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const flipedIncludes = R.flip(R.includes);
|
|
50
|
+
|
|
51
|
+
const validIssuers = R.cond([
|
|
52
|
+
[flipedIncludes(VALID_ISSUERS), R.always(Right("Valid Issuer"))],
|
|
53
|
+
[R.T, R.always(Left("Invalid Issuer"))]
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
const checkAlg = R.compose(
|
|
57
|
+
chain(validAlgorithm),
|
|
58
|
+
maybeToEither("No Algorithm in Header"),
|
|
59
|
+
prop("alg")
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const checkIssuer = R.compose(
|
|
63
|
+
chain(validIssuers),
|
|
64
|
+
maybeToEither("No Issuer in Payload"),
|
|
65
|
+
prop("iss")
|
|
66
|
+
);
|
|
67
|
+
const checkHeader = R.compose(
|
|
68
|
+
chain(checkAlg),
|
|
69
|
+
map(R.tap(logTag("head"))),
|
|
70
|
+
decodeJwtHeader
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const checkPayload = R.compose(
|
|
74
|
+
chain(checkIssuer),
|
|
75
|
+
map(R.tap(logTag("payload"))),
|
|
76
|
+
decodePayLoad
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const validations = [checkHeader, checkPayload];
|
|
80
|
+
|
|
81
|
+
//const signValidations = [checkHeader, checkPayload];
|
|
82
|
+
|
|
83
|
+
const jwtValidations = R.compose(
|
|
84
|
+
R.converge((...args) => reduce(concat, Right(""))(args), validations)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
jwtValidations
|
|
89
|
+
};
|
package/src/verify.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var jwt = require("jsonwebtoken");
|
|
2
|
+
const R = require("ramda");
|
|
3
|
+
const Either = require("crocks/Either");
|
|
4
|
+
const { chain, merge, either } = require("crocks/pointfree");
|
|
5
|
+
const fanout = require("crocks/helpers/fanout");
|
|
6
|
+
|
|
7
|
+
const { Left, Right } = Either;
|
|
8
|
+
const { makeError } = require("./helpers");
|
|
9
|
+
|
|
10
|
+
const { jwtValidations } = require("./validations");
|
|
11
|
+
|
|
12
|
+
const safeVerify = secret => token => {
|
|
13
|
+
try {
|
|
14
|
+
return Right(jwt.verify(token, secret));
|
|
15
|
+
} catch (err) {
|
|
16
|
+
return Left(err);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const mergeEither = (e1, e2) => e1.chain(() => e2);
|
|
21
|
+
|
|
22
|
+
const handleToken = secret => token =>
|
|
23
|
+
R.compose(
|
|
24
|
+
either(makeError, R.identity),
|
|
25
|
+
chain(safeVerify(secret)),
|
|
26
|
+
merge(mergeEither),
|
|
27
|
+
fanout(jwtValidations, Right)
|
|
28
|
+
)(token);
|
|
29
|
+
|
|
30
|
+
const verify = (secret, mockData) => token => {
|
|
31
|
+
return R.cond([
|
|
32
|
+
[R.isNil, R.always(handleToken(secret)(token))],
|
|
33
|
+
[R.T, R.identity]
|
|
34
|
+
])(mockData);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
verify
|
|
39
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
const { verify } = require("./verify");
|
|
3
|
+
|
|
4
|
+
const validToken =
|
|
5
|
+
"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1NTQzMzcyODR9.k6R3HV1_QB-B4CQ1PQYrXCSxM9mxnPtR6lJb-sKIHpLIbAmWgbjVAc6vGYqRSF4W";
|
|
6
|
+
|
|
7
|
+
const invalidToken =
|
|
8
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC.eyJCj19qlQPasOu_JsUmJEe5yst_iTf0ECu4";
|
|
9
|
+
const secret = "shhhh";
|
|
10
|
+
|
|
11
|
+
test("verify valid token", () => {
|
|
12
|
+
const valid =
|
|
13
|
+
"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJjb2duaWRlc2stand0In0.8V-6DUJzTQpFgGEoLQxSjOaxslVdsOHLLM04zTcJhWnpsdQearWS_nhpIaM2LskP";
|
|
14
|
+
expect(verify(secret)(valid)).toHaveProperty("foo");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("wrong secret", () => {
|
|
18
|
+
const wrongSecret = "sad";
|
|
19
|
+
const out = verify(wrongSecret)(validToken);
|
|
20
|
+
console.log(out.name, out.code, out.message, out.stack);
|
|
21
|
+
expect(out).toHaveProperty("name");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("wrong token", () => {
|
|
25
|
+
const wrongSecret = "sad";
|
|
26
|
+
expect(verify(secret)(invalidToken)).toHaveProperty("name");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("has mock data", () => {
|
|
30
|
+
const mockData = { test: "test" };
|
|
31
|
+
expect(verify(secret, mockData)(invalidToken)).toHaveProperty("test");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("test actual token", () => {
|
|
35
|
+
const token =
|
|
36
|
+
"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTU0ODI1NzUxLCJleHAiOjE1NTQ5OTg1NTEsImlzcyI6ImNvZ25pZGVzay1qd3QifQ.6yyrr4kqKHyqEj8f_fPAzPN1BogNrT7JsiiZOyzS-gRd1227LxcTxphqm49WXPHC";
|
|
37
|
+
//const secret = "472D4B614E645267556B587032733576";
|
|
38
|
+
const secret = "472D4B614E645267556B587032733576";
|
|
39
|
+
expect(verify(secret)(token)).toHaveProperty("id");
|
|
40
|
+
});
|