Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Commit

Permalink
Add support for new keycloak device auth flow
Browse files Browse the repository at this point in the history
- Legacy flow is still supported

closes #64

Signed-off-by: Oguzcan Kirmemis <[email protected]>
  • Loading branch information
oguzcankirmemis authored and wagmarcel committed Sep 15, 2023
1 parent 37eb6b2 commit 98402c9
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 19 deletions.
65 changes: 51 additions & 14 deletions lib/authService/authenticate.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,38 @@ function verifyAndDecodeToken(token) {
});
}

class Authenticate {
function getRealm(token) {
// issuer has to contain realm id, e.g.: http://<keycloak-url>/auth/realms/iff
const parts = token.iss.split("/");
return parts[parts.length - 1];
}

function validate(token, username) {
let type = token.type;
let did = token.device_id;
if (!type || !did) {
return false;
}
if (type !== "device" || did !== username) {
return false;
}
return true;
}

function legacyValidate(token, username) {
let accounts = token.accounts;
let type = token.type;
let did = token.sub;
if (!accounts || !type || !did) {
return false;
}
if (accounts.length !== 1 || accounts[0].role !== "device" || type !== "device" || did !== username) {
return false;
}
return true;
}

class Authenticate {
constructor(config, logger){
this.config = config;
this.logger = logger;
Expand All @@ -47,7 +77,6 @@ class Authenticate {
}
// expects "username" and "password" as url-query-parameters
async authenticate(req, res) {

this.logger.debug("Auth request " + JSON.stringify(req.query));
var username = req.query.username;
var token = req.query.password;
Expand All @@ -63,21 +92,29 @@ class Authenticate {
res.sendStatus(400);
return;
}
// check whether accounts contains only one element and role is device
var accounts = decoded_token.accounts;
var did = decoded_token.sub;
if (accounts === undefined || did === undefined || accounts.length !== 1 || accounts[0].role !== "device" || did !== username) {
if (!validate(decoded_token, username) && !legacyValidate(decoded_token, username)) {
res.sendStatus(400);
return;
}
var accountId = accounts[0].id;
//put account/device into the list of accepted topics
var key = accountId + "/" + did;
await this.cache.setValue(key, "acl", true);

// For SparkplugB put account/gateway(node) into the list of accepted topics to authenticate Node/gateway messages
if (decoded_token.gateway !== undefined ||decoded_token.gateway === null) {
var gatewayKey=accountId + "/"+ decoded_token.gateway;
// check whether accounts contains only one element and role is device
var accounts = decoded_token.accounts;
var did = decoded_token.device_id ? decoded_token.device_id : decoded_token.sub;
var accountId = accounts ? accounts[0].id : null;
let realm = getRealm(decoded_token);
// put realm/device into the list of accepted topics
await this.cache.setValue(realm + "/" + did, "acl", true);
// put account/device into the list of accepted topics (legacy)
if (accountId) {
var key = accountId + "/" + did;
await this.cache.setValue(key, "acl", true);
}
// For SparkplugB put (legacy) account/gateway(node) and realm/gateway(node) into the list of accepted topics to authenticate Node/gateway messages
if (decoded_token.gateway !== undefined || decoded_token.gateway === null) {
if (accountId) {
var legacyGatewayKey = accountId + "/" + decoded_token.gateway;
await this.cache.setValue(legacyGatewayKey, "acl", true);
}
let gatewayKey = realm + "/" + decoded_token.gateway;
await this.cache.setValue(gatewayKey, "acl", true);
}
res.sendStatus(200);
Expand Down
21 changes: 16 additions & 5 deletions test/unit/lib_authServiceTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ describe(fileToTest, function(){
it('Authentication shall successfully validate a token', function(done){
var decodedToken = {
sub: "deviceId",
iss: "http://keycloak-url/auth/realms/realmId",
type: "device",
accounts: [{
role: "device",
id: "accountId"
Expand Down Expand Up @@ -149,7 +151,7 @@ describe(fileToTest, function(){
};
var cache = {
setValue: function(key, type, value) {
assert.equal(key, "accountId/deviceId", "Wrong cache value received.");
assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received.");
assert.equal(type, "acl", "Wrong cache value received.");
assert.equal(value, true, "Wrong cache value received.");
}
Expand All @@ -174,7 +176,9 @@ describe(fileToTest, function(){
it('Authentication shall successfully validate a token with gatewayid', function(done){
var decodedToken = {
sub: "deviceId",
iss: "http://keycloak-url/auth/realms/realmId",
gateway: "gatewayId",
type: "device",
accounts: [{
role: "device",
id: "accountId"
Expand Down Expand Up @@ -204,7 +208,8 @@ describe(fileToTest, function(){
};
var cache = {
setValue: function(key, type, value) {
assert.oneOf(key,["accountId/deviceId" , "accountId/gatewayId"], "Wrong cache value received.");
console.log(key, type, value);
assert.oneOf(key,["accountId/deviceId", "realmId/deviceId", "accountId/gatewayId", "realmId/gatewayId"], "Wrong cache value received.");
assert.equal(type, "acl", "Wrong cache value received.");
assert.equal(value, true, "Wrong cache value received.");
return;
Expand All @@ -230,6 +235,8 @@ describe(fileToTest, function(){
it('Authentication shall detect wrong deviceId in username', function(done){
var decodedToken = {
sub: "deviceId",
iss: "http://keycloak-url/auth/realms/realmId",
type: "device",
accounts: [{
role: "device",
id: "accountId"
Expand Down Expand Up @@ -260,7 +267,7 @@ describe(fileToTest, function(){
};
var cache = {
setValue: function(key, type, value) {
assert.equal(key, "accountId/deviceId", "Wrong cache value received.");
assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received.");
assert.equal(type, "acl", "Wrong cache value received.");
assert.equal(value, true, "Wrong cache value received.");
}
Expand All @@ -284,6 +291,8 @@ describe(fileToTest, function(){
it('Authentication shall detect wrong role in token', function(done){
var decodedToken = {
sub: "deviceId",
iss: "http://keycloak-url/auth/realms/realmId",
type: "device",
accounts: [{
role: "wrontRole",
id: "accountId"
Expand Down Expand Up @@ -314,7 +323,7 @@ describe(fileToTest, function(){
};
var cache = {
setValue: function(key, type, value) {
assert.equal(key, "accountId/deviceId", "Wrong cache value received.");
assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received.");
assert.equal(type, "acl", "Wrong cache value received.");
assert.equal(value, true, "Wrong cache value received.");
}
Expand All @@ -338,6 +347,8 @@ describe(fileToTest, function(){
it('Authentication shall detect wrong account array', function(done){
var decodedToken = {
sub: "deviceId",
iss: "http://keycloak-url/auth/realms/realmId",
type: "device",
accounts: [{
role: "device",
id: "accountId"
Expand Down Expand Up @@ -371,7 +382,7 @@ describe(fileToTest, function(){
};
var cache = {
setValue: function(key, type, value) {
assert.equal(key, "accountId/deviceId", "Wrong cache value received.");
assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received.");
assert.equal(type, "acl", "Wrong cache value received.");
assert.equal(value, true, "Wrong cache value received.");
}
Expand Down

0 comments on commit 98402c9

Please sign in to comment.