Hi there,
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.
Warm Regards,
Stefan van Roosmalen
testproject.zip (11.0 KB)