2

The route look like this where verifytoken is a middleware.

router.get('/v1/endpoint', verifytoken, apis.getData);

In order to protect this route we will generally use keycloak.protect(); but I want to use verifytoken which is a middleware and always go to apis.getDatat irrespective of whether the route is protected or not. But the middleware function will attach a string based on if the user is authenticated or not.

router.get('/v1/endpoint', keycloak.protect(), apis.getData);

This code should run and protect the route and based on if it's authorized or not I want to add verified string that will be used by apis.getData to send the correct amount of data.

const keycloak = require('../../keycloak').getKeycloak();

/**
 * @param {Object} request - request object with authorization header.
 * @param {Object} response - response object.
 * @param {Object} next - calls the next function with user payload.
 */
module.exports = function(request, response, next) {
    // authorization token.
    const token = request.headers.authorization;

    // if token is not sent the authorization fails.
    if (!token) {
        return response.status(401).send('Access Denied, missing authorization token!');
    }

    // check if the token is valid or not.
    try {
        const verified = {};
        if (keycloak.protect()) {
            verified.verified = 'verified';
        }
        console.log('Token is verified', verified);
        response.locals.user = verified;
        next();
    } catch (err) {
        console.log('Token invalid!!!');
        response.locals.user = 'unknown';
        next();
    }
};

Code for configuring keycloak

const session = require('express-session');
const Keycloak = require('keycloak-connect');
const keycloakConfig = require('./keycloak.json');

let _keycloak;

function initKeycloak() {
    if (_keycloak) {
        console.warn('Trying to init Keycloak again!');
        return _keycloak;
    }

    console.log('Initializing Keycloak...');
    const memoryStore = new session.MemoryStore();
    _keycloak = new Keycloak({ store: memoryStore }, keycloakConfig);
    return _keycloak;
}

function getKeycloak() {
    if (!_keycloak) {
        console.error('Keycloak has not been initialized. Please called init first.');
    }
    return _keycloak;
}

