

# exFrame-Security

## Environment Variables

AUTH0_AUDIENCE
This environment variable is what is validated against the JWT token coming from Auth0. This must match each environment the token will be generated.

AUTH0_DOMAIN
The domain is the Auth0 domain to the jwks well-known json service. This json service is used to validate the public key of the jwks RS256 token generated by Auth0

## Express JWT Middleware
`exframe-security` allows microservices to secure the express router REST endpoints using this middleware. The user profile will be returned as a `user` property on the request object. The middleware will automatically return an HTTP error code 401 if the token is not valid or does not exist. The first is the user object from the id token passed with the request. The other is a csp string property that denotes the harmony-csp header passed with the request.

### Implementation
```javascript
const security = require('exframe-security');

route.get('/someroute', security.expressJwtMiddleware, (req, res, next) => {
  const user = req.user;
  next();
});
```

### Overriding the audience
The middleware can also be called with an options object to build a middleware that overrides the `AUTH0_AUDIENCE` environment variable. `audience` accepts either an array or a comma delimited string, the same as the environment variable.

```javascript
const security = require('exframe-security');

const jwtCheck = security.expressJwtMiddleware({ audience: ['https://my-audience'] });

route.get('/someroute', jwtCheck, (req, res, next) => {
  const user = req.user;
  next();
});
```

