Browse Source

initial commit

main
Nicolas Massé 5 years ago
commit
97496d90f3
  1. 3
      .gitignore
  2. 91
      README.md
  3. 147
      index.js
  4. 1411
      package-lock.json
  5. 22
      package.json

3
.gitignore

@ -0,0 +1,3 @@
keycloak.json
node_modules

91
README.md

@ -0,0 +1,91 @@
# Keycloak NodeJS Adapter demo
## Setup
```sh
git clone https://github.com/nmasse-itix/keycloak-nodejs-demo.git
cd keycloak-nodejs-demo
npm install
```
## Demo scenario
Install Red Hat SSO.
Create a Realm named Red Hat.
Start the Petstore Server.
```sh
node index.js
```
Show some REST requests.
```sh
http http://localhost:8080/pets/
http http://localhost:8080/pets/1
```
Create a client named "nodejs-client", **Bearer Only**.
Download **keycloak.json** from the **Installation** tab (select **Keycloak OIDC JSON** from the dropdown list).
Uncomment all lines in index.js and restart the Petstore server.
Show that the Petstore server now requires authentication.
```sh
http http://localhost:8080/pets/
```
Create a **confidential** client named "rest-client", with only the **Direct Access Grants** flow enabled.
Create a user **john** with password **secret**.
Request a token for john.
```sh
curl https://$SSO_HOSTNAME/auth/realms/redhat/protocol/openid-connect/token -XPOST -d client_id=rest-client -d client_secret=$CLIENT_SECRET -d grant_type=password -d username=john -d password=secret
```
Save it for later.
```sh
TOKEN=$(curl https://$SSO_HOSTNAME/auth/realms/redhat/protocol/openid-connect/token -XPOST -d client_id=rest-client -d client_secret=$CLIENT_SECRET -d grant_type=password -d username=john -d password=secret -s |jq -r .access_token)
```
Call one of the REST endpoints.
```sh
http http://localhost:8080/pets/ "Authorization:Bearer $TOKEN"
```
Now edit index.js and update all REST endpoint to check for user roles, either **read** or **write**. Like this:
```js
router.get("/pets/:id", keycloak.protect("read"), function(req,res){
```
Restart the petstore server and get a new token.
Show that now, calls are rejected.
```sh
http http://localhost:8080/pets/ "Authorization:Bearer $TOKEN"
```
Give the **read** role to john, get a new token and show that you can query the Read REST endpoints.
```sh
http http://localhost:8080/pets/ "Authorization:Bearer $TOKEN"
```
Write calls a forbidden.
```sh
http DELETE http://localhost:8080/pets/1 "Authorization:Bearer $TOKEN"
```
Give the **write** role to john, get a new token and show that you can query the Write REST endpoints.

147
index.js

@ -0,0 +1,147 @@
const express = require('express');
const cors = require('cors')
const _ = require("underscore");
const bodyParser = require('body-parser');
const util = require('util');
//const session = require('express-session');
//const Keycloak = require('keycloak-connect');
// CORS Setup
var corsConfig = {
"origin": true,
"credentials": true
};
// ExpressJS Setup
const app = express();
app.use(cors(corsConfig)); // Handle CORS requests
var router = express.Router();
var port = 8080;
var pets = { "1": {"id":1,"name":"Eclair","tag":"cat"},
"2": {"id":2,"name":"Cannelle","tag":"cat"} };
var counter = 3;
// Enable sessions in ExpressJS
/*
const memoryStore = new session.MemoryStore();
app.use(session({
store: memoryStore,
secret: 's3cr3t!',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}));
*/
// Enable the Keycloak adapter
//const keycloak = new Keycloak({ store: memoryStore });
//app.use(keycloak.middleware());
// Handle CORS pre-flight request
app.options('*', cors(corsConfig));
// Log every request
router.use(function (req,res,next) {
next();
console.log("%s %s => %d", req.method, req.originalUrl, res.statusCode);
});
// All requests must be authenticated
//router.use(keycloak.protect());
// Get all the pets
router.get("/pets", function(req,res){
success(res, 200, _.values(pets));
});
// Get a pet
router.get("/pets/:id", function(req,res){
var id = req.params.id;
if (! (id in pets)) {
return error(res, 404, util.format("No such pet with id '%s' !", id));
}
success(res, 200, pets[id]);
});
// Create a pet
router.post("/pets", function(req,res){
var pet = req.body;
if (pet == null) {
return error(res, 400, "No body sent !");
}
pet.id = counter++;
pets[pet.id] = pet;
success(res, 201, pet);
});
// Delete a pet
router.delete("/pets/:id", function(req,res){
var id = req.params.id;
if (! (id in pets)) {
return error(res, 404, util.format("No such pet with id '%s' !", id));
}
var pet = pets[id];
delete pets[id];
success(res, 200, pet);
});
// Update a pet
router.put("/pets/:id", function(req,res){
var pet = req.body;
if (pet == null) {
return error(res, 400, "No body sent !");
}
var id = req.params.id;
if (! (id in pets)) {
return error(res, 404, util.format("No such pet with id '%s' !", id));
}
if (pet.id != id) {
return error(res, 400, util.format("The id cannot be updated: '%s' vs '%s'", pet.id, id));
}
pets[id] = pet;
success(res, 200, pet);
});
//
// Please find below the plumbing code
//
// Register the JSON Parser for POST and PUT requests
app.use(bodyParser.json());
// Register the router
app.use("/",router);
// 404 Handler (Not Found)
app.use("*",function(req,res){
error(res, 404, "Not found");
});
// Start the HTTP Server
var server = app.listen(port,function(){
console.log("API Mockup listening at port %d", port);
});
function error(res, code, message) {
var response = {
status: code,
message: message
};
return res.status(code)
.type("application/json")
.send(JSON.stringify(response));
}
function success(res, code, response) {
return res.status(code)
.type("application/json")
.send(JSON.stringify(response));
}

1411
package-lock.json

File diff suppressed because it is too large

22
package.json

@ -0,0 +1,22 @@
{
"name": "keycloak-nodejs-demo",
"version": "1.0.0",
"description": "A demo of the Keycloak NodeJS Adapter",
"repository": "https://github.com/nmasse-itix/keycloak-nodejs-demo.git",
"main": "index.js",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-session": "^1.17.1",
"keycloak-connect": "^12.0.4",
"underscore": "^1.12.1",
"util": "^0.12.3"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Nicolas Massé",
"license": "MIT"
}
Loading…
Cancel
Save