Identity Server in Load Balancer environment in Azure

Hi,

We are currently trying to deploy Squidex Identity Server in an AKS environment having load balancers. We are facing issue when load is being distributed between different pods.

Generally, we have a web client which uses he identity server for authentication and everything works fine considering when there’s only one pod. But when setup the load balancers for the same, the web client throws exception after login from the identity server that it’s unable to verifiy the signature of the key. We are assuming that it’s happening because in case of load balancers there’s a possibility that the request was submitted and completed on one pod of the identity server but when the validation happens the identity server gets switched to a different pod.

Is there a way we can fix this issue?

Log snippet:

    fail: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[17]
      Exception occurred while processing message.
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match key

Which version do you use? It sounds like the encryption key is not shared. But the 3.1 version is used a mongo key store.

The problem is that the 3.1 version seems to have some issues and I am looking for people who want to contribute.

We are currently using 2.2. We were also facing issue with 3.1 so for now reverted back to the old one.

If we get the 3.1 working it should be solved.

In 2.2 we don’t have any workaround or configuration change we can take a look at?

Exactly…there is no config yet

Hi,
I made a lot of progress with the 3.1 version. It requires a few changes to the schemas but it should work now. If you are motivated you can test it.

The changes:

Clients

The changes are not needed and just improve the UX a little bit.

  • Disable field to disable a client
  • Allowed values for grant types.
{
    "properties": {
        "label": "Clients"
    },
    "scripts": {},
    "fieldsInLists": [],
    "fieldsInReferences": [],
    "fields": [
        {
            "name": "clientId",
            "properties": {
                "isRequired": true,
                "fieldType": "String",
                "editor": "Input",
                "isUnique": true,
                "label": "Client Id",
                "hints": "Unique id of the client."
            },
            "partitioning": "invariant"
        },
        {
            "name": "clientName",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Client Name",
                "hints": "Client display name (used for logging and consent screen)."
            },
            "partitioning": "language"
        },
        {
            "name": "clientUri",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Client Uri",
                "hints": "URI to further information about client (used on consent screen)."
            },
            "partitioning": "language"
        },
        {
            "name": "logo",
            "properties": {
                "fieldType": "Assets",
                "previewMode": "ImageAndFileName",
                "mustBeImage": true,
                "label": "Logo",
                "hints": "URI to client logo (used on consent screen)."
            },
            "partitioning": "invariant"
        },
        {
            "name": "requireConsent",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Require Consent",
                "hints": "Specifies whether a consent screen is required."
            },
            "partitioning": "invariant"
        },
        {
            "name": "allowOfflineAccess",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Allow Offline Access",
                "hints": "Gets or sets a value indicating whether to allow offline access."
            },
            "partitioning": "invariant"
        },
        {
            "name": "clientSecrets",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "Client Secrets",
                "hints": "Client secrets - only relevant for flows that require a secret."
            },
            "partitioning": "invariant"
        },
        {
            "name": "allowedScopes",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "Allowed Scopes",
                "hints": "Specifies the api scopes that the client is allowed to request."
            },
            "partitioning": "invariant"
        },
        {
            "name": "allowedGrantTypes",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "allowedValues": [
                    "authorization_code",
                    "implicit",
                    "client_credentials"
                ],
                "label": "Allowed Grant Types",
                "hints": "Specifies the allowed grant type."
            },
            "partitioning": "invariant"
        },
        {
            "name": "redirectUris",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "Redirect Uris",
                "hints": "Specifies allowed URIs to return tokens or authorization codes to"
            },
            "partitioning": "invariant"
        },
        {
            "name": "postLogoutRedirectUris",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "Post Logout Redirect Uris",
                "hints": "Specifies allowed URIs to redirect to after logout."
            },
            "partitioning": "invariant"
        },
        {
            "name": "allowedCorsOrigins",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "Allowed Cors Origins",
                "hints": "Gets or sets the allowed CORS origins for JavaScript clients."
            },
            "partitioning": "invariant"
        }
    ],
    "isPublished": true
}

2. API Scopes

This is a new schema that is needed because of the changes in identity server.

