Skip to content

Commit

Permalink
Merge pull request #38 from vzakharchenko/add_express_middleware
Browse files Browse the repository at this point in the history
add express middleware
  • Loading branch information
vzakharchenko committed Apr 30, 2021
2 parents f720411 + a2704b2 commit fe0957e
Show file tree
Hide file tree
Showing 52 changed files with 6,846 additions and 1 deletion.
18 changes: 18 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,21 @@ jobs:
- run:
name: lint example/keycloak-cloudfront-portal cloudfront
command: cd example/keycloak-cloudfront-portal && npm run lint
- run:
name: lint example/chain-service-calls/frontend
command: cd example/chain-service-calls/frontend && npm i && npm run lint
- run:
name: lint example/chain-service-calls/service1
command: cd example/chain-service-calls/service1 && npm i && npm run lint
- run:
name: lint example/chain-service-calls/service2
command: cd example/chain-service-calls/service2 && npm i && npm run lint
- run:
name: lint example/chain-service-calls/service3
command: cd example/chain-service-calls/service3 && npm i && npm run lint
- run:
name: lint example/express/frontend
command: cd example/express/frontend && npm i && npm run lint
- run:
name: lint example/express/express-service
command: cd example/express/express-service && npm i && npm run lint
6 changes: 6 additions & 0 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,11 @@ jobs:
- run: cd example/keycloak-cloudfront-portal/lambda-edge-example && npm i && npm run build
- run: cd example/keycloak-cloudfront-portal && npm i && npm run build
- run: cd example/keycloak-cloudfront-portal && npm run lint
- run: cd example/chain-service-calls/frontend && npm i && npm run lint
- run: cd example/chain-service-calls/service1 && npm i && npm run lint
- run: cd example/chain-service-calls/service2 && npm i && npm run lint
- run: cd example/chain-service-calls/service3 && npm i && npm run lint
- run: cd example/express/frontend && npm i && npm run lint
- run: cd example/express/express-service && npm i && npm run lint


