Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite Redirect Loop with Duende IdentityServer using Terraform Helm Provider on AWS Kubernetes #14

Closed
lirei-lab opened this issue Oct 30, 2023 · 9 comments
Labels
bug Something isn't working

Comments

@lirei-lab
Copy link

Summary:

I'm experiencing an infinite redirect loop when deploying TheIdServer with Duende IdentityServer on a Kubernetes cluster in AWS (using Linux instances). The deployment is managed via the Terraform Helm provider.

Details:

  • Expected Behavior: After authenticating with Duende IdentityServer, I expect to be redirected back to my application's designated return URL and remain there.

  • Actual Behavior: After authentication, I'm caught in an infinite loop of redirects between my application and TheIdServer.

  • Reproduction Steps:

    1. Followed the tutorial from this link.
    2. Deployed TheIdServer on an AWS Kubernetes cluster (Linux instances) using Terraform Helm provider.
    3. Started the authentication process from my application.
    4. Completed the authentication on Duende IdentityServer.
    5. Observed the infinite redirect loop between the application and TheIdServer.
  • Environment & Configuration:

    • OS: AWS Kubernetes Cluster with Linux instances.
    • Browser: Predominantly on Chrome, but the issue is reproducible on other browsers as well.
    • TheIdServer Version: Using image aguacongas/theidserver.duende:7.4.6
    • Terraform: v1.6.2 on linux_amd64
    • Providers:
      • registry.terraform.io/hashicorp/helm v2.11.0
      • registry.terraform.io/hashicorp/kubernetes v2.23.0
      • registry.terraform.io/hashicorp/random v3.5.1
      • registry.terraform.io/hashicorp/tls v4.0.4
    • Any custom configurations or setups, if any.

Additional Information:

  1. I have ensured that the client application's redirect URL and post-logout redirect URL are correctly configured.
  2. Cookie names have been checked for uniqueness to avoid any overlap.
  3. The issue persists across different environments (dev, staging).
@lirei-lab lirei-lab added the bug Something isn't working label Oct 30, 2023
@aguacongas
Copy link
Member

How did you configure your app ?
What's auth flow you use ?
Can you share browser log (console and network) ?

@lirei-lab
Copy link
Author

lirei-lab commented Oct 30, 2023

Sure, here's the configuration I'm using with the Terraform Helm provider:
The domain was changed intentionally.

module "theidserver" {
  source = "Aguafrommars/theidserver/helm"
  host = "myserver.example.com"
  tls_issuer_name = "letsencrypt-prod"
  tls_issuer_kind = "ClusterIssuer"
  create_namespace = true

  override_settings = local.values
  image = {
    repository = "aguacongas/theidserver.duende"
    pullPolicy = "Always"
    tag = "7.4.6"
  }
}

Additionally, here's the related configuration from my YAML file:

NAME: theidserver
LAST DEPLOYED: Mon Oct 30 13:45:05 2023
NAMESPACE: theidserver
STATUS: failed
REVISION: 1
CHART: theidserver
VERSION: 4.8.0
APP_VERSION: 4.5.2
USER-SUPPLIED VALUES:
adminSettings:
  apiBaseUrl: https://www.myserver.example.com/api
  providerOptions:
    authority: https://www.myserver.example.com
    postLogoutRedirectUri: https://www.myserver.example.com/authentication/logout-callback
    redirectUri: https://www.myserver.example.com/authentication/login-callback
  settingsOptions:
    apiUrl: https://www.myserver.example.com/api/api/configuration
  welcomeContenUrl: https://www.myserver.example.com/api/welcomefragment