{
    "properties": {
        "label": "API Scopes"
    },
    "scripts": {},
    "fieldsInLists": [],
    "fieldsInReferences": [],
    "fields": [
        {
            "name": "name",
            "properties": {
                "isRequired": true,
                "fieldType": "String",
                "editor": "Input",
                "isUnique": true,
                "label": "Name",
                "hints": "The unique name of the API scope."
            },
            "partitioning": "invariant"
        },
        {
            "name": "displayName",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Display Name",
                "hints": "The display name of the API scope."
            },
            "partitioning": "language"
        },
        {
            "name": "description",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Description",
                "hints": "The description name of the API scope."
            },
            "partitioning": "language"
        },
        {
            "name": "disabled",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Disabled",
                "hints": "Enable or disable the scope."
            },
            "partitioning": "invariant"
        },
        {
            "name": "emphasize",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Emphasize",
                "hints": "Emphasize the API scope for important scopes."
            },
            "partitioning": "invariant"
        },
        {
            "name": "userClaims",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "User Claims",
                "hints": "List of accociated user claims that should be included when this resource is requested."
            },
            "partitioning": "invariant"
        }
    ],
    "isPublished": true
}

3. API Resources

Changes are needed: Especially the references to the API scopes.

Read more about it here: https://identityserver4.readthedocs.io/en/latest/topics/resources.html#migration-steps-to-v4

{
    "properties": {
        "label": "API Resources"
    },
    "scripts": {},
    "fieldsInLists": [],
    "fieldsInReferences": [],
    "fields": [
        {
            "name": "name",
            "properties": {
                "isRequired": true,
                "fieldType": "String",
                "editor": "Input",
                "isUnique": true,
                "label": "Name",
                "hints": "The unique name of the API."
            },
            "partitioning": "invariant"
        },
        {
            "name": "displayName",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Display Name",
                "hints": "The display name of the API."
            },
            "partitioning": "language"
        },
        {
            "name": "description",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Description",
                "hints": "The description name of the API."
            },
            "partitioning": "language"
        },
        {
            "name": "disabled",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Disabled",
                "hints": "Enable or disable the API."
            },
            "partitioning": "invariant"
        },
        {
            "name": "scopes",
            "properties": {
                "fieldType": "References",
                "editor": "List",
                "schemaIds": [
                    "<ADD SCHEMA ID TO API SCOPES HERE>"
                ],
                "label": "Scopes",
                "hints": "The scopes for this API."
            },
            "partitioning": "invariant"
        },
        {
            "name": "userClaims",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "User Claims",
                "hints": "List of accociated user claims that should be included when this resource is requested."
            },
            "partitioning": "invariant"
        }
    ],
    "isPublished": true
}

4. Identity Resources

Changes here are not needed. Just a few properties are added:

  • Disable: Same as clients
  • Emphasize: To mark it as important.
{
    "properties": {
        "label": "Identity Resources"
    },
    "scripts": {},
    "fieldsInLists": [],
    "fieldsInReferences": [],
    "fields": [
        {
            "name": "name",
            "properties": {
                "isRequired": true,
                "fieldType": "String",
                "editor": "Input",
                "isUnique": true,
                "label": "Name",
                "hints": "The unique name of the identity information."
            },
            "partitioning": "invariant"
        },
        {
            "name": "displayName",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Display Name",
                "hints": "The display name of the identity information."
            },
            "partitioning": "language"
        },
        {
            "name": "description",
            "properties": {
                "fieldType": "String",
                "editor": "Input",
                "label": "Description",
                "hints": "The description name of the identity information."
            },
            "partitioning": "language"
        },
        {
            "name": "required",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Required",
                "hints": "Specifies whether the user can de-select the scope on the consent screen."
            },
            "partitioning": "invariant"
        },
        {
            "name": "disabled",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Disabled",
                "hints": "Enable or disable the scope."
            },
            "partitioning": "invariant"
        },
        {
            "name": "emphasize",
            "properties": {
                "fieldType": "Boolean",
                "editor": "Toggle",
                "label": "Emphasize",
                "hints": "Emphasize the API scope for important scopes."
            },
            "partitioning": "invariant"
        },
        {
            "name": "userClaims",
            "properties": {
                "fieldType": "Tags",
                "editor": "Tags",
                "label": "User Claims",
                "hints": "List of accociated user claims that should be included when this resource is requested."
            },
            "partitioning": "invariant"
        }
    ],
    "isPublished": true
}

You can use the sync tool to make the necessary changes.