1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ Icon?
.webpack/*
.coverage/*
.tgz
.webpack
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ example/*
.circleci/*
.idea/*
.coverage
.webpack
__mocks__
__tests__
keycloak-cloudfront-dynamodb
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ npm install keycloak-lambda-authorizer -S
```
# Examples
- [Serverless example (Api gateway with lambda authorizer)](example/keycloak-authorizer/README.md)
- [Example of expressjs middleware](example/express)
- [Example of calling a chain of micro services, where each service is protected by its secured client](example/chain-service-calls)
- [CloudFront with Lambda:Edge example](example/keycloak-cloudfront/README.md)
- [CloudFront with portal authorization (switching between security realms)](example/keycloak-cloudfront-portal)
# How to use
Expand Down Expand Up @@ -728,6 +730,35 @@ keycloakJson,
});
}
```
## 15. ExpressJS middleware

```
const fs = require('fs');
const { middlewareAdapter } = require('keycloak-lambda-authorizer');
function getKeycloakJSON() {
return JSON.parse(fs.readFileSync(`${__dirname}/keycloak.json`, 'utf8'));
}
const app = express();
app.get('/expressServiceApi', middlewareAdapter(
getKeycloakJSON(),
{
enforce: {
enabled: true,
resource: {
name: 'service-api',
},
},
},
).middleware,
async (request, response) => {
response.json({
message: `Hi ${request.jwt.payload.preferred_username}. Your function executed successfully!`,
});
});
```


# If you find these useful, please [Donate](https://secure.wayforpay.com/button/b18610f33a01c)!
112 changes: 112 additions & 0 deletions __tests__/src/adapter/middlewareAdapterTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
jest.mock('../../../src/keycloakAuthorizer');
jest.mock('../../../src/Jwks');
jest.mock('../../../src/utils/optionsUtils');
jest.mock('jsonwebtoken');
const jwt = require('jsonwebtoken');
const keycloakAuthorizer = require('../../../src/keycloakAuthorizer');
const optionsUtils = require('../../../src/utils/optionsUtils');

const jwks = require('../../../src/Jwks');

const { middlewareAdapter } = require('../../../src/adapter/middlewareAdapter');

describe('testing middlewareAdapter', () => {
beforeEach(() => {
keycloakAuthorizer.adapter.mockImplementation(async () => 'adapter');
jwks.jwksUrlResponse.mockImplementation(() => 'jwksUrlResponse');
jwt.decode.mockImplementation(() => {});
optionsUtils.commonOptions.mockImplementation(() => ({ logger: console }));
});

afterEach(() => {
});

test('test middlewareAdapter jwks', async () => {
optionsUtils.commonOptions.mockImplementation(() => ({
logger: console,
keys: {
publicKey: {
key: 'test',
},
},
}));
const ma = middlewareAdapter({}, {});
await ma.middleware({ baseUrl: '/service/jwks' }, {
json: (message) => {
expect(message).toEqual('jwksUrlResponse');
},
}, {});
});

test('test middlewareAdapter', async () => {
optionsUtils.commonOptions.mockImplementation(() => ({
logger: console,
}));
const ma = middlewareAdapter({}, {});
await ma.middleware({ headers: { authorization: 'Bearer auth' } }, {
json: (message) => {
expect(message).toEqual('jwksUrlResponse');
},
}, () => {
expect(1).toEqual(1);
});
});

test('test middlewareAdapter Error', async () => {
optionsUtils.commonOptions.mockImplementation(() => ({
logger: console,
}));
keycloakAuthorizer.adapter.mockImplementation(async () => { throw new Error('111'); });
const ma = middlewareAdapter({}, {});
await ma.middleware({ headers: { authorization: 'Bearer auth' } }, {
status: (code) => {
expect(code).toEqual(403);
return {
end: () => {
expect(1).toEqual(1);
},
};
},
}, () => {
throw new Error('Error');
});
});

test('test middlewareAdapter Authorization Error 1', async () => {
optionsUtils.commonOptions.mockImplementation(() => ({
logger: console,
}));
const ma = middlewareAdapter({}, {});
await ma.middleware({ headers: { } }, {
status: (code) => {
expect(code).toEqual(403);
return {
end: () => {
expect(1).toEqual(1);
},
};
},
}, () => {
throw new Error('Error');
});
});

test('test middlewareAdapter Authorization Error 2', async () => {
optionsUtils.commonOptions.mockImplementation(() => ({
logger: console,
}));
const ma = middlewareAdapter({}, {});
await ma.middleware({ headers: { authorization: 'test' } }, {
status: (code) => {
expect(code).toEqual(403);
return {
end: () => {
expect(1).toEqual(1);
},
};
},
}, () => {
throw new Error('Error');
});
});
});
Binary file added docs/keycloak-cross-client-authentication3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/user1Chain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/user2Chain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/userChain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions example/chain-service-calls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Chain of service calls
![](../../keycloak-cross-client-authentication3.png)

## 1. Start Keycloak

### Docker
Using the image from https://hub.docker.com/r/jboss/keycloak/
```
docker run -p 8090:8080 -e JAVA_OPTS="-Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.profile.feature.upload_scripts=enabled -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true" -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -v `pwd`/example/chain-service-calls:/chain-service-calls -e KEYCLOAK_IMPORT=/chain-service-calls/example-realm-export.json jboss/keycloak
```
### Standard
```
sh bin/standalone.sh -c standalone.xml -b 0.0.0.0 -Djboss.bind.address.management=0.0.0.0 --debug 8190 -Djboss.http.port=8090
```
Open the Keycloak admin console, click on Add Realm, click on import 'Select file', select example-realm-export.json and click Create.

## 2. Run Services Locally
- Service1
```bash
cd service1
npm i
npm run start
```
- Service2
```bash
cd service2
npm i
npm run offline
```
- Service3
```bash
cd service3
npm i
npm run offline
```

## 3. Run UI locally

```bash
cd frontend
npm i
npm run start
```

## 4. Open UI
[http://localhost:3001](http://localhost:3001)

users:

| User | Password | Service 1 Role 1 | Service 1 Role 2 | Service 2 Role | Service 3 Role |
|:----------|:-----------|:-----------------|:-----------------|:---------------|:---------------|
| user | user | X | X | X | X |
| user1 | user1 | - | - | X | X |
| user2 | user2 | X | - | - | X |

## 6. Results

| User | Result | Description |
|:----------|:-------------------------------------------------------------------------------------------------------|:------------------------------------------------------|
| User | ![](../../docs/userChain.png) | All Access |
| User1 | ![](../../docs/user1Chain.png) | FrontEnd->Service2 and FrontEnd->Service3->Service2 | |
| User1 | ![](../../docs/user2Chain.png) | FrontEnd->Service1->Service2 and FrontEnd->Service2 |
Loading

0 comments on commit fe0957e

Please sign in to comment.