@ssense/auth

The Auth Module is a combination for an HTTP middleware (compatible with express and restify) and a Typescript decorator. Used together, they allow protection for all the routes of your application, handling user authentication and authorizations.

Usage no npm install needed!

<script type="module">
  import ssenseAuth from 'https://cdn.skypack.dev/@ssense/auth';
</script>

README

Auth Module

The Auth Module is a combination of an HTTP middleware (compatible with express and restify) and a Typescript decorator. Used together, they allow to protect all the routes of your application, handling user authentication and authorizations.

How to use

Add the AuthModule middleware

You first have to load the AuthModule middleware to protect all your application's routes. Example usage:

// This example covers restify, but you can also use the middleware if you use express
import { Server } from 'restify';
import { AuthModule } from '@ssense/auth';

// Create restify server
const server = restify.createServer(/* Set your server options here */);

// Create AuthModule (the configuration object is fully optional, refer to the typescript definition file for more detail)
const authModule = new AuthModule({
    // Override the default SSENSE authorization server (only use for development purpose)
    authServerHost: 'test.ssense.com',
    // Use HTTP protocol instead of HTTPS for the authentication server (don't use in production!)
    authServerSecure: false,
    // All the routes will be protected by default, add the routes that you want to keep public here
    publicRoutes: [
        /^\/+$/, // Set home page as public
        /^(\/robots\.txt|\/favicon\.ico)$/ // Only for example purpose: allow anonymous requests to /robots.txt and /favicon.ico
    ],
    publicHttpMethods: [
        'DELETE' // Just an example, in this case all HTTP DELETE requests will be allowed, regardless of the route
    ],
    // Specific callback to handle unauthorized requests, a generic error page will be displayed if not present (res.statusCode set to 403 by default)
    onForbidden: ((req, res, next) => {
        res.send('You are not allowed to access this page');
        next();
    })
});

// Add middleware to our server
server.use(authModule.authenticate());

Just by adding this middleware, all your application's routes will be protected from anonymous requests (a valid ssense.com email address is required). The user's authentication is also fully managed, you can focus on your business case without having to take care about authentication.

Retrieving user's information at the controller level

After loading the AuthModule middleware, all the controllers of your application will have an extra auth param in the request object. This param is an instanceof of AuthInfo, giving the currently logged in user's information, as follows:

interface AuthInfo {
    // Id of the currently logged in user, for an SSENSE employee, will be the corresponding email address
    id: string;
    // Type of the currently logged in account (possible values are "user" and "service")
    type: string;
    // List of scopes available for the currently logged in user
    scopes: string[];
    // Raw JWT token used for authenticating the currently logged in user
    token: string;
    // Shortcut method indicating if the currently logged in account has a given scope
    hasScope(scope: string): boolean;
    // Shortcut method indicating if the currently logged in account has all the given scopes
    hasScopes(scopes: string[]): boolean;
}

Let's take an example:

// Assuming that the AuthModule middleware has already been loaded

// Example controller function
public async myControllerFunction(req, res, next): Promise<any> {
    // req has now an extra "auth" property
    res.send('You are currently logged in as ' + req.auth.id + ', you are logged in as a ' + req.auth.type + ' account');
    res.end();
}

Automatically protect controller functions based on current user's scopes

You can automatically protect your controller's function by allowing access only to users that have the appropriate scopes. To enable this feature, you can use the requireScope() typescript decorator, as in the example below:

// Assuming that the AuthModule middleware has already been loaded

import { AuthModule } from '@ssense/auth';

// Enable the auto protection feature based on user's scopes
@AuthModule.requireScope('scope1')
public async myControllerFunction(req, res, next): Promise<any> {
    // req has now an extra "auth" property
    res.send('You are currently logged in as ' + req.auth.id + ' and you have at least one required scope to access this function');
    res.end();
}

Using the @AuthModule.requireScope('scope1') line, logged in users need to have the specififed scope to access your function. You can also allow access to a function for several scopes, using @AuthModule.requireScope(['scope1', 'scope2']), in this case the authenticated user needs to have at least one of the scopes passed in parameter to be able to access this function.

If they don't have any of the possible scopes, they will get a 403 error page, the response will automatically be sent and your function will not even be called.

If you want to check for several scopes, you can use the @AuthModule.requireAllScopes() decorator, which takes a string[] as parameter. In this case the authenticated user needs to have all of the scopes passed in parameter to be able to access this function.

Alternatively, you can use the same functionality as a middleware:

import { AuthModule } from '@ssense/auth';

@Get('/something', AuthModule.scopeMiddleware(['resources:operation']))
public async myControllerFunction(req, res, next): Promise<any> {
    //...
}

The scopeMiddleware function have an optional 2nd parameter where true is "all of the scopes" and false is "at least one of the scopes". default to true

This has the advantage to be usable on the controller level:

import { AuthModule } from '@ssense/auth';

@injectable()
@Controller('/somethings', AuthModule.scopeMiddleware(['resources:operation'])
export class SomeController extends BaseController {
    //...
}

And can be chainable :

import { AuthModule } from '@ssense/auth';

@Get('/something')
public async getSomething(req: Request, res: Response, next: Next): Promise<void> {
    if(req.query.something){
        AuthModule.scopeMiddleware(['resources:operation'])(req,res,next)
    }
    next()
}