How to pass the Tenant_id from TMS Webcore to XData

Hello,

i want Users to Authenticate via TMS Sphinx to a TMS Webcore Application, from where they can access their specific Data.
Working along the TMS Sphinx Simple Demo helps a lot here.
I'm able to Login, pass the Access Token and let the XDataWebConnection establish a connection.
But when i add the "Tenant" Middleware to the XDataServer the struggle begins.
Using VCL it seems, that the function "OnManagerCreate" is the key to solve this.
But when accessing the XData Server from a TMS Webcore application it seems, that this function isn't called at all.
So here my Question
Do i have to pass the Tenant_id from the TMS Webcore Application directly or is it possible to pass the Access Token and the XData Server handles the rest?
Or do i have to Login, Receive the Access Token, Request the Tenant_id and then pass it with every Request to the XDataServer?

Thanks in advance and kind regards,
Kurt Paffen

1 Like

Hi @Paffen_Kurt1, let me answer with an explanation and then a question back to you.

The Tenant middleware detects the tenant of the request and then adds a ITenant interface in the Sparkle context, so that any code that deals with tenant in the request can use such interface and get the current tenant by reading the TenantId property of the interface.

In other words, if your server is muti-tenant and you need to check the current tenant, you just need this code:

  TenantId := THttpServerContext.Current.RequiredItem<ITenant>.TenantId;

If there is no ITenant in the context, the code above will raise an exception. You can also use just Item<ITenant>, then you need to check if it returns nil. This might be useful if you have code that can be executed with a tenant in the context.

Back to Tenant middleware, it can extract the tenant id from the request from several places. It's up to you to decide which place of the request you will accept the tenant id from, by filling one or more properties of the middleware, which are:

  • HeaderName: The tenant id will be the value of the specified HTTP header provided in this property, e.g. tenant-id: 123.
  • QueryParam: The tenant id will be the value of the specified URL query param, e.g. ?tenant-id=123.
  • BasePath: This should indicate a base URL path after which the next path segment will be the tenant id. For example, if you set BasePath to /myserver/foo, the tenant id will be extract as /myserver/foo/{tenant-id}. It's worth noting that the middleware will rewrite the URL after extracting the tenant id, so that further request processing will work as if the request as a single tenant. For example, if the request URL path is /myserver/foo/123/customers/1, then the middleware will set the tenant id to 123, and the URL path request will be rewritten as /myserver/foo/customers/1.
  • BaseDomain: The tenant id will be extracted from the subdomain relative to the domain host in the request. For example, is BaseDomain is myserver.com, if the Host of the request is 123.myserver.com, then the tenant id will be 123.
  • UserClaim: the tenant id will be extracted from one claim of the current user of the request (which data is usually extracted from the JWT). For example, if this property contains tenant-id, then the tenant id will be extracted from the JWT claim named tenant-id.
  • DomainMap: This is for white-label tenant, you can map full domains to tenant id. So you can have a 1-1 map of domains to tenant ids, e.g., somecustomer.com maps to 123, anothercust.com maps to 225, and so on.

Thus, my question/answer to you is:
How have you configured your tenant middleware? Where in the request is your API XData server expecting the tenant id? "All" you would have to do is pass such tenant id to the server from your Web Core application.

Hello and thanks for the reply.
So i made it work to pass a tenant_id via

Args.Request.Headers.SetValue('Tenant', 'Some ID');

in the OnRequest function of my XDataWebConnection.
Passing the tenant_id this way, i have to somehow receive the tenant_id saved in the sx_user Table from the Sphinx server and have to send it back to the xdata Server. So if i'm correct the process would look like this:

  1. I have to login, receive the Access Token
  2. Then i ask the sphinx server for the tenant_id belonging to this Access Token
  3. I receive the Tenant_id from the Sphinx Server
  4. Then i pass the Access Token and the Tenant_id to the XData Server.
  5. Get my filtered Data

Looking on the different approaches you mentioned, i think passing the tenant_id via the JWT directly is the one i have in mind and want to realise.
Is it possible to configure my Sphinx Server to pass the Access Token containing the tenant_id from Table sx_users?
Then the Process (when i'm correct) would look like this:

  1. I have to login, receive the Access Token
  2. I pass the Access Token to the XData Server
  3. Get my filtered Data

That would be more comfortable, because i only have to handle the Access Token.
I'm looking forward to your thoughts on this and hopefully i answered your question correctly.

Kind regards,

Kurt Paffen

There are two "tenants" you need to solve: in the Sphinx server, at the moment of the login, and then in the XData server, for the API.

In the login process, do you expect user to login to a specific tenant already? How do you expect to know that a user belongs to a tenant?

You don't need an Access Token to login. Sphinx will open the login page, user will login, then an Access Token will be generated, which might include the tenant id. You give back the Access Token with the tenant id to the web application (or any other client), and then such token will already have the tenant id information to be passed to the XData API server.

Yes, i want to assign tenant_ids to Users manually . Much like the TMS Subscription Manager, where you only get to see your purchased Products after you login.In my scenario i want our users to only see the Devices that belong to them.
So i will generate the Users manually and create a tenant_id.

So if a Tenant_id is assigned to a user (saved in the sx_user table) it automatically is added to the Access Token? Does this happen automatically or do i have to configure the Sphinx Server to do so?
And how can i extract the Tenant_Id from the Access Token in the XData Server?

You have to add the tenant_id manually to the access token. This is accomplished in the TSphinxConfig.OnConfigureToken event, which actually you probably eventually need to use anyway, to add custom information to your access token. Here is a small example:

procedure TForm7.SphinxConfig1ConfigureToken(Sender: TObject; Args: TConfigureTokenArgs);
begin
  Args.Token.Claims.AddOrSet('tenant-id', Args.User.TenantId)
end;

This is done automatically by the Tenant middleware as I explained above. Once you included the tenant id is in the JWT in the claim tenant-id, you just need to fill the UserClaim property of the Tenant middleware with the value tenant-id and it will extract it automatically.

OK
When sending the Access Token like this

Args.Request.Headers.SetValue('Authorization', 'Bearer '+dmReg.SphinxWebLogin.AuthResult.AccessToken);

(dmReg is a Datamodule containing the Sphinx Component)
I get Error 500 " Required item ITenant not found in context".
I guess my way of passing the Access Token is not correct.
How do i have to pass the AccessToken?

This is the way i Configured the Sphinx Token: and Tenant Middleware

procedure TAuthServerContainer.SphinxConfigConfigureToken(Sender: TObject;
  Args: TConfigureTokenArgs);
begin
  Args.Token.Claims.AddOrSet('TenantId', Args.User.TenantId)
end;
procedure TServerContainer.XDataServerManagerCreate(Sender: TObject;
  Args: TManagerCreateArgs);
begin
  Args.Manager.EnableFilter('MandantenFilter').SetParam('TenantId', Args.Handler.Context.RequiredItem<ITenant>.TenantId);
end;

Tenant

Make sure you put JWT middleware before Tenant middleware. It's the JWT middleware which translates the JWT claims to the IUser interface, which is then analyzed by the Tenant middleware.

Also check your access token (an easy-to-use tool is https://jwt.io) to confirm that the tenant id claim is effectively there.

1 Like

This topic was automatically closed 60 minutes after the last reply. New replies are no longer allowed.