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
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" }
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" }
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?