3

User trying to access /hello url in the browser via a reverse-proxy.

"500 Error: Cannot exchange code for grant in bearer-only mode"

error is popped after successful login by the user on Keycloak login page on access to protected url.

The following code is used in the reverse proxy:

var Keycloak = require('keycloak-connect');
var session = require('express-session');
var memoryStore = new session.MemoryStore();

let keycloak = new Keycloak(memoryStore);
app = express();
app.use( keycloak.middleware() );
app.get( '/hello', keycloak.protect( 'realm:admin' ));

keycloak.json is:

{
  "realm": "master",
  "auth-server-url": "https://127.0.0.1/auth",  
  "resource": "test_ui",
  "confidential-port": 0,  
  "credentials" : {
    "password" : "d31c4718-12e9-407b-9bf2-cb72734a23f0"
  }
}

client test_ui is confidential. Adding bearer-only : true to keycloak.json results in access denied error instead of the above error.

What's wrong with the configuration?

rok
  • 9,403
  • 17
  • 70
  • 126
  • 1
    Hello! Practical solution example: change the realm, app code and cURL example. Credible sources: Official keycloak documentation, oAuth RFC. I have also edited my answer to make the required steps to fix your issue simpler to understand. Take another look. – NotGaeL Oct 08 '18 at 21:10

1 Answers1

3

First of all: your keycloak.json config and node.js keycloak.protect app code are unaligned:

  • keycloak.json describes "realm":"master"
  • But you are invoking keycloak.protection on your app's GET /hello resource for admin realm only: app.get( '/hello', keycloak.protect( 'realm:admin' ));

TRY CHANGING THE REALM TO MASTER

app.get( '/hello', keycloak.protect( 'realm:master' ));

YOU CAN TEST IF IT WORKS USING cURL

curl -i http://YOUR_APP_SERVER_HOST/hello -H "Authorization: Bearer YOUR_BEARER_TOKEN"

DON'T FORGET TO SET UP BEARER AUTHENTICATION ON KEYCLOAK

https://www.keycloak.org/docs/3.1/authorization_services/topics/enforcer/keycloak-enforcement-bearer.html

AND GET YOUR BEARER TOKEN FIRST

curl \
  -d 'client_id=YOUR_KEYCLOAK_CLIENT' \
  -d 'username=YOUR_USERNAME' \
  -d 'password=YOUR_PASSWORD' \
  -d 'grant_type=password' \
  'https://YOUR_KEYCLOAK_SERVER_HOST/auth/realms/YOUR_REALM/protocol/openid-connect/token'

NOW, THE LITERATURE

What happens here is essentially:

  • On your first request: you are trying to access functionality that requires Bearer token authentication, but you are using grant exchange code instead. Hence the error 500 response. Are you authenticating first against your keycloak /auth authentication service endpoint using your user credentials in order to obtain a valid Bearer token for that realm? Or are you, on the contrary, mistakenly trying to use your user credentials to authenticate directly against your app's service GET /hello API resource endpoint instead?

  • Then, on your second request you try enabling Bearer only authentication, but:

    1. Your client is not examining the provided response WWW-Authenticate header for determining the required authentication method and, most importantly:
    2. Your client is not issuing a new GET /hello request providing a valid HTTP Authorization header with your obtained Bearer token valid for GET /hello's assigned realm. Therefore you simply get an HTTP authentication error response (Typically 401 Unauthorized HTTP response, although 403 Forbidden is sometimes used instead).

In short, you need to align the realm and auth scheme on client and app server and auth server config, so:

  • Your app server endpoint GET /hello will send a WWW-Authenticate HTTP header specifying the required user realm and authentication scheme.

  • Your client will then use the provided user credentials to authenticate on your keycloak authentication server and receive a Bearer token.

You must have configured the keycloak authentication server to support the required authentication scheme for that realm and the user must have permissions to operate on that realm. Check out the keycloak server administrative console for that purpose.

  • Finally, once this is done for that realm and authentication scheme, your client will be able to issue a GET /hello request to your app server containing the acquired HTTP Bearer token on the HTTP Authorization header. cURL example:

    curl -i http://example.com/api/hello -H "Authorization: Bearer mytoken123"

For more info:

Community
  • 1
  • 1
NotGaeL
  • 8,344
  • 5
  • 40
  • 70