Hope you're all doing great!
As my project is nearing completion, I'm reaching out with what might just be my final query about the GraphQL interface I've been working on.
In my previous interactions within this support community, I've received great assistance from Wagner Landgraf. He helped me to successfully integrate (JWT-based) security into my GraphQL project. Quick sidenote: I'm currently using Web Broker, but I'm definitely eyeing the possibility of incorporating TMS Sparkle down the road. You can find all the details, along with the sources, right here: GraphQL endpoint handling authorization with JWT via HTTP Headers - #7 by wlandgraf
At this point, I'm wrestling with a final challenge. It's all about leveraging a specific value from the payload of the JWT and seamlessly integrating it into the GraphQL queries. My initial thought was to use a global variable to store this value, but it seems like that might not be the smartest approach. In fact, I'm a bit stuck on determining the best way to proceed. I've been poring over the source code shared in my previous forum post, and I've made a few additions to address this concern. Check out the attached files for more insight.
Here's a snippet of what I've added to FormUnit1, aimed at capturing the value from the JWT:
implementation uses GraphQL.Bookshelf; procedure TForm1.DoParseAuthentication(AContext: TIdContext; const AAuthType, AAuthData: String; var VUsername, VPassword: String; var VHandled: Boolean); var LToken: string; LJWT: TJWT; LBuilder: IJOSEConsumerBuilder; LConsumer: IJOSEConsumer; LCustomerEmail: String; begin VHandled := False; if SameText(AAuthType, 'Bearer') and (AAuthData <> '') then begin LToken := AAuthData; try LJWT := TJOSE.DeserializeOnly(LToken); try // Build consumer LBuilder := TJOSEConsumerBuilder.NewConsumer .SetVerificationKey(SHARED_SECRET) .SetExpectedAlgorithms([TJOSEAlgorithmId.HS256]); // Recommended to set expected audience and issuer LBuilder.SetSkipDefaultAudienceValidation; // LBuilder.SetExpectedAudience(True, 'http://my-api-url'); // LBuilder.SetExpectedIssuers(True, 'http://url-of-jwt-issuer'); LConsumer := LBuilder.Build; LConsumer.Process(LToken); // Getting the value of customer_email LCustomerEmail := GetFieldValueFromJSON(LJWT.Claims.JSON.ToString, 'customer_email'); GraphQL.Bookshelf.CustomerEmailFromJWT := LCustomerEmail; OutputDebugString(PChar('customer_email: ' + LCustomerEmail)); // just for debugging; remove this line later // --end VHandled := True; finally LJWT.Free; end; except on E: EJOSEException do raise EIdHTTPUnsupportedAuthorisationScheme.Create(E.Message); on E: EInvalidJWTException do raise EIdHTTPUnsupportedAuthorisationScheme.Create(E.Message); else raise; end; end; end; function TForm1.GetFieldValueFromJSON(AJSONString: String; AFieldName: String): String; var LJSONObject: TJSONObject; begin LJSONObject := TJSONObject.ParseJSONValue(AJSONString) as TJSONObject; try Result := LJSONObject.GetValue(AFieldName).Value; finally LJSONObject.Free; end; end;
Subsequently, I have added this in the unit GraphQL.Bookshelf to use the value from the JWT:
var CustomerEmailFromJWT: String; function TMutation.CreateAuthor(Name: string): TAuthor; begin Result := TAuthor.Create(Name + ' (' + CustomerEmailFromJWT + ')'); try AuthorList.Add(Result) except Result.Free; raise; end; end;
While this seems to be working like a charm, I do have a couple of questions bubbling up:
- Are there any concerns about potential hiccups when multiple users engage with the GraphQL interface simultaneously using the solution I have chosen here?
- Could there possibly be a different or better approach to tackle this challenge?
Massive thanks in advance for your guidance and expertise.
Stefan van Roosmalen
testproject.zip (11.0 KB)