Login using OpenID

From where? Are there any particular claims which are expected?

No, you are right. You should talk with @pushrbx, he has already solved it.

Is there a ticket here I can refer to?

@nicholas I haven’t read the thread through yet, but I will dig into it this evening.
If you want to authenticate through an external server, you need to take out identity server from squidex, and it only should have JWT authentication.
Example: https://github.com/pushrbx/squidex/blob/keycloak/src/Squidex.Domain.Users.Keycloak/KeycloakUserResolver.cs

In overall my branch of squidex is acting as a “resource” within the oauth2 terminology, instead of providing identity.
So basically your authentication and http request flow should look like this:
Client software -> authenticates with external identity server -> gets token -> sends request with token to squidex -> squidex validates token -> gets role information from external identity server -> authorizes the requests -> send response

Somehow I am able to log in but the behavior is very haphazard. For example, in order to login with my external login I logged, got redirected to the login page, restarted npm and the application, debugged the solution again and it worked! I am suspecting it might have to do something with caching in the angular app. If that is the case is there a way to disable caching completely (from the app not Chrome) to try it

out?

@pushrbx Thank you but I do not feel the need to unplug the current implementation of identity server from the Squidex solution. If you do not agree I am open to your suggestions. What I want is another method to Authenticate, like Google. I did manage to achieve this and was able to register a user :slight_smile: But sometimes the login is working haphazardly, I log in and get redirected to the login screen again, this happened with Google authentication too and my colleague reported that even with admin login.

This is a similar scenario reported: [SOLVED] Squidex Locally Hosted - Login Doesn’t Work

Maybe I can try to carefully log every step and open another thread under the Bugs section?

When I was fiddling around with this I’ve found two options:

  • have the identity server in a different process, and copy the identity from there to the identity server in squidex. In this case your external identity server is an external provider, and it is displayed on the login screen, and when the user clicks on it, the system should build a redirect url, where you receive the user details from the external identity server. Based on these external user details the system adds a new user to the db, and generates its own token for the user. In case of google and the others currently squidex does this thing, and I think we call this identity brokering.
  • have the identity server in a different process, remove identity server from squidex, and set the authentication scheme to JWTBearer. In this case squidex will act as a resource of your external identity server, and you need to rewrite the frontend of squidex to authenticate through your external identity server and get the tokens from there.

I guess you managed to do the option one from the above list, and now only problem is that you get signed out after logged in. In that case check the local storage, because squidex frontend is using oidc js lib to authenticate with the backend, and that one saves the token in local storage. I had a similiar issue before, and the solution was some sort of config modification.

It depends where you want to store your user information.

if it is fine for you to have multiple user databases, you can keep identity server. If you want to have a single place for all your user data you can get rid of identity server.

Angular is caching the user inforrmation in the local store, but there is no way to disable it.

You are right I implemented the first option. I created my own Identity Server and is acting as an external login provider. I am putting the code here maybe it will be of interest to anyone.

AuthenticationService.cs

public static class AuthenticationServices
{
    public static void AddMyAuthentication(this IServiceCollection services, IConfiguration config)
    {
        var identityOptions = config.GetSection("identity").Get<MyIdentityOptions>();
        var urlsOptions = config.GetSection("urls").Get<MyUrlsOptions>();

        services.AddOidcStateDataFormatterCache();

        services
        .AddAuthentication()
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = IdentityConstants.ExternalScheme; // This is very important
            options.Authority = "http://localhost:5000";
            options.ClientId = "my-client";
            options.ClientSecret = Constants.InternalClientSecret;
            options.RequireHttpsMetadata = identityOptions.RequiresHttps;
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.Scope.Add(Constants.ProfileScope);
            options.Scope.Add(Constants.RoleScope);
            options.Scope.Add("email");
            options.ResponseType = OpenIdConnectResponseType.IdToken;
            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = ClaimTypes.Name,
                RoleClaimType = ClaimTypes.Role,
            };
        })
        .AddCookie()
        .AddMyGoogleAuthentication(identityOptions)
        .AddMyMicrosoftAuthentication(identityOptions)
        .AddMyIdentityServerAuthentication(identityOptions, config);
    }
}

Client, Api Resources and Identity Resources on custom Identity Server

public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource(ApiScope)
            {
                UserClaims = new List<string>
                {
                    JwtClaimTypes.Email,
                    JwtClaimTypes.Role,                     
                }
            }
        };
    }

    public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
            new Client
            {
                ClientId = "my-client",
                ClientName = "my-client",
                ClientSecrets = new List<Secret> { new Secret(InternalClientSecret) },
                RedirectUris = new List<string>
                {
                    "http://localhost:50006/portal/signin-oidc",
                    "http://localhost:50006/orleans/signin-oidc",
                    "http://localhost:50006/identity-server/signin-oidc"
                },
                AccessTokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds,
                AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials,
                AllowAccessTokensViaBrowser = true,
                AllowedCorsOrigins =     { "http://localhost:50006" },
                AllowedScopes = new List<string>
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.Email,
                    ApiScope,
                    ProfileScope,
                    RoleScope,
                },
                RequireConsent = false,
                AlwaysSendClientClaims = true
            }
        };
    }

    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            new IdentityResources.Email(),
            new IdentityResource("role",
            new[]
            {
                JwtClaimTypes.Role
            }),
            new IdentityResource("squidex-profile",
            new[]
            {
                "urn:squidex:name",
                "urn:squidex:picture"
            })
        };
    }

I do not mind so I guess for now I can leave both Identity Servers as to be less intrusive to the original source code as I can.

This is very frustrating and I cannot wrap my head around it as it very difficult to achieve login / debug. Do you remember by any chance what config did you change?

Not really. I only remember that I’ve found the problem by installing an addon in firefox which outputs all the http traffic in stdout, so when I run firefox from command line I could see all the http traffic, and based on that I could figure out what is going wrong.

If you can provide a simple demo identity server I can also have a look.

@nicholas I had problems with my localdb and therefore I tested it with keycloak. The only config I need is:

        .AddOpenIdConnect("Keycloak", options =>
        {
            options.Authority = "http://localhost:8080/auth/realms/master";
            options.ClientId = "keycloak";
            options.ClientSecret = "db7a542b-69ed-43c6-af2a-5465f5a39769";
            options.RequireHttpsMetadata = identityOptions.RequiresHttps;
        })

I tried Auth0 and it works too. Let me give Keycloack a spin too. Will keep you posted.

If you use keycloak you have to ensure that the email is provided. You can just map the email to the profile scope under Mappings.

I also added the support for oidc to the code: https://github.com/Squidex/squidex/commit/f464844d5629f5ef8d35271c3046ccb3c22f18da

Keycloak works and so does Auth0. Only my Identity Server implementation (along with samples hosted by IdenityServer4) are not working.

Let us close this issue for now, if I find something I will make sure to update this ticket!

Thanks a million for your support on this and for updating the codebase. Much obliged!

You are welcome. Post closed.