1

We are working on developing a mobile app and users should be able to log in to the app and in our backend Drupal site authenticated from Keycloak. Our main goal is that the user would be able to login from the app to update/create content using REST api.

We will be using a JWT token generated from Keycloak to authenticate user in the backend site.

We are using the following Drupal core and module versions:

  • Drupal: 8.5.3
  • OAuth2 JWT SSO: 8.x-1.0-rc1
  • OpenID Connect: 8.x-1.0-beta5
  • RESTful Web Services: 8.5.3
  • Simple OAuth: 8.x-3.0
  • OpenID Connect REST API: 8.x-1.0-rc1

I will first try with REST API to see if the implementation will work before integrating it to our mobile app.

Below are the steps of REST request executed in POSTMAN tool

Pre-setup

From http://192.168.254.107:8080/admin/config/services/openid-connect We enable generic and fill in the following fields

GENERIC
Redirect URL: https://192.168.254.107/openid-connect/rest/generic
Client ID: opensocial
Client secret: XXXXX-XXXXX-XXXXX
Authorization endpoint
http://192.168.254.107:8083/auth/realms/master/protocol/openid-connect/auth
Token endpoint
http://192.168.254.107:8083/auth/realms/master/protocol/openid-connect/token
UserInfo endpoint
http://192.168.254.107:8083/auth/realms/master/protocol/openid-connect/userinfo

And I have client created in Keycloak,

http://192.168.254.107:8083/auth/admin/master/console/#/realms/master/clients/XXXXX-XXXXX-XXXXX
Client ID: opensocial
Client Protocol: openid-connect
Access Type: Confidential
* Valid Redirect URIs : http://192.168.254.107:8080/*
Base URL: http://192.168.254.107:8080
Web Origins: http://192.168.254.107:8080/*

Steps

  1. Generate JWT from Keycloak

    REQUEST: POST
    ENDPOINT: http://192.168.254.107:8083/auth/realms/master/protocol/openid-connect/token
    HEADER: Content-Type: application/x-www-form-urlencoded
    BODY: client_id=opensocial&client_secret=f3e8f92d-c9ff-4139-b715-33e3aaa7194d&username=[username_from_keycloak]&password=[password_from_keycloak]&grant_type=password
    
    RESPONSE:
    
        {
            "access_token": "XXXXX.XXXXX.XXXXX",
            "expires_in": 3600,
            "refresh_expires_in": 1800,
            "refresh_token": "XXXXX.XXXXX.XXXXX",
            "token_type": "bearer",
            "not-before-policy": 0,
            "session_state": "7f739b98-421c-4aaf-a85c-6e38424d9492",
            "scope": "email profile"
        }
    
  2. Validated in jwt.io website and/or by fetching userinfo from Keycloak

    REQUEST: GET
    ENDPOINT: http://192.168.254.107:8083/auth/realms/master/protocol/openid-connect/userinfo
    HEADER: Authorization: Bearer [access_token]
    RESPONSE:
    
    {
        "sub": "331e758e-b7cd-44b0-93ca-97aa44310335",
        "email_verified": false,
        "preferred_username": "admin"
    }
    
    1. Create content using token

      REQUEST: POST ENDPOINT: http://192.168.254.107:8080/node (this is the backend site) HEADERS: Content-Type: application/json Authorization: Bearer [access_token] RESPONSE:

      Status: 500 500 Service unavailable (with message) The website encountered an unexpected error. Please try again later.

In Drupal logs I find the following.

Type php

Date Thursday, May 9, 2019 - 06:52 User Anonymous (not verified) Location http://192.168.254.107:8080/node Referrer
Message Error: Call to a member function validateAuthenticatedRequest() on null in Drupal\simple_oauth\Server\ResourceServer->validateAuthenticatedRequest() (line 63 of /opt/app-root/src/html/modules/simple_oauth/src/Server/ResourceServer.php) #0 /opt/app-root/src/html/modules/simple_oauth/src/Authentication/Provider/SimpleOauthAuthenticationProvider.php(63): Drupal\simple_oauth\Server\ResourceServer->validateAuthenticatedRequest(Object(Symfony\Component\HttpFoundation\Request)) #1 /opt/app-root/src/html/core/lib/Drupal/Core/Authentication/AuthenticationManager.php(52): Drupal\simple_oauth\Authentication\Provider\SimpleOauthAuthenticationProvider->authenticate(Object(Symfony\Component\HttpFoundation\Request)) #2 /opt/app-root/src/html/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php(78): Drupal\Core\Authentication\AuthenticationManager->authenticate(Object(Symfony\Component\HttpFoundation\Request)) #3 [internal function]: Drupal\Core\EventSubscriber\AuthenticationSubscriber->onKernelRequestAuthenticate(Object(Symfony\Component\HttpKernel\Event\GetResponseEvent), 'kernel.request', Object(Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher)) #4 /opt/app-root/src/html/core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php(111): call_user_func(Array, Object(Symfony\Component\HttpKernel\Event\GetResponseEvent), 'kernel.request', Object(Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher)) #5 /opt/app-root/src/vendor/symfony/http-kernel/HttpKernel.php(127): Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.request', Object(Symfony\Component\HttpKernel\Event\GetResponseEvent)) #6 /opt/app-root/src/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1) #7 /opt/app-root/src/html/modules/simple_oauth/src/HttpMiddleware/BasicAuthSwap.php(67): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #8 /opt/app-root/src/html/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Drupal\simple_oauth\HttpMiddleware\BasicAuthSwap->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #9 /opt/app-root/src/html/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #10 /opt/app-root/src/html/modules/jsonapi/src/StackMiddleware/FormatSetter.php(40): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #11 /opt/app-root/src/html/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\jsonapi\StackMiddleware\FormatSetter->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #12 /opt/app-root/src/html/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(50): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #13 /opt/app-root/src/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #14 /opt/app-root/src/html/core/lib/Drupal/Core/DrupalKernel.php(664): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #15 /opt/app-root/src/html/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request)) #16 {main}. Severity Error Hostname 192.168.254.107

Why does using a valid JWT token generated from Keycloak resulted into user not being authenticated in Drupal using REST API request? Is the method I used not possible? If not, then is there any other processes I can follow to achieve the goal?

1 Answer 1

1

JWT token generated from keycloak is rejected because Drupal api accepts token generated by its own internal authorization server.

Drupal's api is protected by OAuth2 protocol. Drupal use its own OAuth2 authorization server to protect its api. So, to gain access to drupal api, you need to provide an access token issued by drupal authorization server.

you can check the "iss" field in the access_token: value must be related to drupal's authorization server.

About the keycloak configuration you gave: it is related to the drupal account creation / login via OpenIdConnect. It allow to automatically create an account / log into with user data and credentials provided by keycloak. But it is not related to drupal api access. These are 2 distinct things:

  • OpenIdConnect: to create an account and log into system using an external identity provider, like keycloak here
  • OAuth2: to restrict / secure api access.

I'm also looking for a way to configure drupal to use an external OAuth2 authorization server like Keycloak

1
  • Hello @Thomas LIMIN, I'm also looking for the same solution. Any chance?
    – dickwan
    Commented Nov 23, 2021 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.