module.exports = {
    initKeycloak,
    getKeycloak,
};
  • I want to make clarification two items. Which middleware using keycloak-connect or other? second question is keycloak.protect() want to use verify the token for locals.user insted of using apis.getData? – Bench Vue Sep 10 '22 at 21:54
  • Yes, it's keycloak-connect and I have also updated the code for it. The question is that we use **router.get('/v1/endpoint', keycloak.protect(), apis.getData);** this method to protect a particular route and only if the user is logged in the apis.getData will be run but I want is **router.get('/v1/endpoint', verifytoken, apis.getData);** should use the verifytoken method and checks if the user is authenticated or not in the verify token method and if the user is authenticated move to **apis.getData** with verified status else **unknown** status as you can see in the code. – undefined-2493 Sep 10 '22 at 23:10
  • @BenchVue did you get time to look into this? Thanks! – undefined-2493 Sep 11 '22 at 13:37
  • I looked. [This example](https://github.com/keycloak/keycloak-nodejs-connect/tree/main/example) works fine. I made two users. one is just user can see Token but he can't access resource. The other user can access resource who has View/Write scope policy. I think middleware good to user for two reasons. One is just get token after login and pass to Backend. Backend handle to access or not by verify token to Keycloak. Second is Frontend verify user can access or not directly. – Bench Vue Sep 12 '22 at 01:36
  • So your question to handle Token verify is not good approach. I already take over by middleware. – Bench Vue Sep 12 '22 at 01:38
  • What I am trying to achieve is that **router.get('/v1/endpoint', keycloak.protect(), apis.getData);** here if the user is logged in then **keycloak.protect()** should return **true** and move to **apis.getData** else return **false** and move to **apis.getData** – undefined-2493 Sep 12 '22 at 04:32
  • The [parameter](https://github.com/keycloak/keycloak-nodejs-connect/blob/main/middleware/protect.js#L42) of protect() is string type only support "function" pointer or "string" what is value of api.getData? – Bench Vue Sep 12 '22 at 11:31
  • getData is a function – undefined-2493 Sep 12 '22 at 14:11
  • It's mean to handle the checking protect() by your own function. In your api,getData(), not shows in question. – Bench Vue Sep 12 '22 at 14:19
  • So I am trying to protect in the first peace of code which **router.get('/v1/endpoint', verifytoken, apis.getData);** verify token middleware. – undefined-2493 Sep 12 '22 at 14:26
  • If you access /v1/endpoint, call verifytoken with function point. It return FALSE from your function. You needs to debug in your code. – Bench Vue Sep 12 '22 at 14:28
  • router.get('/v1/endpoint', verifytoken, apis.getData); the workflow for this is when calling /v1/endpoint then we move to verifytoken which is a middleware function that is the first big peace of code in the post and the verification if the user is protected or not happens there. if the user is verified then we move on to the getData function with a value 'verified' else we need to move with a value 'unverified'. But the problem is that I can't verify in the verifytoken function and not move on to the next function which is getData. – undefined-2493 Sep 12 '22 at 14:36
  • I answered your question. I hope to address how to verify token by custom function. – Bench Vue Sep 13 '22 at 10:28

1 Answers1

1

I will shows how to use custom token verify My demo base on example of keycloak-nodejs-connect

I just modified code only. it is not full example code.

UI in view/index.html

<div class="nav">
    <ul>
        <li><a href="/login">Login</a></li>
        <li><a href="/protected/resource">Protected Resource</a></li>
        <li><a href="/verify">Token Verify</a></li>
        <li><a href="/logout">Logout</a></li>
    </ul>
</div>

javascript in index.js

function my_token_verify(token, req, res) {
  // no token, it makes access denied
  if (!token || !token.token) {
    return false;
  }
  console.log(token.token);
  console.log(token.header);
  console.log(token.content);
  console.log(token.signature);
  console.log(token.clientId);
  // whatever your own logic for verify token
  if (token.header.alg == 'RS256' && token.header.typ == 'JWT')
    return true;
  return false;
}

app.get('/verify', keycloak.protect(my_token_verify), function (req, res) {
  res.render('index', {
    result: JSON.stringify(JSON.parse(req.session['keycloak-token']), null, 4),
    event: 'verify token: true'
  })
})

Result of browser, if return true from my_token_verify(), will shows this screen.

enter image description here

if return false from my_token_verify(), will shows access denied.

enter image description here VS code debug configuration

{
    "version": "0.2.0",
    "configurations": [
        {
            "command": "npm start",
            "name": "Run npm start",
            "request": "launch",
            "type": "node-terminal",
            "resolveSourceMapLocations": [
                "${workspaceFolder}/**",
                "!**/node_modules/**"
            ]
        }
    ]
}

Terminal output in VS code

Debugger attached.
Example app listening at http://:::3000
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGSjg2R2NGM2pUYk5MT2NvNE52WmtVQ0lVbWZZQ3FvcXRPUWVNZmJoTmxFIn0.eyJleHAiOjE2NjMwOTcxMjksImlhdCI6MTY2MzA2NDc0MSwiYXV0aF90aW1lIjoxNjYzMDYxMTI5LCJqdGkiOiIzNWM0MWIyZi0xMDc3LTQ1ZjMtYmFjOC04N2YxYWUyMDY1MGIiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxODAvYXV0aC9yZWFsbXMvbm9kZWpzLWV4YW1wbGUiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiOTlhYTBlZDMtMjljMS00NTdhLTg2OGYtMjVjZTlhOTMxZDAzIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoibm9kZWpzLWNvbm5lY3QiLCJzZXNzaW9uX3N0YXRlIjoiNGZmNGFjNGItZTkzOS00YWYzLWJhNDMtZjU4NjlmYTM4NmNkIiwiYWNyIjoiMCIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIiwiZGVmYXVsdC1yb2xlcy1ub2RlanMtZXhhbXBsZSJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJzaWQiOiI0ZmY0YWM0Yi1lOTM5LTRhZjMtYmE0My1mNTg2OWZhMzg2Y2QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluLXVzZXIifQ.F-u7QIuku81f5e6EAPddKllE_8xGw7zF0GrlPsqUYOAdIlwLvIrCjdLh2l1PmcpE4J6wWS9jLk8swA5hIySy2Z-X9k8OzxqY4nWXSbeOmIKY-fXqRZmV7_nNuo4b3veQv3JPfbpUhP96yFun4jDeTbbJFycvr_u0wg4KZoqrXhc
{
  alg: 'RS256',
  typ: 'JWT',
  kid: 'FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE'
}
{
  exp: 1663097129,
  iat: 1663064741,
  auth_time: 1663061129,
  jti: '35c41b2f-1077-45f3-bac8-87f1ae20650b',
  iss: 'http://localhost:8180/auth/realms/nodejs-example',
  aud: 'account',
  sub: '99aa0ed3-29c1-457a-868f-25ce9a931d03',
  typ: 'Bearer',
  azp: 'nodejs-connect',
  session_state: '4ff4ac4b-e939-4af3-ba43-f5869fa386cd',
  acr: '0',
  realm_access: {
    roles: [
      'offline_access',
      'uma_authorization',
      'default-roles-nodejs-example'
    ]
  },
  resource_access: { account: { roles: [Array] } },
  scope: 'openid email profile',
  sid: '4ff4ac4b-e939-4af3-ba43-f5869fa386cd',
  email_verified: false,
  preferred_username: 'admin-user'
}
<Buffer 17 eb bb 40 8b a4 bb cd 5f e5 ee 84 00 f7 5d 2a 59 44 ff cc 46 c3 bc c5 d0 6a e5 3e ca 94 60 e0 1d 22 5c 0b bc 8a c2 8d d2 e1 da 5d 4f 99 ca 44 e0 9e ... 78 more bytes>
nodejs-connect

Debugging in VS code enter image description here

Bench Vue
  • 5,257
  • 2
  • 10
  • 14
  • Thanks for the reply and that's what I have been doing but the following code only triggers when right token is provided to it. ```app.get('/verify', keycloak.protect(my_token_verify), function (req, res) { res.render('index', { result: JSON.stringify(JSON.parse(req.session['keycloak-token']), null, 4), event: 'verify token: true' }) })``` – undefined-2493 Sep 13 '22 at 20:15
  • So rather than using keycloak.protect() I can just use a middleware and write my own logic that protects the route based on whether the user is logged in or not. – undefined-2493 Sep 13 '22 at 20:17
  • ``` const options = { method: 'GET', url: '${keycloakConfig['auth-server-url']}/realms/${keycloakConfig.realm}/protocol/openid-connect/userinfo', headers: { Authorization: req.headers.authorization, }, }; // send a request to the userinfo endpoint on keycloak request(options, (error, response) => { if (error) throw new Error(error); // if the request status isn't "OK", the token is invalid if (response.statusCode !== 200) {} else {} }); ``` – undefined-2493 Sep 13 '22 at 20:20
  • I am not recommend own your middle-ware version. It is hard to manager a version control. if you want to add custom logic may fork it to start new repo. Sorry I can't help to modify middle-ware version. – Bench Vue Sep 13 '22 at 20:38
  • Ok, sure. Thanks! Can you tell me what happens in the your logic when you don't pass on a token? ```app.get('/verify', keycloak.protect(my_token_verify), function (req, res) { res.render('index', { result: JSON.stringify(JSON.parse(req.session['keycloak-token']), null, 4), event: 'verify token: true' }) })``` – undefined-2493 Sep 13 '22 at 20:42
  • In my_token_verify(), if return false means protect() will false with access denied. if return true means protect() will call next with can access. So if don't pass a token, the HTML UI will display access denied. I will update my page. – Bench Vue Sep 13 '22 at 21:42
  • Thank you, if we don't pass the token then the function `my_token_verify` will not be run but that's not what I want. That's what is happening in my case – undefined-2493 Sep 14 '22 at 01:30
  • That is one of good example, modify the middle-ware means, you have to responsible low - level token handling. That is why I not recommend to modify middle-ware. Your own rick to change it. – Bench Vue Sep 14 '22 at 01:35
  • Hey @undefined-2493, thanks for your accept this thread. I hope to address for your problem with this answer. I gave a thumbs up to make more 15 points. Can you re makes thumbs up to me. It will give me 10 more points. – Bench Vue Sep 14 '22 at 16:57