This package is the authentication and authorization library to be used by the MyHealthPass health system.
The package was written in TypeScript and npm packages have been created for the two releases.
An example of how it can be used within a Node.js web application is shown below.
npm
npm install myhealthpass-auth
To use the package the auth
object must be instantiated as seen below:
import { AuthApp } from 'myhealthpass-auth';
// ...
const config = {
authSecret: 'my-secret',
accountsStore: new YourAccountStore(),
};
const authApp = new AuthApp();
const auth = authApp.configure(config);
The YourAccountStore
object would be your concrete implementation of the provided IAcccountStore
interface, to handle the retrieval and storage of accounts to an actual data store.
The auth
object can then be used to call the various methods.
// register
const details = new AccountDetails('username');
const registerResult: Account = auth.register(details, 'password');
// login
const loginResult: Account = auth.login('username', 'password');
// authenticate token
const valid: boolean = auth.authenticate(loginResult.token!);
// upload a profile picture
auth.accountHandler.saveProfileImage('username', 'base64-profile-picture');
The configuration object used above comes with default values described in the case study. However, for extensibility, these configuration values can be overridden.
const config = {
authSecret: 'my-secret',
accountsStore: new YourAccountStore(),
imageFolderLocation: "C:\\Users\\userprofile\\Downloads",
bruteDetection: {
failedCount: 10, // for 10 failed attempts
requestTimePeriod: 10, // within 10 secs
lockedPeriod: 5 // lock requests for 5 secs
}
};
The following are the data models used within the package:
-
Account
: This is used as a representation of the registered or logged in user. It is used to track the user token and whether or not the user account is locked. -
AccountDetails
: This is used as a representation of data passed to the library during registration.
Assumption: The consumer should make use of the
Account
andAccountDetails
models to map to data in their backing database/storage. It is also used to track the profile picture and region the user belongs to.
-
Region
: This is used to track what region the user belongs to and therefore what policies should be applied to them.
Assumption: All regions would have the default lockout policy, as described by the 3 failed login attempts, in the case study.
-
Request
: This is used to track the login request made to the library.
Assumption: The consumer's web API is responsible for the population of this object when calling the
login
method. Any calls to thelogin
method without the populatedRequest
object will fail.
-
RequestTrace
: This is used to track additional data about theRequest
and therefore detect possible brute force attacks.
Assumption: Regions are representations of time zones.
Therefore a list of possible regions is created by the package for use to add to an account. The consumer will not be creating their own regions.
As stated in the case study, different authentication rules such as session length, password policies and user lockout policies are configured by region. As such, interfaces were created for the possible password and user lockout policies, called IPasswordPolicy
and ILockoutPolicy
, respectively.
Assumption: The locking of a user's account after 3 failed login attempts, is a type of user lockout policy. Each region has therefore been given a default
ILockoutPolicy
ofFailedLoginPolicy
.
There are also helper methods available to add and remove policies from regions in the RegionHandler
class.
These are:
- addPasswordPolicy(regionIdentifer: string, policy: IPasswordPolicy)
- removePasswordPolicy(regionIdentifer: string, policyIdentifer: string)
- addLockoutPolicy(regionIdentifer: string, policy: ILockoutPolicy)
- removeLockoutPolicy(regionIdentifer: string, policyIdentifer: string)
The following are the interfaces available for use in the package:
-
IAccountStore
: The inheriting class should be a concrete implementation that uses the backing database/storage to:getAccount(username: string)
addNewAccount(account: Account)
recordProfilePhoto(username: string, base64ImageStr: string)
-
ILockoutPolicy
: This is a type of user lockout policy that can be added to a region.
The implementations would determine the conditions for the lock with:-
shouldLock(obj: any): boolean
And set the lock period with: lockPeriod: number
-
-
IPasswordPolicy
: This is a type of password policy that can be added to a region.
The implementations would determine the conditions for the password policy with:validate(password: string): boolean
Unit tests have been created for the case study and can be run by using a terminal in the solution directory to run:
npm install
To ensure that the required packages are installed.
Then run:
npm run test
The following are some assumptions made while developing the solution:
- Both the
login
andregister
methods return anAccount
object that includes a token which the user can use to authenticate. - This package does not handle the actual storage of data. The consumer should make use of the
Account
andAccountDetails
models to map to data in their backing database/storage after login and registration. - The consumer should implement the
IAcccountStore
to handle the actual retrieval and storage of accounts. - The
login
method was expanded to include aRequest
object parameter for brute force prevention.- This object must contain the properties of user agent, client IP address and request signature.
- A brute force attack would come from a malicious person calling the consumer's web API. Therefore the consumer must ensure that the user agent, client IP address and request signature are captured for each call to their web API and passed along to the library to validate against.
- Any calls to the
login
method without the populatedRequest
object will fail.
- Regions are representations of time zones.
-
An account can only belong to one region, therefore the region will get set at the time of registration, either through a
timezone
property passed withaccountDetails
during registration or set by the user's current timezone, if one is not provided. - The locking of a user's account after 3 failed login attempts, is a type of user lockout policy.
- All regions would therefore have this default lockout policy, as described by the 3 failed login attempts, in the case study.
- The ability to upload a profile picture is handled by the
saveProfileImage
method in theAccountHandler
class.- This method is called during the registration process, as well as can be called by the consumer within their application.
- The consumer should provide a folder path to store the profile image through the configuration setting. This will be used to store the image on the file system.
- Additionally, the consumer should implement the
recordProfilePhoto
method in theIAcccountStore
interface, to do any additional storing or processing of the image, as it is also called by thesaveProfileImage
method.
- UTC dates are used for all date calculations to avoid timezone conflicts.
- All time period configuration options represent seconds.
The diagrams describing the architecture can be seen here.
The provided Node.js web application under examples > node-app, can use used to test the implementation of the package.
- Download the node-app folder
- Open a terminal in the folder and run
npm install
- Run the node app by running
npm run start
- Using a tool for testing API requests, make a request to the endpoint of the running node application, likely http://localhost:3000.
POST http://localhost:3000/register
Example Body:
{
"accountDetails": {
"username": "bob",
"fullName": "Bob Smith",
"timezone": "America/La_Paz",
"profilePicture": "base64string"
},
"password": "password1"
}
POST http://localhost:3000/login
Example Body:
{
"username": "bob",
"password": "password1"
}
POST http://localhost:3000/authenticate
Example Body:
{
"token": "token-received-after-login-or-register",
}