@nathangroup/authorize
v2.0.6-8
Published
a jwt authorize module
Downloads
78
Maintainers
Keywords
Readme
Dependencies
This package uses jsonwebtoken
, sqlite
and express
as dev dependencies and will install when you install the modules on installation. The package will also create a keys folder and generate the necessary private and public keys, it will also create a roles DB for you to use if need be
Implementation
const auth = require('@nathangroup/authorize')
You can also run the keys generator on the app.js or your index file so as to generate the relevant keys on server startup. The keys will only be generated once
The following is a sample implementation for the same
const { GenerateKeyPair } = require('@nathangroup/authorize');
GenerateKeyPair();
imports the module to your project.
Methods
IsAuthenticated
- Checks for a token on the headers and authorizes a given route
GenerateToken("token string")
- Generates a token with encrypted data
GenerateKeyPair()
- Generates a key pair of public and private .pem files
EncryptText("encryption salt", "string to be encrypted")
- Encrypts data over a custom algorithm
DecryptText("encryption salt", "string to be decrypted")
- Decrypts data which have been encrypted using the EncryptText method
keyEncrypt("string to be encrypted")
- Encrypts data using private and public keys
keyDecrypt("string to be decrypted")
- Decrypts data using private and public keys
RoleAuthenticated
- Authenticates a user based on a given role
createRole({ /* role Object */ })
- creates a role in the database. The route anticipates an object containing
{
name: "name of the role", permissions: [
{ "name": "frontend-route",
"permissions": { "read": true, write: true },
"contains": [ "route-name e.g users" ]}], id
}
getAllRoles
- returns all the roles in the database
updateRole({ /* object of what is to be updated */ })
- updates a role in the database. The route anticipates an object containing
{ name: "name of the role", permissions: [], id: 1 /* role id being updated */ }
the permissions are optional but the id and name are a must
getRoleById(id)
- Return a single role by the id as parsed in the parameter
deleteRole(id)
- deletes a given role in the DB, the ID parameters are a must
getServerSavedRoles
- This route gives you all the roles saved in the server sqlite db
IsLocalAuthenticated
- this method authenticates a Nathan digital token and can be used in place of IsAuthenticated
when local tokens are used
VerifyNathanToken
- this method is used to verify the token if you feel the need to verify the token then use the data there to create a middleware
GenerateNathanToken()
- this method generates a nathan digital token. payload
are a required field. The payload can be a string or a an object, the rest expiry, type
are optional fields. Without the expiry the token will be valid for 1 year, the data type should be a date object, we advice using Date() objects that are in javascript. The type is a string showing which type the token is, i.e is it ana access or a refresh token. ps - this token is meant to be issued by Nathan and Nathan group
routesList(app)
- this gives all the routes in the application, you can pass the app or routes in the parameters and it will give all the routes in the app
app.get("/all-routes", (req, res) => {
const data = routesList(routes, { prefix: "/v1" });
res.status(200).json({ data });
});
check if token is valid
auth.IsAuthenticated
when applied to a route takes you next or gives you an error e.g
router.get("/home",auth.IsAuthenticated, myHomePage)
this will check for the Authorization token in the headers and check if it accepts tokens of type
Bearer, Api_key, Refresh, Access
generate tokens
auth.GenerateToken('token content as a string', 'expiry as a string')
the expiry could be in days, hours, minutes etc e.g 1d for one day etc. The generate token will create a keys folder in the parent folder which will contain two files a private and public .pem files if there is none. If there is one the module will use the available pem files to encrypt and decrypt data.
The token is a jwt encrypted with rs512 algorithm. The tokens contains a payload which has been encrypted and upon authentication the methods sets a res.user object based on the payload provided as decrypted.
Encryption and Decryption
The module can also be used for encryption and decryption by calling
auth.EncryptText('string to be encrypted')
and
auth.DecryptText('string to be decrypted')
and they both accept and return a string value. These encrypt and decrypt texts and they are not as strong encryption. These two encryption methods can also be used on the frontend in the case one needs to encrypt data while sending it to the server. The data encrypted must use the same package version so as to ensure the encryption and decryption is the same.
The other encryption methods are keyEncrypt
and keyDecrypt
which use the servers private and public keys to encrypt data. They can be used as
auth.keyEncrypt('string to be encrypted')
and
auth.keyDecrypt('string to be decrypted')
they will both return a string of the encrypted data and decrypted data respectively.
Encrypt("string to be encrypted")
Decrypt("string to be decrypted")
The above methods encrypt string through the crypto module. Their usage creates a new string containing encryption methods that are stronger than EncryptText and DecryptText. This is also the recommened method to use if one is to moce data in an encrypted format since this wont change even when we change the keys change
Keys generation
The module can generate keys by calling auth.GenerateKeyPair()
this will generate a keys folder with a public and private .pem files which should not be pushed to a git repository.
req.user
the req.user object is set and should be an object parsed as a string preferably containing and any other fields needs
{_id: '', first_name: '', last_name: '', email: ''}
req.refresh
the req.refresh is set up when one is using the VerifyRefreshToken middleware ensuring and checking if the refresh token given is valid
{_id: '', first_name: '', last_name: '', email: ''}
Nathan Digital Token
Function:
GenerateNathanToken(payload, expiry, type)
Description
This function generates a Nathan token, which appears to be a custom token format used by "Nathan & Nathan Group". The token is encrypted using two encryption functions, keyEncrypt and Encrypt, which are assumed to be imported from a separate ./encryption module.
Parameters:
payload (object)
- The data to be encoded in the token. This should be a JavaScript object or a string.
expiry (Date, optional)
- The expiry time of the token. If not provided, the token will expire in one year.
type (string, optional)
- The type of token (e.g., "Access Token", "Refresh Token"). Defaults to "Access Token".
Returns:
string
The encrypted Nathan token.
Example Usage:
const payload = { user_id: 123, username: "johndoe" };
const token = auth.GenerateNathanToken(payload);
console.log(token);
Functions
VerifyNathanToken(token)
- This function takes a token as input and verifies it. It checks the following things:
- The issuer of the token is "Nathan & Nathan Group"
- The token is not expired
- The token is properly formatted (has 3 parts separated by periods)
- If the token is valid, it returns the decoded payload of the token.
Otherwise, it returns an error message.
IsLocalAuthenticated(req, res, next)
This is a middleware function that can be used to protect routes in a Node.js application.
- It checks for a token in the Authorization header of the request.
- If a token is found, it calls the VerifyNathanToken function to verify it.
- If the token is valid, it adds the decoded payload of the token to the request object as the user property.
- If the token is invalid, it returns a 401 Unauthorized response to the client.
- If no token is found, it returns a 401 Unauthorized response to the client.
this method retrieves the token from the Authorization header of the request.
- If there is no token, it returns a 401 Unauthorized response to the client.
- If the token is present, it splits the token into an array of parts.
- It checks the format of the token to make sure it has 3 parts separated by periods and that the first part is one of
Bearer, Api_key or Token
- If the token is valid, it adds the decoded payload of the token to the request object as the user property and calls the next function to continue processing the request.
- If the token is invalid, it returns a 401 Unauthorized response to the client.
Role structure
for the roles to work you can call the auth.RoleAuthenticated
method and the route will work as defined below. The super-admin role is created automatically and it has permission to all roles in the API.
The roles will be saved in an sqlite database so as to reduce the latency time and be incorporated int the tokens, this will enable the role to be checked when the token is being checked and only then shall the data be given back to the client. The roles have the freedom to be manipulated from the frontend as the user decides. The frontend team can create a page to change the permissions.
The package will create a new file named roles.model.js. This file will aid in saving the roles in mongoDb as the main storage but role verification will happen from the sqlite db that is created in the keys folder for speed and agility.
Please ensure that the user object in the users object has a role field that points to the roles collection in mongoDb, these keys will also be used in the backend when making queries to the sqlite support Db
{
"name": "super-admin",
"permissions": [
{
"name": "dashboard",
"permissions": {
"read": true,
write: true
},
"contains": [ "tasks", "profile" ]
}
]
}
This structure is checked when one is using a specific route.
The name
of the role is the main identifier e.g a super-admin, sales consultant etc. The permissions
array contains objects that determine if the given role is verified. the name
in the object is the route as defined by the frontend team. E.g /home
on the frontend, the permissions
are boolean values of read
and write
, read will have the GET method and write will have the POST, PATCH and DELETE methods.
According to the MCS template each route is more or less like this
/v1/users/all-users
Roles are checked on the second value and in this case the users, this are the values that are in the contains
array, if the array is empty then the algorithm will check for the route named as the name of the frontend route.
To have the roles work as intended please have the user Object contain a role
key with the id of the said role in the database. e.g role: 1
for the said role linking the role to the user.
Dart & Frontend password encryption
The following code block works in encrypting data such as the login password and email. The idea is that the login should be encrypted to avoid MITM attacks on the login credentials.
ps the encryption strings should be the same on the API as well as on the frontend and mobile
The dart/flutter code
final String encryptionSalt = "use your new salt here";
String encodeText(String string) {
List<int> encStr = utf8.encode(string);
List<String> arrayFromString =
encStr.map((point) => point.toRadixString(16)).toList();
return arrayFromString.join("");
}
String encrypt(String salt, String text) {
List<int> textToChars(String text) =>
text.runes.toList(); // For Dart, String.runes returns the Unicode code points of the string.
String byteHex(int n) => n.toRadixString(16).padLeft(2, '0');
int applySaltToChar(int code) =>
textToChars(salt).fold(code, (a, b) => a ^ b);
return text
.split('')
.map((char) => textToChars(char)[0])
.map(applySaltToChar)
.map(byteHex)
.join("");
}
String encryptText(String text) {
String encodedText = encodeText(text);
return encrypt(encryptionSalt, encodedText);
}
Frontend Usage
This package can be sparingly used in the frontend, some of the methods might not apply unless used in a server method i.e Nuxt server or Next server or even Sveltekit server. In the normal react, vue the tested working methods are the EncryptText()
and the DecryptText()
methods.
These methods used with a single salt between themselves and the backend can greatly aid in the encryption and decryption methods.
to use them you can use
import { EncryptText, DecryptText } from '@nathangroup/authorize';
// For encryption
EncryptText('salt', 'text to be encrypted');
// For decryption
DecryptText('salt', 'text to be decrypted');