- Issue created by @brianperry
- Status changed to Active
7 months ago 11:40pm 5 May 2024 - Assigned to brianperry
- 🇺🇸United States brianperry
The current client can authenticate using Drupal issued JWT with something like this:
// Get a JWT token using basic auth const headers = new Headers(); const encodedCredentials = stringToBase64(`admin:admin`); headers.set("Authorization", `Basic ${encodedCredentials}`); const tokenRequest = await fetch( "https://drupal-client.ddev.site/jwt/token", { headers }, ).then((response) => response.json()); // Use the JWT with custom auth const jsonApiClient = new JsonApiClient(baseUrl, { debug: true, authentication: { type: "Custom", credentials: { value: `Bearer ${tokenRequest.token}`, }, }, }); const actions = await jsonApiClient.getCollection("action--action");
It is also possible to refresh your JWT using the existing JWT:
// Refresh your token using the existing token const jwtHeaders = new Headers(); jwtHeaders.set("Authorization", `Bearer ${tokenRequest.token}`); const jwtTokenRequest = await fetch( "https://drupal-client.ddev.site/jwt/token", { headers: jwtHeaders }, ).then((response) => response.json());
Next I'm going to try to work up an example where the token is issued by the JS app, but honored by Drupal.
This has been helpful though - I think what we need to implement here is the overlap between these two flows. Basically the client assumes that you have a valid JWT to start, and then uses (and updates) it from there. We can document how you'd get the initial token, or maybe create small helpers.
- 🇮🇳India Ruturaj Chaubey Pune, India
@brianperry
The example you shared in #5 → looks pretty solid.
For the second scenario, I was able to issue a JWT from a JS app and send it to the Drupal back-end.
On Drupal front, I tried to consume the token using a custom controller but its still in progress.
Is this the kind of strategy we want to use in this case?
- 🇮🇳India Ruturaj Chaubey Pune, India
The back-end code looks something like this
public function success(Request $request) { $authHeader = $request->headers->get('Authorization'); if ($authHeader && preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) { $jwt = $matches[1]; try { $decoded = JWT::decode($jwt, 'dummy-jwt-secret'); // Assuming the payload has a userId. $uid = $decoded->userId; $user = User::load($uid); if ($user) { return new JsonResponse([ 'status' => 'success', 'user' => [ 'uid' => $user->id(), 'name' => $user->getUsername(), ], ]); } } catch (\Exception $e) { $this->logger->error('JWT validation failed: @message', ['@message' => $e->getMessage()]); return new JsonResponse(['status' => 'error', 'message' => 'Invalid JWT token'], 401); } } $data = [ 'status' => 'success', 'message' => 'The request was successful.', 'value' => $authHeader ]; return new JsonResponse($data); }
- 🇺🇸United States brianperry
Thanks for checking in on this @ruturaj-chaubey!
That back end code makes sense at a glance, but my assumption was that we could use https://www.drupal.org/project/jwt → for essentially the same thing.
I tried an example that issues a token in node.js that should be compatible with what the JWT module is expecting (uses the same key, has the same payload, etc), but if I try to authorize using the token I sign in node, it doesn't seem to work.
import jwt from "jsonwebtoken"; const token = jwt.sign( { drupal: { uid: "1", }, }, privateKey, { algorithm: "HS512", expiresIn: 60 * 60 }, ); const headers = new Headers(); headers.set("Authorization", `Bearer ${token}`); fetch("https://drupal-client.ddev.site/jsonapi/action/action", { headers }) .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error("Error:", error));
My best guess based on inspecting tokens using the debugger at https://jwt.io/ is that it is a problem related to base64 encoding. I'll keep poking at this, but wondering if I'm missing something obvious, or if the JWT module can't handle this use case.
- Issue was unassigned.