michieldemey / express-jwt-permissions Goto Github PK
View Code? Open in Web Editor NEW:vertical_traffic_light: Express middleware for JWT permissions
License: MIT License
:vertical_traffic_light: Express middleware for JWT permissions
License: MIT License
Originally posted on Stack Overflow
I am attempting to use express-jwt-permissions to protect an API route and I am unable to use the guard syntax "guard.check('user')". I have successfully used express-jwt which express-jwt-permissions builds upon.
An interesting fact is that express-jwt requires the JWT_SECRET to be assigned to it, whereas there are no instruction on the express-jwt-permissions docs to understand this interplay or perhaps there is some example missing?
My current code is as follows:
/////////////////////////////////////////////
// auth.ts - Auth and set user permissions
/////////////////////////////////////////////
router.post('/', async (request, response, next) => {
const {email, password} = request.body
try {
// Authenticate
const user = await authenticate(email, password)
user.permissions = ['user'] // set the express-jwt-permissions here
// Create JWT token
let token = jwt.sign(user.toJSON(), process.env.JWT_SECRET, {
expiresIn: '60m'
})
let {iat, exp} = jwtDecode(token)
// Respond with token
response.status(HttpStatus.OK).send({iat, exp, token})
...
I successfully retrieved a JWT token from the 'api/auth' endpoint.
{
"iat": 1559650778,
"exp": 1559654378,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwZXJtaXNzaW9ucyI6WyJ1c2VyIl0sIl9pZCI6IjVjZjUyZjc1NDA4MTk0YWI1MGZlMWNkNiIsIm5hbWUiOiJHYXJ5IFBhbHVrIiwiZW1haWwiOiJnYXJ5QHBsdWdpbi5pbyIsInVzZXJuYW1lIjoiZ2FyeSIsInBhc3N3b3JkIjoiJDJhJDEwJEt1U1NUQXowd1MxNU5tRjRVQjZQb2VMTC5Ya1phZkc5Sm9xVkVRWnZZcHFkTFNrZXliTU1lIiwidXBkYXRlZEF0IjoiMjAxOS0wNi0wM1QxNDozMjoyMS4zMDlaIiwiY3JlYXRlZEF0IjoiMjAxOS0wNi0wM1QxNDozMjoyMS4zMDlaIiwiX192IjowLCJpYXQiOjE1NTk2NTA3NzgsImV4cCI6MTU1OTY1NDM3OH0.qnfH_OHq2YqaKCRIbwtw788SQC51F8PJESRCf3Nlrak"
}
I then attempted to authorize with combinations of Bearer Token, OAuth2, prepending token with/without 'jwt ' etc, but nothing seems to get past the route guard on 'api/registry'.
/////////////////////////////////////////////
// server.ts - API auth routes
/////////////////////////////////////////////
server.use(
'/api/registry',
guard.check('user'),
require('./api/v1/registry')
)
server.use('/api/auth', require('./api/v1/auth'))
...
Result:
{
"name": "UnauthorizedError",
"message": "user object \"user\" was not found. Check your configuration.",
"code": "user_object_not_found",
"status": 403,
"inner": {
"message": "user object \"user\" was not found. Check your configuration."
}
}
The expected result would be that I can make an API call to '/api/registry' with the JWT token as a bearer token / OAuth2? and that should let me pass the route guard.
Thanks
If I try to compile it with typescript I get the following error:
node_modules/express-jwt-permissions/index.d.ts:1:10 - error TS2305: Module '"express-unless"' has no exported member 'RequestHandler'.
The Problem seems to occurs if some other module uses express-unless above 1.0.0. Maybe it would be a good idea to update the dependencies and the related typings?
Hey! I'm getting an UnauthorizedError when using the PUT method. Works with POST, so that's what I'll be using for now, but I'm curious. I think it should work just the same with all allowed methods on res.header, right?
Am I missing something?
xtend can easily be replaced with Object.assign
This reduce the dependencies and possible also avoid duplicated versions where some package don't use the same version range for the xtend package
// immutable
Object.assign({}, a, b)
// mutable
Object.assign(a, b)
Hey there, thanks for jwt permissions!
This condition will always return 'false' since the types 'ErrorCode' and '"permission_denied"' have no overlap.ts(2367)
if (err instanceof UnauthorizedError)
if ((err as UnauthorizedError).code === "permission_denied")
export type ErrorCode =
"revoked_token" |
"invalid_token" |
"credentials_bad_scheme" |
"credentials_bad_format" |
"credentials_required"
export class UnauthorizedError extends Error {
status: number;
message: string;
name: 'UnauthorizedError';
code: ErrorCode;
inner: { message: string };
constructor(code: ErrorCode, error: { message: string });
}
I need a guard.ignore() that I can put inline on specific routes
I am using express-jwt with credentialsRequired: trueto ensure that I only accept calls that include a JWT in them
But there are a few routes for which I do not want to use guard.check()
I need to have it both ways !!
:)
Auth0 released their Core authorization and if i understand correctly , when i chose to add permissions to access token, its in the "permissions" property.
If the API is called by client_credential application, the token will have scopes associated with this application instead of permissions.
Could express-jwt-permissions be used to check both "permissions" and "scopes" at the same time?
Based on Express JWT:
A custom function for extracting the token from a request can be specified with the getToken option. This is useful if you need to pass the token through a query parameter or a cookie. You can throw an error in this function and it will be handled by express-jwt.
app.use(jwt({
secret: 'hello world !',
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
}));
How do I integrate permissions in this case?
Thank you so much for this package. The simplicity and flexibility enabled is awesome.
I'd like to suggest an addition to documentation. The context being, many of us understand the concept of Auth, but unless we've been deep in the weeds, we don't find it intuitive. So at first glance, the below wouldn't occur to someone who was looking to check permissions, but then wanted to check roles, or some other attributes of the token:
const Permissions = useGuard({
requestProperty: "auth",
permissionsProperty: "permissions"
});
const Roles = useGuard({
requestProperty: "auth",
permissionsProperty: ${Config.namespace}/roles
});
The point we miss as "novices" is that the package allows us to setup many different types of guards and apply them in parallel.
Thanks again!
I would love to be able to specify a function to be called to get the check string based on the request
app.use(guard({
getCheck: function fromReq (req) {
return req.url.split('/')[2];
}
}));
Once I saw how your code worked I made the 2nd part of the url the same as the permissons values
While running my tests, my stdout is getting cluttered with UnauthorizedError: Permission denied
error logs.
I really don't want to mock console.error
calls, and I couldn't find any entry point for disabling logging while testing.
I don't think the default behaviour of the tool should even call console.error
, tho.
Hi i have an issue always get this message,
"Could not find permissions for user. Bad configuration?"
At first thank you very much for this helpfull and handy module. It works very fine.
For me it would be very helpful if i can access the missing permission at the error handler:
Example:
app.use(guard.check('admin'))
app.use(function (err, req, res, next) {
if (err.code === 'permission_denied') {
const missingPermisson = err.missing;
res.status(403).send('Forbidden - Missing permission: ' + missingPermission);
}
});
If it would be possible to access the missing permission via the error object I'm able to respond it to the client.
Usually one gets the Statuscode (e.g. 401) from the property "statusCode" and not "status".
Newbie question alert.
Why would you encode permissions in a JWT that gets sent to the user? It's true that we can detect tampering, but nonetheless, why expose this to the world?
Why not get the token, determine which user it is, then decide on which permissions to apply. A database read would be required anyway.
Version: 1.0.0
Node version: 6.11.x
Issue: options
is undefined at https://github.com/MichielDeMey/express-jwt-permissions/blob/master/index.js#L30. It appears that the xtend
functionality is broken.
Hi, i'm using express-jwt-permissions with jwt-simple library.
My JWT is the next:
{
"sub": "5b0330a31606984fbf311c29",
"iat": 1527308253,
"exp": 1558844253,
"permissions": [
"user",
"user:create",
"user:read",
"user:all",
"user:update",
"user:delete"
]
}
Use guard.check('user') in my endpoint but i have an error
example:
api.get('/users', isAuth, guard.check('user'), UserCtrl.getUsers)
someone has a solution?
We shouldn't be calling next
without an exception when requestProperty
is not found.
express-jwt-permissions/index.js
Lines 27 to 28 in d901f21
When express-jwt
isn't there to catch unauthenticated users (since this middleware only deals with authorization and expects authentication to already have been performed) the route would be open to everyone.
In order to prevent such a scenario we should throw an exception stating that the requestProperty
couldn't be found, either because it was incorrectly configured or because authentication has not been performed prior to using express-jwt-permissions
.
Cannot seem to change the requestProperty of my user object to payload
. Double checked req.payload
and its working fine without the guard.
Here's my express-JWT config:
let auth = {
required: jwt({
secret: secret,
userProperty: 'payload',
getToken: getTokenFromHeader
})
}
signing jwt
return jwt.sign({
id: this._id,
username: this.username,
exp: parseInt(exp.getTime() / 1000),
permissions: ['r','w']
}, secret);
Set using let guard = require('express-jwt-permissions')({requestProperty: 'payload'});
Accessing it using guard.check('r')
throws 'user object payload was not found. Check your configuration.'
Hello,
How do i pass in an array of permissions to check?
Let's say the user has permissions: Admin, how can i make the guard to check if any of the users permissions match the ones in the guard?
router.get('/', guard.check(['Admin', 'SuperAdmin'])) does not work.
Hi,
I am using express-jwt-permissions and supertest but I want two things:
I want to stub the middleware that I am using.
I want to pass the guard restricction:
I'll go to share a piece of code:
...
MyRoles: guard.check([
['admin'],
['user']
])
...
app.post("/myendpoint", MyRoles, async (req, res) => {
console.log(1)
}
I don't arrive to the console.log but, if I remove the middleware MyRoles everything works well.
This is my unit test:
it("should respond with a 200", function (done) {
request
.post("/myendpoint")
.set("Authorization", "Bearer eyJhbGciOi.eyJkYXRhIjp7InVzZ")
.expect("Content-Type", /json/)
.expect(200)
.end(function (err, res) {
...
done()
});
});
In the User interface everything works perfectly but I need the two requirements (stub the middleware and pass the middleware by token)
I am hardcoding the token but it didn't pass the middleware, always return 403.
If you have any ideas, please let me know.
Thank you very much.
When a user object is not set, the middleware throws a permissions error.
Not having a user object simply means that the route doesn't require any authentication/authorization.
express-jwt
7.x
changed the default property from req.user
to req.auth
I guess this library could also by default start using that property? (major release)
see https://github.com/auth0/express-jwt#migration-from-v6
workaround for now is to set up the library with
import ejp from 'express-jwt-permissions'
const guard = ejp({
requestProperty: 'auth'
})
Is there a way to configure such that permission should be granted if either "user:read" or "user:write" strings are present in the permissions object array of strings in the token payload?
can you add support for a custom delimiter?
(We are using comma separated list for the permissions)
I am not able to find an typing for the module. For now I used a declaration file. But if it is available please let me know.
unauthorized access returns an exception trace
were/how do I change that to returning an HTML error 403 ?
UnauthorizedError: Permission denied
at Object. (/path/to/my/dev/folder/node_modules/express-jwt-permissions/index.js:4:23)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Function._load (/usr/local/lib/node_modules/pm2/node_modules/pmx/lib/transaction.js:62:21)
at Module.require (module.js:483:17)
at require (internal/module.js:20:19)
at Object. (/path/to/my/dev/folder/index.js:11:49)
express-unless that is being used by packages like express-jwt
allows easily to exclude global middleware (applied using app.use()
) from certain paths.
Having support for this would make it easy for users to check for permissions on all paths except certain ones. In this case things like this could be possible (without extra config):
//Check for write permission unless it's a GET request
app.use(guard.check("write").unless({method: "GET"}));
//Check for write permission unless it's the /auth endpoint
app.use(guard.check("write").unless({path: "/auth"}));
//Combined works of course aswell
app.use(guard.check("write").unless({method: "GET", path: "/auth"}));
Their docs have instructions about how to integrate it with packages.
It is of course possible to use express-unless
without "native support"/integration, but its configuration is a bit confusing and needs a bit of "mixing" and a few more lines of code on the user's side.
I dont get the error
user object "user" was not found. Check your configuration.
here my token:
{
"role": "ADMIN",
"name": "[email protected]",
"lastConnection": "2019-06-26T06:50:52.992Z",
"email": "[email protected]",
"scope": "admin articles",
"exp": 1561540712,
"iat": 1561533512
}
the token is supposed to be like that ?:
{
"role": "ADMIN",
"name": "[email protected]",
"lastConnection": "2019-06-26T06:50:52.992Z",
"email": "[email protected]",
"user": {
"scope": "admin articles",
}
"exp": 1561540712,
"iat": 1561533512
}
because it doesn't work...
I was wondering why the UnauthorizedError sends the HTTP code 403 (Forbidden).
Wouldn't 401 (Unauthorized) make more sense? ;)
We should consider releasing a 1.0.0 version, it inspires more confidence in what is already a stable module and some NPM search engines will otherwise mark it as unstable.
/cc @MichielDeMey
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.