appSettings:
  env:
    InitialData__Users__0__Password: '@s4foqGjoa9k4xMC'
  file:
    ApiAuthentication:
      ApiSecret: e14E5]V&@XeX%[Rh
      Authority: https://www.myserver.example.com
    BackchannelAuthenticationUserNotificationServiceOptions:
      ApiUrl: https://www.myserver.example.com/api/email
      Authority: https://www.myserver.example.com
      ClientSecret: '@%}51pk:Ze+157}4'
    EmailApiAuthentication:
      ApiUrl: https://www.myserver.example.com/api/email
      Authority: https://www.myserver.example.com
      ClientSecret: '@%}51pk:Ze+157}4'
    InitialData:
      Apis:
      - ApiSecrets:
        - Type: SharedSecret
          Value: e14E5]V&@XeX%[Rh
        DisplayName: TheIdServer admin API
        Name: theidserveradminapi
        Scopes:
        - theidserveradminapi
        - theidservertokenapi
        UserClaims:
        - name
        - role
      Clients:
      - AccessTokenType: Reference
        AllowedCorsOrigins:
        - https://www.myserver.example.com
        AllowedGrantTypes:
        - authorization_code
        AllowedScopes:
        - openid
        - profile
        - theidserveradminapi
        BackChannelLogoutSessionRequired: false
        ClientClaimsPrefix: null
        ClientId: theidserveradmin
        ClientName: TheIdServer admin SPA Client
        ClientUri: https://www.myserver.example.com
        FrontChannelLogoutSessionRequired: false
        PostLogoutRedirectUris:
        - https://www.myserver.example.com/authentication/logout-callback
        RedirectUris:
        - https://www.myserver.example.com/authentication/login-callback
        RequireClientSecret: false
        RequirePkce: true
      - AccessTokenType: Reference
        AllowedGrantTypes:
        - client_credentials
        AllowedScopes:
        - openid
        - profile
        - theidserveradminapi
        BackChannelLogoutSessionRequired: false
        Claims:
        - Type: role
          Value: Is4-Writer
        - Type: role
          Value: Is4-Reader
        ClientClaimsPrefix: null
        ClientId: public-server
        ClientName: Public server Credentials Client
        ClientSecrets:
        - Type: SharedSecret
          Value: '@%}51pk:Ze+157}4'
        FrontChannelLogoutSessionRequired: false
      - AllowAccessTokensViaBrowser: true
        AllowedCorsOrigins:
        - https://www.myserver.example.com
        AllowedGrantTypes:
        - implicit
        BackChannelLogoutSessionRequired: false
        ClientClaimsPrefix: null
        ClientId: theidserver-swagger
        ClientName: TheIdServer Swagger UI
        FrontChannelLogoutSessionRequired: false
        RedirectUris:
        - https://www.myserver.example.com/authentication/login-callback
        RequireClientSecret: false
      Users:
      - Claims:
        - ClaimType: name
          ClaimValue: TheIdServer Admin
        - ClaimType: given_name
          ClaimValue: Admin
        - ClaimType: nickname
          ClaimValue: Admin
        Email: admin@myserver.example.com
        EmailConfirmed: true
        Roles:
        - Is4-Writer
        - Is4-Reader
        UserName: admin@myserver.example.com
dataProtection:
  create: false
  crt: LS0tLS1CRUdJT.....
  key: LS0tLS1CRUdJT..
image:
  pullPolicy: Always
  repository: aguacongas/theidserver.duende
  tag: 7.4.6
ingress:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
    nginx.ingress.kubernetes.io/proxy-buffering: "on"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  enabled: true
  hosts:
  - host: www.myserver.example.com
  tls:
    hosts:
    - www.myserver.example.com
mysql:
  architecture: replication
  auth:
    database: theidserver
    password: p5OoG9fKYDnUqyt1
    replicationPassword: $eMTZb}v=Pjw%${z
    replicationUser: theidserverReplication
    rootPassword: nRYfHb7dYscfjAfT
    username: theidserver
redis:
  auth:
    password: Y(c&vyj+1N6J#Ug8
  replica:
    replicaCount: 1
replicaCount: 3
seq:
  ingress:
    annotations:
      cert-manager.io/cluster-issuer: letsencrypt-prod
      kubernetes.io/ingress.class: nginx
      nginx.ingress.kubernetes.io/ssl-redirect: "true"
    tls:
    - hosts:
      - seq.myserver.example.com
      secretName: theidserver-seq
  ui:
    ingress:
      enabled: true
      hosts:
      - seq.myserver.example.com
      path: /
service:
  ports:
    https: 443
signingKey:
  create: false
  crt: LS0tLS1C...
  key: LS0tLS1CRUdJTiBSU0Eg....
ssl:
  ca:
    create: false
    trust: false
  create: false
  issuer:
    enabled: true
    kind: ClusterIssuer
    ref: letsencrypt-prod

COMPUTED VALUES:
adminSettings:
  administratorEmail: aguacongas@gmail.com
  apiBaseUrl: https://www.myserver.example.com/api
  authenticationPaths:
    remoteProfilePath: /identity/account/manage
    remoteRegisterPath: /identity/account/register
  loggingOptions:
    minimum: Warning
  menuOptions:
    showSettings: true
  prerendered: true
  providerOptions:
    authority: https://www.myserver.example.com
    clientId: theidserveradmin
    defaultScopes:
    - theidserveradminapi
    postLogoutRedirectUri: https://www.myserver.example.com/authentication/logout-callback
    redirectUri: https://www.myserver.example.com/authentication/login-callback
    responseType: code
  settingsOptions:
    apiUrl: https://www.myserver.example.com/api/api/configuration
    typeName: Aguacongas.TheIdServer.BlazorApp.Models.ServerConfig, Aguacongas.TheIdServer.BlazorApp.Infrastructure
  userOptions:
    roleClaim: role
  welcomeContenUrl: https://www.myserver.example.com/api/welcomefragment
affinity: {}
appSettings:
  env:
    InitialData__Users__0__Password: '@s4foqGjoa9k4xMC'
  file:
    AccountOptions:
      AutomaticRedirectAfterSignOut: true
      ShowLogoutPrompt: false
    ApiAuthentication:
      ApiName: theidserveradminapi
      ApiSecret: e14E5]V&@XeX%[Rh
      Authority: https://www.myserver.example.com
      CacheDuration: "0:10:0"
      EnableCaching: true
      LegacyAudienceValidation: true
      RequireHttpsMetadata: false
      SupportedTokens: Both
    AuthenticatorIssuer: TheIdServer
    BackchannelAuthenticationUserNotificationServiceOptions:
      ApiUrl: https://www.myserver.example.com/api/email
      Authority: https://www.myserver.example.com
      ClientId: public-server
      ClientSecret: '@%}51pk:Ze+157}4'
      HttpClientName: ciba
      Scope: theidserveradminapi
    DataProtectionOptions:
      KeyProtectionOptions:
        KeyProtectionKind: X509
        X509CertificatePath: /usr/local/share/ca-certificates/dp.pfx
      StorageKind: EntityFramework
    DbType: MySql
    DynamicClientRegistrationOptions:
      AllowedContacts:
        AllowedHosts:
        - www.certification.openid.net
        Contact: certification@oidf.org
    DynamicConfigurationOptions:
      ProviderType: Aguacongas.DynamicConfiguration.Redis.RedisConfigurationProvider,
        Aguacongas.DynamicConfiguration.Redis
    EmailApiAuthentication:
      ApiUrl: https://www.myserver.example.com/api/email
      Authority: https://www.myserver.example.com
      ClientId: public-server
      ClientSecret: '@%}51pk:Ze+157}4'
      HttpClientName: email
      Scope: theidserveradminapi
    EnableOpenApiDoc: true
    IdentityOptions:
      SignInOptions:
        RequireConfirmedAccount: false
      UserOptions:
        AllowedUserNameCharacters: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+ '
    IdentityServer:
      Key:
        KeyProtectionOptions:
          KeyProtectionKind: X509
          X509CertificatePath: /usr/local/share/ca-certificates/sk.pfx
        StorageKind: EntityFramework
        Type: KeysRotation
    IdentityServerOptions:
      Endpoints:
        EnableJwtRequestUri: true
    InitialData:
      ApiScopes:
      - DisplayName: TheIdServer admin API scope
        Name: theidserveradminapi
        UserClaims:
        - name
        - role
      - DisplayName: TheIdServer token API scope
        Name: theidservertokenapi
        UserClaims:
        - name
        - role
      Apis:
      - ApiSecrets:
        - Type: SharedSecret
          Value: e14E5]V&@XeX%[Rh
        DisplayName: TheIdServer admin API
        Name: theidserveradminapi
        Scopes:
        - theidserveradminapi
        - theidservertokenapi
        UserClaims:
        - name
        - role
      Clients:
      - AccessTokenType: Reference
        AllowedCorsOrigins:
        - https://www.myserver.example.com
        AllowedGrantTypes:
        - authorization_code
        AllowedScopes:
        - openid
        - profile
        - theidserveradminapi
        BackChannelLogoutSessionRequired: false
        ClientClaimsPrefix: null
        ClientId: theidserveradmin
        ClientName: TheIdServer admin SPA Client
        ClientUri: https://www.myserver.example.com
        FrontChannelLogoutSessionRequired: false
        PostLogoutRedirectUris:
        - https://www.myserver.example.com/authentication/logout-callback
        RedirectUris:
        - https://www.myserver.example.com/authentication/login-callback
        RequireClientSecret: false
        RequirePkce: true
      - AccessTokenType: Reference
        AllowedGrantTypes:
        - client_credentials
        AllowedScopes:
        - openid
        - profile
        - theidserveradminapi
        BackChannelLogoutSessionRequired: false
        Claims:
        - Type: role
          Value: Is4-Writer
        - Type: role
          Value: Is4-Reader
        ClientClaimsPrefix: null
        ClientId: public-server
        ClientName: Public server Credentials Client
        ClientSecrets:
        - Type: SharedSecret
          Value: '@%}51pk:Ze+157}4'
        FrontChannelLogoutSessionRequired: false
      - AllowAccessTokensViaBrowser: true
        AllowedCorsOrigins:
        - https://www.myserver.example.com
        AllowedGrantTypes:
        - implicit
        BackChannelLogoutSessionRequired: false
        ClientClaimsPrefix: null
        ClientId: theidserver-swagger
        ClientName: TheIdServer Swagger UI
        FrontChannelLogoutSessionRequired: false
        RedirectUris:
        - https://www.myserver.example.com/authentication/login-callback
        RequireClientSecret: false
      Users:
      - Claims:
        - ClaimType: name
          ClaimValue: TheIdServer Admin
        - ClaimType: given_name
          ClaimValue: Admin
        - ClaimType: nickname
          ClaimValue: Admin
        Email: admin@myserver.example.com
        EmailConfirmed: true
        Roles:
        - Is4-Writer
        - Is4-Reader
        UserName: admin@myserver.example.com
    Migrate: true
    RedisConfigurationOptions: 
      Channel: Aguacongas.TheIdServer.Duende.Channel
      HashKey: Aguacongas.TheIdServer.Duende
    Seed: true
    Serilog:
      Enrich:
      - FromLogContext
      - WithMachineName
      - WithThreadId
      LevelSwitches:
        $controlSwitch: Information
      MinimumLevel:
        ControlledBy: $controlSwitch
      WriteTo:
      - Args:
          apiKey: DVYuookX2vOq078fuOyJ
          controlLevelSwitch: $controlSwitch
          serverUrl: http://localhost:5341
        Name: Seq
      - Args:
          outputTemplate: '[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}'
          theme: Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Literate, Serilog.Sinks.Console
        Name: Console
      - Args:
          outputTemplate: '[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}'
        Name: Debug
    SignalR:
      HubOptions:
        EnableDetailedErrors: true
      RedisOptions:
        Configuration:
          ChannelPrefix: TheIdServer
      UseMessagePack: true
    SiteOptions:
      Name: TheIdServer
    SwaggerUiSettings:
      OAuth2Client:
        AppName: TheIdServer Swagger UI
        ClientId: theidserver-swagger
        UsePkceWithAuthorizationCodeGrant: true
      WithCredentials: true
    TokenCleanupInterval: "00:05:00"
autoscaling:
  enabled: false
  maxReplicas: 100
  minReplicas: 1
  targetCPUUtilizationPercentage: 80
connectionString: null
dataProtection:
  cn: null
  create: false
  crt: LS0tLS1CRU....
  ipList: []
  key: LS0tLS1CR....
  sanList: []
deploymentAnnotations: {}
fullnameOverride: ""
image:
  pullPolicy: Always
  repository: aguacongas/theidserver.duende
  tag: 7.4.6
imagePullSecrets: []
ingress:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
    nginx.ingress.kubernetes.io/proxy-buffering: "on"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  enabled: true
  hosts:
  - host: www.myserver.example.com
  tls:
    hosts:
    - www.myserver.example.com
mysql: ....
 

@aguacongas
Copy link
Member

Are you able to launch and connect to the admin app ?

@lirei-user
Copy link

Yes, I was able to launch the admin app, but after accessing the website and Sign in, it automatically redirects me back to the login page repeatedly.

@aguacongas
Copy link
Member

Do you have an HTTPS certificate issue ?

@lirei-user
Copy link

No, the cert-manager and Let's Encrypt are working properly, and the certificate doesn't appear to be the source of the issue.

@aguacongas
Copy link
Member

Do you have CORS issue ?

@aguacongas
Copy link
Member

The issue is in the adminSettings : the client doesn't ask for scope openid and profile so the serveur doen't return an id_token.
Update your configuration to set defaultScopes with openid and profile :

adminSettings:
...
  providerOptions:
...
    defaultScopes:
    - openid
    - profile
    - theidserveradminapi

The request to /appsettings.json should reflect your changes and it should return a json like :

{
...
  "providerOptions": {
...
    "defaultScopes": [
      "openid",
      "profile",
      "theidserveradminapi"
    ],
...
}

It's a bug of the Helm chart I'm going to fix soon.

@aguacongas aguacongas transferred this issue from Aguafrommars/TheIdServer Nov 1, 2023
aguacongas added a commit that referenced this issue Nov 1, 2023
@lirei-user
Copy link

Thank you, it works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants