TSphinxWebLogin - OnUserLoggedIn Event not called

My webcore app has a datamodule, a main form and several sub forms (created by the main form).

The application

  • creates the datamodule and initialises it

  • the datamodule creates the main form

  • the main form creates the default sub form

  • the user then selects several options on the defualt sub form and clicks submit

  • this calls Sphinx.Login

  • the user logs in successfully

  • the datamodule is recreated and initialised

  • the main form is created

But the TSphinxWebLogin.OnUserLoggedIn is not called

Also if I try to access the data returned in the JWT from the OnCreate of the datamodule it fails:


TypeError: Cannot read properties of null (reading 'AuthResult')
    at Object.AuthResult (Sphinx.WebLogin.pas:172:21)
    at Object.GetIdPayloadObject (Auth.Service.pas:136:35)
    at Object.GetClaimValue (Auth.Service.pas:90:12)
    at Object.UserId (Auth.Service.pas:155:13)

Using version 1.10.0.0 released 16-07-224

any ideas? Thanks

Very hard to tell. Note that many of the operations you mentioned are asynchronous, there might something not executed in the order you expect.

I'm afraid I need a minimal project reproducing the issue so we can debug to have an idea of what's happening.

I’ll see what I can do, however even if it’s all async I’d still expect that event to fire at some point, even if it’s not in the order I’d expect

Still struggling with this. Wrote a test app pair and that was fine. I ran the test web app against the original server and that could login, with the same credentials.

I have checked the settings for Sphinx on the main webapp on the server and can't see anything different.

I have even created another app as a Sphinx client app that is identical to the test app (but with different id and redirect).

  Client := SphinxConfig1.Clients.Add;
  Client.ClientId := 'TestWebApp';
  lUri := 'http://localhost:8000/Project1/Project1.html';
  Client.DefaultRedirectUri := lUri;
  Client.RedirectUris.Add(lUri);
  Client.RedirectUris.Add('http://localhost:8000/Project1/');
  Client.RequireClientSecret := False;
  Client.AllowedGrantTypes := [TGrantType.gtAuthorizationCode];
  Client.ValidScopes.Add('openid');
  Client.ValidScopes.Add('email');

  Client := SphinxConfig1.Clients.Add;
  Client.ClientId := 'CyrusWebApp';
  lUri := 'http://localhost:8000/AlphaWebApp/Index.html';
  Client.DefaultRedirectUri := lUri;
  Client.RedirectUris.Add(lUri);
  Client.RedirectUris.Add('http://localhost:8000/AlphaWebApp');
  Client.RequireClientSecret := False;
  Client.AllowedGrantTypes := [TGrantType.gtAuthorizationCode];
  Client.ValidScopes.Add('openid');
  Client.ValidScopes.Add('email');

An attempted login from the main web app doesn't create a JWT, it looks as if the TokenBuilder isn't called. It is difficult to step through as there is so much middleware involved.

@wlandgraf is there a point I could break point to see where the decision might be made not to call the token builder?

Thanks

The registration process works.

Open id config shows

{
    "issuer": "http://localhost:2024/AlphaLogin",
    "authorization_endpoint": "http://localhost:2024/AlphaLogin/oauth/authorize",
    "token_endpoint": "http://localhost:2024/AlphaLogin/oauth/token",
    "response_types_supported": [
        "code",
        "id_token",
        "id_token token"
    ]
}

The settings on TSphinxWebLogin are

BaseUri: "http://localhost:2024/AlphaLogin"
ClientId: "AlphaWebApp"
Redirect: "http://localhost:8000/AlphaHolidays/Index.html"
Scope: "openid email"

I have moved Sphinx into it's own server application and it still acts the same way.

I understand you could create a sample separated project that reproduces the issue (i.e., doesn't call the token builder?)
In that case can you send the sample project? Without us being able to debug it's hard to tell what's going on.

@wlandgraf is there a point I could break point to see where the decision might be made not to call the token builder?

First of all, what HTTP requests do you see happening from the browser? You see all the flow happening, the call to authorize endpoint, the call to token endpoint...?

I have a test webapp and the main app. The test app always succeeds but the main app doesn't. The sample project always works so nothing to see there.

This is the Network tab from the main app

and this is the test app

so I can see that crypto, openid-configuration and token aren't being called

It has occasionally worked. I had one success, but since then 5 unsuccessful attempts - same code, same login

I see token being called, the third up from the bottom?

Then it really looks like some async order confusion.

Yes, 3rd up from the bottom on the sample app (listing 2). It doesn't even show itself on the main app (listing 1).

If it was an async thing, I thought it would show up eventually.

When your web app is invoked back from the redirect URL (the index.html?state=... URL), somehow it's not reaching the TSphinxLogin component, either it's not loading, or it's not detecting that the URL is a redirect URL, something like that. That's probably why it's not calling the token endpoint.

Can you please check if these topics help you out somehow:

Especially the idea of calling Login method at all times so it not also redirects to the login page, but also process the token if the app is loading as a redirection back from login page.

I have structured the app so that when the user is logging in no other async processes are called until the OnUserLoggedIn event. I do this by setting a session var, so there should be nothing that sits in the way of the login process. However, it still fails, although at least it is calling crypto-js.min.js now

in Local storage I have this

Also is there a way to check the Response.Referer from within the datamodule that way I wouldn't have to rely on session vars.

This has taken days and days and I really don't know what to do next. The whole development is on stop until I sort this out :frowning:

Thanks

What do you mean exactly by Response.Referer?
Aren't you able to reduce the project to a minimum so we can reproduce it at our side?

It's started working. Haven't changed anything as far as I can see. Installed the latest Biz release, but it started working a bit before that.

1 Like

So this all works on my development machine, but when it's copied to the designers computer, it just doesn't call token. Same webcore js, same server, same sphinx server, same database.

@wlandgraf I think I have got to the bottom of this.

In my app I load a config file from the server which is called in

procedure TMainData.WebDataModuleCreate(Sender: TObject);
begin
  Await(ConfigInit);
  if FLoginState <> TLoginState.lisLoggingIn then
    InitModule;
end;
procedure TMainData.ConfigInit;
begin
  Await(LoadConfig);

  if SessionValue[Login_Source] = '' then
    FLoginSource := TLoginSource.lsNotSpecified
  else
    FLoginSource := TRttiEnumerationType.GetValue<TLoginSource>(SessionValue[Login_Source]);

  if (SessionValue[Login_State] = '') then
    FLoginState := TLoginState.lisNotLoggedIn
  else
    FLoginState := TRttiEnumerationType.GetValue<TLoginState>(SessionValue[Login_State]);

end;

And LoadConfig

procedure TMainData.LoadConfig;
var
  Conn: TXDataWebConnection;
  Response: IHttpResponse;
begin

  Conn := TXDataWebConnection.Create(nil);
  try

    Response := TAwait.Exec<IHttpResponse>(Conn.SendRequestAsync(THttpRequest.Create('config/config.json')));
    if Response.StatusCode = 200 then
    begin
      AppConfig.Load(Response.ContentAsText);
      DataConnection.URL := AppConfig.BaseUrl;
      SphinxLogin.Authority := AppConfig.AuthURL;

      if AppConfig.SphinxClientId = '' then
        SphinxLogin.ClientId := 'AlphaWebApp'
      else
        SphinxLogin.ClientId := AppConfig.SphinxClientId;

      SphinxLogin.RedirectUri := SphinxRedirectUri;
    end;

  finally
    Conn.Free;
  end;

end;

But TOidcClient.CheckProviderInformation is called before any of this code. Hence, If I set the values at design time the login works, but if they are empty then this causes an exception before the config is processed.

Not sure why I haven't come across this in other apps.

Any ideas?

@wlandgraf I will relook at this, but do you have any ideas?

When is CheckProviderInformation called?

Have you tried calling Login after the initialization code, something like this:

  Await(ConfigInit);
  if FLoginState <> TLoginState.lisLoggingIn then
    InitModule;
  Oidc.Login;