## Redbird JWT Middleware
`exframe-security` allows for the [Exzeo Redbird Node JS Proxy](https://github.com/exzeo/redbird) to secure the proxy endpoints using this middleware. The middleware will automatically return an HTTP error code 401 if the token is not valid or does not exist. The middleware will return two properties on the request. The first is the user object from the id token passed with the request. The other is a csp string property that denotes the harmony-csp header passed with the request.

### Implementation
```javascript
const security = require('exframe-security');

redbird.addResolver({ match: /^\/(.*)/, priority: 10 }).use(security.redbirdJwtMiddleware)...

```

## Falcon Security
`exframe-security` allows microservices to retrieve a users profile from Falcon. The user profile from Falcon is different from the profile returned from the Auth0 ID token.

### Implementation
```javascript
const security = require('exframe-security');

route.get('/someroute', security.falconSecurityMiddleware, (req, res, next) => {
  const user = req.user;
  next();
});
```
### RPC Implementation
```javascript
const security = require('exframe-security');

rpcServer.func('doSomething', security.falconSecurityRpcMiddleware, async (context, args) => {
  const user = context.user;
  next();
});
```

## Check Claim Security
`exframe-security` allows microservices to retrieve a users profile from Falcon as either middleware or a standalone function. The user profile from Falcon is different from the profile returned from the Auth0 ID token.

### Optional Options Object

- `applicationToken` - Configures `exframe-security` to pass the `harmony-app-token` to falcon for user authorization

### Middleware Implementation

```javascript
const security = require('exframe-security');

// Usage with REST requests (Express)
route.get('/someroute', security.checkClaimMiddleware({ applicationToken: 'XXXXXX' }).express, (req, res, next) => {
  const user = req.user;
  next();
});

// Usage with RPC
rpcServer.func('doSomething', security.checkClaimMiddleware({ applicationToken: 'XXXXXX' }).mq, async (context, args) => {
  const user = context.user;
  next();
});
```

### Standalone Implementation

```javascript
const security = require('exframe-security');

const user = await security.checkClaim(context, accessKey, { applicationToken: 'XXXXXX' });
```

### Internal Tokens

Internal tokens may also be created with a valid access token for use with internal requests. This makes it easier for internal services to make requests against resources that may not be accessible for the authenticated user of the original request. See [here](https://issuecenter.atlassian.net/wiki/spaces/SDLC/pages/737771538/Internal+Tokens) to learn more about internal tokens.

An internal token may be created using the `createInternalToken` function of the `user` object returned  by the `express` and `mq` check claim middleware.

```javascript
const security = require('exframe-security');

rpcServer.func('doSomething', security.checkClaimMiddleware().mq, async (context, args) => {
  const user = context.user;
  const internalToken = await user.createInternalToken(context)
  next();
});
```

## Resource Registration
`exframe-security` allows microservices to register securable resources with the security manager. The developer will need to add a `.securityresourcesrc.js` file that specifies each resource acted upon by the service on behalf of a user and the associated rights that may be granted a particular user. The developer will need to pass the instance of the mq module pre configured.

### Implementation
```javascript
const { resourceRegistrator } = require('exframe-security');

resourceRegistrator.registerResources({ mq });
```

### Example .securityresourcesrc.js File
```javascript
module.exports = {
  resources: [
    { code: 'SECURITY_RESOURCES', service: 'security-manager-service', name: 'Resources', uri: 'SecurityModule:SecurityResources', allowedRights: ['READ'] }
  ]
};
```

## Resource Validation
`exframe-security` allows microservices to validate the requesting user has access to the securable resources they are trying to access. There are a pair of middleware functions to apply to either a Rest or RPC server, one to set up the list of resources a user has that are relevant to the service, and another to check if a user has a access to a specific resource/right pair.

### setupUserSecurityResourceMiddleware
`setupUserSecurityResourceMiddleware` will apply the user resources as an array for the given service and provide a helper method for identifying whether the user has the desired access.

The following properties will be added to the `request.user` object by this middleware function:
- `userSecurityResources` {`object[]`} - List of user resources used by the services
- `userSecurityResources[].right` {`string`} - Access right granted for the resource (e.g. `"READ"`)
- `userSecurityResources[].uri` {`string`} - Resource identifier (e.g. `"PolicyData:Transactions:*"`)
- `userSecurityResources[].mongoFilter` {`object`} - Complete mongo query object with all resource conditions applied used to easily filter only data accessible to the user
  ```javascript
  {
    $and: [
      { $or: [{ companyCode: 'TTIC', state: 'FL', product: 'HO3' }] },
      { agencyCode: { $in: [20001] } }
    ],
  }
  ```

> Note: The RPC version of the function has a slightly different name: `setupUserSecurityResourceRpcMiddleware`
> Note: The "exframe-request" implementation of the function is named: `setupContextUserSecurityResourceMiddleware`

#### Express Example

```javascript
const { setupUserSecurityResourceMiddleware } = require('exframe-security');

const securityResources = require('./.securityresourcesrc');
const userSecurityResourceMiddleware = setupUserSecurityResourceMiddleware(securityResources.resources);

app.use(userSecurityResourceMiddleware);

app.get('/', (request, response, next) => {
  if (!request.user.checkResourceAccess('RESOURCES:*', 'UPDATE')) {
    throw new Error('Forbidden');
  }

  next();
});

app.get('/resources', (request, response, next) => {
  const { mongoFilter } = request.user.userSecurityResources
    .find(resource => resource.uri === 'RESOURCES:*' && resource.right === 'READ');
  };

  // ... mongo query
});
```

#### RPC Example

```javascript
const { setupUserSecurityResourceRpcMiddleware } = require('exframe-security');

const securityResources = require('./.securityresourcesrc');
const userSecurityResourceMiddleware = setupUserSecurityResourceRpcMiddleware(securityResources.resources);

rpcServer.use(userSecurityResourceMiddleware);

rpcServer.func('doSomething', async (context, args) => {
  if (!context.user.checkResourceAccess('RESOURCES:*', 'UPDATE')) {
    throw new Error('Forbidden');
  }

  // ... do something
});

rpcServer.func('accessSomething', async (context, args) => {
  const { mongoFilter } = request.user.userSecurityResources
    .find(resource => resource.uri === 'RESOURCES:*' && resource.right === 'READ');

  // ... mongo query
});
```

#### exframe-request Example

```javascript
const exframeContext = require('exframe-context');
const { setupContextUserSecurityResourceMiddleware } = require('exframe-security');

const securityResources = require('./.securityresourcesrc');
const userContextSecurityResourceMiddleware = setupContextUserSecurityResourceMiddleware(securityResources.resources);

exframeContext.use(userContextSecurityResourceMiddleware);

```
### checkResourceAccessMiddleware
The `checkResourceAccessMiddleware` provides two functions, `express` and `rpc`, that will return a middleware function to validate that a user has access to a given resource/right pair.

The middleware function has the following arguments:

- `resourceUri` - The URI of the resource that the calling users will need access to
- `right` - The right against the URI that the calling users will need access to

#### Express Example

```javascript
const { checkResourceAccessMiddleware } = require('exframe-security');

const checkResourceAccess = checkResourceAccessMiddleware.express('Resources:*', 'READ');

app.get('/', checkResourceAccess, (request, response, next) => {
  next();
});
```
#### RPC Example
```javascript
const { checkResourceAccessMiddleware } = require('exframe-security');

const checkResourceAccess = checkResourceAccessMiddleware.rpc('Resources:*', 'READ');

rpcServer.func('doSomething', checkResourceAccess, async (context, args) => {
  // ... do something
});
```
