{"id":28407226,"url":"https://github.com/vincezk/authorization","last_synced_at":"2025-06-29T13:32:43.328Z","repository":{"id":65424619,"uuid":"80394312","full_name":"VinceZK/authorization","owner":"VinceZK","description":"An Object Oriented Authorization Framework for Node.js","archived":false,"fork":false,"pushed_at":"2020-09-09T00:04:03.000Z","size":622,"stargazers_count":33,"open_issues_count":0,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-08T23:35:22.656Z","etag":null,"topics":["acl","authorization","authorization-profiles","identity","nodejs","passport","security"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VinceZK.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-30T04:46:10.000Z","updated_at":"2024-04-11T20:10:29.000Z","dependencies_parsed_at":"2023-01-23T10:55:21.566Z","dependency_job_id":null,"html_url":"https://github.com/VinceZK/authorization","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/VinceZK/authorization","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fauthorization","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fauthorization/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fauthorization/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fauthorization/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VinceZK","download_url":"https://codeload.github.com/VinceZK/authorization/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VinceZK%2Fauthorization/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262600610,"owners_count":23335102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acl","authorization","authorization-profiles","identity","nodejs","passport","security"],"created_at":"2025-06-02T00:37:29.082Z","updated_at":"2025-06-29T13:32:43.319Z","avatar_url":"https://github.com/VinceZK.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Authorization\nNode-authorization is an object-oriented authorization framework.\nIt allows end-users rather than developers to define authorizations. \n\nIn real use cases, control which views, APIs, or URI can be accessed or not is far more enough. \nWe require different users have different permissions on different business objects.\nFor example, user A can *edit blogs* in category \"DB\", while user B can *add blogs* in category 'JS'.\nAs the categories are growing, it is impossible for developers to change the codes to adapt. \nInstead, you should let the end-users to do the authorization definitions and assignments. \nIn other words, You'd better to \n\n**Split the authorization logic apart from your application logic.** \n\n## Example\nIn the project [UI-logon](https://github.com/VinceZK/Logon), you can find a comprehensive realization \nof an identification management solution. You may experience yourself in this [Live DEMO](https://darkhouse.com.cn/logon/).\n\n![Maintain Permission and Profile](permission.png)\n\nFollowing code snippet gives you an simple example on how to embed node-authorization in your project.\n```javascript\nconst Authorization = require('node-authorization').Authorization;\nconst compileProfile = require('node-authorization').profileCompiler;\nconst fs = require('fs');\n\n// Read the authorization profile from file system and compile it. \n// You can also store profiles in DB(recommended).\nconst rawProfiles = [\n  JSON.parse(fs.readFileSync('./example/testProfile01', 'utf8')),\n  JSON.parse(fs.readFileSync('./example/testProfile02', 'utf8'))\n];\nconst compiledProfile = compileProfile(rawProfiles);\n\nconst Authorization = new Authorization('UserID', compiledProfile);\nif(!Authorization.check('blog', {Tag:'DB',ID:1000001, Action:'Add'})){\n   //Report a message, and break;\n}else{\n   //Do the add blog;\n}\n```\n\n## Terminologies\n### Authorization Object\n*Authorization Object* usually corresponds to a business object, like \"user\", \"blog\", \"material\", \"order\", and so on.\nSometimes, it can also be an abstract object that is only for the permission check purpose.\nFor example, if you want to show the total blog reads to certain users,\nthen you can create an authorization object like \"blogStatistic\".\n\n### Authorization Field\nAn authorization object can be assigned with more than one *Authorization Fields*. \nUsually, we have the \"Action\" authorization field to indicates the operations allowed to a business object.\nBesides, we can have attributes derived from the business object as authorization fields. \nThis is to facility the permission management by easily group object instances through attributes. \nFor example, it is very common to use organizational attributes to group system users,  \nsuch as \"company\", \"department\", and \"group\".\n\n### Authorization \n*Authorization* describes permissions on a business object.\nIt is an instance of an authorization object with concrete authorization values on each authorization field.\nBelow example shows an authorization of the \"user\" authorization object. \nIt allows the granted identity has the permission to \"Create\", \"Edit\", \"Display\", \"Lock\", and \"Unlock\" \nusers that belong to the \"Ordinary\" group.\n```json\n{   \n    \"AuthObject\": \"user\",\n    \"AuthFieldValue\": \n    {\n        \"Group\": [\"Ordinary\"],\n        \"Action\": [\"Create\",\"Edit\",\"Display\",\"Delete\",\"Lock\",\"Unlock\"]\n    }\n}\n```\n\n**Note:** authorizations of the same authorization object won't be merged when doing authorization checks. \nEach authorization is an independent permission description which is checked separately during runtime. \n\nFor example, if below authorization is assigned to an identity together with the above authorization. \nIt doesn't mean the identity can also do \"Edit\", \"Lock\", and \"Unlock\" on users that belong to the \"Admin\" user groups. \n```json\n{   \n    \"AuthObject\": \"user\",\n    \"AuthFieldValue\": \n    {\n        \"Group\": [\"Admin\"],\n        \"Action\": [\"Create\",\"Display\",\"Delete\"]\n    }\n}\n```\n\n### Authorization Profile\n*Authorization Profile* contains multiple authorizations.\nIt is a logic representation of authorizations that can be assigned to an identity. \nBelow authorization profile contains 2 authorizations:\n\n```json\n[\n    {   \"AuthObject\": \"user\",\n        \"AuthFieldValue\":{\n            \"Group\": [\"Ordinary\"],\n            \"Action\": [\"Create\",\"Edit\",\"Display\",\"Delete\",\"Lock\",\"Unlock\"]\n        }\n    },\n    {\n        \"AuthObject\": \"blog\",\n        \"AuthFieldValue\": {\n            \"Tag\": [\"DB\", \"JS\", \"Algorithm\"],\n            \"ID\": [{\"Operator\":\"Between\", \"Option\":\"Include\", \"Low\":1000000, \"High\":1999999}, 2399999],\n            \"Action\": [\"Post\", \"Edit\", \"Publish\"]\n        }\n    }\n]\n```\n\n**Note:** the system differentiates \"raw profile\" and \"compiled profile\" for the performance reason. \nWhen the authorization profiles are maintained and stored, they are raw profiles.\nWhen the authorization profiles are read for permission checks, they are converted to a compiled profile. \nThis is to achieve better performance by sorting and grouping authorizations.\n \n## To Begin\n\n1. Install it:\n\n    ```bash\n    $ npm install node-authorization --save\n    ```\n    \n2. Require it and use:\n    \n   ```javascript  \n   var Authorization = require('node-authorization').Authorization;\n   var compileProfile = require('node-authorization').profileCompiler;\n   \n   var Authorization = new Authorization('UserID', compiledProfile);\n   if(!Authorization.check('blog', {Tag:'DB',ID:1000001, Action:'Add'})){\n       //Report a message, and break;\n    }else{\n       //Do the add blog;\n    }   \n   ```\n   \n3. You can also refer the `test/authorization` and run the tests by:   \n   ```bash\n   $ npm run test\n   ```\n## With ExpressJS and PassportJS\n\nIt is easy to get confused on authentication and authorization. \nWhile authentication verifies the user’s identity, \nauthorization verifies that the user in question has the correct permissions and rights to access the requested resource.\nThe two always work together. Authentication occurs first, then authorization. \n\n[Passport](http://passportjs.org/) is a popular **authentication** middleware which is compatible with [Express](http://expressjs.com/).\nAnd now you can use **Node-Authorization** as an accompaniment with Passport and Express. \nYou only need to do following 4 steps:\n\n1. In your Passport login strategy, when the user identification is verified, roll-in its authorization profiles:\n    ```javascript\n    passport.use(new LocalStrategy(\n      function(username, password, done) {\n        User.findOne({ username: username }, function (err, user) {\n          if (err) { return done(err); }\n          if (!user) { return done(null, false); }\n          if (!user.verifyPassword(password)) { return done(null, false); }\n          \n          //Begin profiles roll-in\n          //Get user raw profiles, compile them, and save the compiled profile to the session store\n          ...\n          user.authProfile = compileProfile(rawProfiles);\n          //End profiles roll-in\n          \n          return done(null, user);\n        });\n      }\n    ));\n   ```\n\n2. In the passport \"serializeUser\" function, save the compiled profile to the session storage:\n    ```javascript\n    /**\n     * Cache all the identity information to the session store\n     * This method is usually only called once after successfully login\n     */\n    passport.serializeUser(function(identity, done) {\n        done(null, identity);\n    });\n    ```\n\n3. In the passport \"deserializeUser\" function, initialize the Authorization object with the session profile:\n    ```javascript\n    /**\n     * Express Session helps to get identity from the session store (like Redis)\n     * and pass to this method for *Each* request (should exclude static files).\n     * Here, we pass the identity together with Authorization object to req.user.\n     * We should not change the identity object as it will reflect to req.session.passport.user,\n     * which afterwards will be saved back to Redis session store for every HTTP response.\n     */\n    passport.deserializeUser(function(identity, done) {\n      const user = {\n        identity: identity,\n        Authorization: null\n      };\n    \n      if(identity.userid \u0026\u0026 identity.profile)\n        user.Authorization = new Authorization(identity.userid, identity.profile);\n    \n      done(null, user); // be assigned to req.user\n    });\n    ```\n\n4. In your restful APIs, embed the authorization checks:\n    ```javascript\n    addBlog:function(req,res) {\n        if(!req.user.Authorization.check('blog', {Tag:req.body.blog.tag, ID:req.body.blog.ID, Action:'Add'})){\n            res.end('You do not have the permission to add a new blog!');\n        }else\n            blog.addBlog(req.body.blog, function(msg,blogId){\n                ...\n                res.json(msg);\n            }\n         )\n    }\n    ```\n\nWhen a user is logged on, the authorization profiles are compiled and saved in the login session storage. \nThen each time the user performs an action on an object, \ncorresponding authorization checks can be done before it actually happens. \nDevelopers can decide where to embed the \"authorization.check()\" statements.\n\nYou can refer the corresponding implementation in project [UI-logon](https://github.com/VinceZK/Logon/blob/master/server/identification.js),\nas well as its [Live DEMO](https://darkhouse.com.cn/logon/).\n\n## Maintain Authorization Profile\nAuthorization profiles consists of authorizations. \nThey are in JSON format and can be maintained through all possible UI tools by the end-users. \nThey are recommended to be saved in DB so that they can be easily associated with login user IDs. \nYou can develop a role maintenance UI, which generates the authorization profiles. \nWhen the roles are assigned to users, the corresponding authorization profiles are also assigned.\nAgain, you can refer the [Live DEMO](https://darkhouse.com.cn/logon/) and the project [UI-logon](https://github.com/VinceZK/Logon).\n\nA user can be assigned with multiple authorization profiles, \nand each authorization profile includes multiple authorizations. \nEach authorization has one authorization object and more than one authorization fields. \nAn authorization field is assigned with authorization values to indicate the granted permissions.\n\nFor example, the following profile contains an authorization object named \"blog\", which has fields \"Tag\", \"ID\", and \"Action\".\n\n```javascript\n[\n    ...\n    {\n        \"AuthObject\": \"blog\",\n        \"AuthFieldValue\":{\n            \"Tag\": [\"DB\", \"JS\", \"Algorithm\"],\n            \"ID\": [{\"Operator\":\"Between\", \"Option\":\"Include\", \"Low\":1000000, \"High\":1999999}],\n            \"Action\": [\"Post\", \"Edit\", \"Publish\"]\n        }\n    },\n    ...\n]\n``` \n\nThe permission described by above profile stands for the user who is assigned can do `Post, Edit, and Publish` \non `blogs` which are tagged with `DB, JS, or Algorithm` and whose ID are `between 1000000 and 1999999`.\n\nThe authorization value of a field is usually an array, \nwhich can contain elementary values(string or integer) or **Select Options**, and they can be mix. \nIf you want the full permission, just assign the wildcard \"*\" to the field.\n\nA select option is described by 4 attributes: \"Operator\", \"Option\", \"Low\", and \"High\", which are detailed bellow: \n\n### Operator\nNow, following operators are supported:\n\n1. **Between**: between the Low value and High value, the Low and High are both included.\n\n2. **GreaterThan**: greater than the Low value, the High value is ignored. \n\n3. **LessThan**: less than the Low value, the High value is ignored. \n\n4. **GreaterEqual**: greater than or equal to the Low value.\n\n5. **LessEqual**: less than or equal to the Low value.\n\n6. **Equal**: equal to the Low value.\n\n7. **NotEqual**: not equal to the Low value.\n\n8. **StartsWith**: the check value is string, and starts with the Low value.\n\n9. **EndsWith**: the check value is string, and ends with the Low value.\n\n10. **Contains**: the check value is string, and contains the Low value.\n\n11. **Matches**: the Low value is a regular expression.\n\n### Option\nIt only contains 2 possible value: \"Include\" or \"Exclude\". \nAnd \"Exclude\" is just the complement set of \"Include\".\n\n### Low \u0026 High\nStands for the lower value and higher value. \nHigher value is currently only used in the \"Between\" operator.\n\n### Authorization Maintenance UI\n\n![Authorization Profile](profile_blog.png)\n\n![Maintain Authorization Value](authorization.png)\n\n## Switch Authorization Trace\nIt is very useful to know which permissions are missing \nwhen certain authorization checks fail, or when developers want to know which authorization objects are checked\nduring a certain operation. \n\nYou can switch the authorization trace on by calling following global function:\n\n```javascript\nAuthorization.switchTraceOn();\n```\nor switch off:\n\n```javascript\nAuthorization.switchTraceOff();\n```\n\nThe trace result is output to the console:\n\n```bash\nIdentity is \"Vincezk'\"\nAuthorization object: \"comment2\"\nRequired permission: {\"blogID\":3999999,\"Content\":\"hello there\",\"Action\":\"Post\"}\n\n Authorization object: \"comment4\"\n  Required permission: {\"blogID\":3999999,\"Content\":\".... Shit\",\"Action\":\"Post\"}\n   Granted permission: {\"AuthObject\":\"comment4\",\"AuthFieldValueComposition\":[{\"blogID\":[{\"Operator\":\"GreaterThan\",\"Option\":\"Exclude\",\"Low\":4000000}],\"Content\":[{\"Operator\":\"EndsWith\",\"Option\":\"Exclude\",\"Low\":\"Shit\"}],\"Action\":\"*\"}]}\n```\n\nThe trace switch on/off does not require the restart of the node processes. It is a hot switch. \nIf you provide a function to allow end-users to switch on/off, then it will be much more convenient.\n\n## License\n[The MIT License](http://opensource.org/licenses/MIT)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincezk%2Fauthorization","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvincezk%2Fauthorization","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvincezk%2Fauthorization/lists"}