How the Thoras dashboard authenticates users, and how to configure the two built-in modes: generated htpasswd credentials or OIDC login against your identity provider.
The Thoras dashboard ships with authentication enabled by default. Requests hit
an oauth2-proxy sidecar in the
dashboard pod. Only authenticated requests reach the dashboard container.Two modes are supported, selected via thorasDashboard.auth.mode:
htpasswd (default): a username and password generated in-cluster on
first install by the Thoras config-controller. No external setup is required.
oidc: OIDC login against your identity provider (Okta, Azure Entra ID,
or any OIDC-compliant provider). You provide the client credentials in a
Secret you manage.
Only oidc gives you per-person attribution in the
audit trail; htpasswd shares one identity across
everyone with dashboard access.
User -> oauth2-proxy sidecar -> Thoras Dashboard (loopback) ^ htpasswd file OR OIDC IdP
Fields under the unused mode’s block are ignored, so you can switch modes by
changing auth.mode.
Credentials and session cookies are only meaningful behind TLS. Terminate TLS
at your ingress before exposing the dashboard beyond kubectl port-forward,
and set thorasDashboard.auth.cookieSecure: true. The chart default is
false so that port-forward works over plain HTTP. This default is suitable
for evaluation only. Safari drops Secure cookies over plain HTTP, so you
cannot enable cookieSecure and use kubectl port-forward together from a
Mac.
Before chart 5.0, the documented way to protect the dashboard was to add your
own oauth2-proxy container under thorasDashboard.extraContainers and retarget
the Service at port 4180. The chart now ships that sidecar for you. Migrating
keeps your IdP app registration and your existing oauth2-proxy-secrets Secret.
Only values.yaml changes.Remove the old wiring, noting the values of your existing oauth2-proxy flags
for the next step:
Add the built-in sidecar configuration, substituting the values you noted:
thorasDashboard: auth: mode: oidc oidc: provider: oidc # or entra-id; reuse your existing --provider value (Okta uses oidc) issuerURL: <your --oidc-issuer-url value> redirectURL: <your --redirect-url value> emailDomains: [yourcompany.com] # your --email-domain value(s) existingSecret: secretName: oauth2-proxy-secrets
The chart reads client-id, client-secret, and cookie-secret from the
existing Secret by default (key names are configurable under
auth.oidc.existingSecret). The Service goes back to targeting the dashboard’s
containerPort, now owned by the chart’s sidecar, so no service.targetPort
override is needed.
Set thorasDashboard.auth.enabled: false if you already gate the dashboard
somewhere else, such as an auth plugin at the ingress or gateway, a service-mesh
policy, or a custom SSO sidecar wired through thorasDashboard.extraContainers
and thorasDashboard.service.targetPort.
thorasDashboard: auth: enabled: false
With built-in auth disabled, the dashboard’s nginx listens on
thorasDashboard.containerPort with nothing in front of it. Any workload in
the cluster that can reach the thoras-dashboard Service can then use part of
the Thoras API without authentication, bypassing the shared apiClientSecret.
Only disable built-in auth if something external gates every request. The
audit trail also can’t attribute actions to a person in
this mode; every action is recorded as unknown.
If you enable the chart’s
NetworkPolicy alongside OIDC mode,
the chart opens egress on port 443 from the dashboard pod so the sidecar can
reach your identity provider. Tighten this with a layered policy scoped to your
IdP’s hostnames (for example a CiliumNetworkPolicy with toFQDNs) if your
security posture requires it.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Thoras Documentation","description":"Documentation for Thoras, the autonomous control plane for compute efficiency. Predictive Kubernetes autoscaling, self-hosted.","colors":{"primary":"#6b998f","light":"#9bc1b9","dark":"#3e8055"},"logo":{"light":"https://mintcdn.com/thoras/d2dju-xNTLxLCVzd/logo/light.png?fit=max\u0026auto=format\u0026n=d2dju-xNTLxLCVzd\u0026q=85\u0026s=657db4d808549261842fe7ed7fd4332c","dark":"https://mintcdn.com/thoras/d2dju-xNTLxLCVzd/logo/dark.png?fit=max\u0026auto=format\u0026n=d2dju-xNTLxLCVzd\u0026q=85\u0026s=da741af6fba7fc23f3bd17420f7109ef","href":"https://docs.thoras.ai"},"favicon":"https://mintcdn.com/thoras/SlaX_dne9nsRxoWC/favicon.png?fit=max\u0026auto=format\u0026n=SlaX_dne9nsRxoWC\u0026q=85\u0026s=f2e4225c1bfcda4dc6ad64e7f51ebdde","navbar":{"links":[{"href":"https://thoras.ai","label":"Website"},{"href":"https://github.com/thoras-ai/helm-charts","label":"GitHub"}],"primary":{"type":"button","label":"Book a demo","href":"https://thoras.ai/book-a-demo"}},"footer":{"socials":{"linkedin":"https://www.linkedin.com/company/thoras","x":"https://x.com/thorasai","github":"https://github.com/thoras-ai","youtube":"https://www.youtube.com/@Thorasai"},"links":[{"header":"Product","items":[{"label":"Book a demo","href":"https://thoras.ai/book-a-demo"},{"label":"Cloud savings calculator","href":"https://thoras.ai/cloud-savings-calculator"},{"label":"Website","href":"https://thoras.ai"}]},{"header":"Company","items":[{"label":"About","href":"https://thoras.ai/about-us"},{"label":"Case studies","href":"https://thoras.ai/case-studies"},{"label":"Blog","href":"https://thoras.ai/blog"},{"label":"Newsroom","href":"https://thoras.ai/newsroom"},{"label":"Partners","href":"https://thoras.ai/partners"}]},{"header":"Resources","items":[{"label":"Quickstart","href":"https://docs.thoras.ai/getting-started/quickstart"},{"label":"GitHub","href":"https://github.com/thoras-ai/helm-charts"}]}]},"seo":{"metatags":{"og:title":"Thoras Documentation","og:description":"The autonomous control plane for compute efficiency. Predictive Kubernetes autoscaling, self-hosted.","og:image":"/og-image.png","og:url":"https://docs.thoras.ai","og:site_name":"Thoras Documentation","twitter:card":"summary_large_image","twitter:site":"@thorasai"},"indexing":"navigable"},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"thoras","hasAudioNarration":false,"actualSubdomain":"thoras","customDomains":["docs.thoras.ai"],"gitSource":{"type":"github","owner":"thoras-ai","repo":"docs-mintlify","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6adbbad25370aeb5d3fb84d5","collectionId":"da86ee12-1413-4831-9812-8a2a60415e27","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6adbbad25370aeb5d3fb84d5"},"entitlements":{},"buildId":"6adbbad25370aeb5d3fb84d5","clientVersion":"0.0.3966","searchFilterCounts":[{"language":"default","tag":"Documentation","count":28},{"language":"default","tag":"Guides","count":20},{"language":"default","tag":"Reference","count":8},{"language":"default","tag":"Operating Thoras","count":7},{"language":"default","tag":"Scaling workloads","count":7},{"language":"default","tag":"FAQ","count":6},{"language":"default","tag":"Installation","count":6},{"language":"default","tag":"Policies","count":4},{"language":"default","tag":"Dashboard authentication","count":3},{"language":"default","tag":"Getting Started","count":2}]},"assistantConfig":{},"hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"guides/dashboard-auth","slug":"/guides/dashboard-auth","pageMetadata":{"title":"Dashboard authentication","description":"How the Thoras dashboard authenticates users, and how to configure the two built-in modes: generated htpasswd credentials or OIDC login against your identity provider.","href":"/guides/dashboard-auth"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"How the Thoras dashboard authenticates users, and how to configure the two built-in modes: generated htpasswd credentials or OIDC login against your identity provider.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`2`,id:`overview`,children:`Overview`}),`\n`,_jsxs(_components.p,{children:[`The Thoras dashboard ships with authentication enabled by default. Requests hit\nan `,_jsx(_components.a,{href:`https://oauth2-proxy.github.io/oauth2-proxy/`,children:`oauth2-proxy`}),` sidecar in the\ndashboard pod. Only authenticated requests reach the dashboard container.`]}),`\n`,_jsxs(_components.p,{children:[`Two modes are supported, selected via `,_jsx(_components.code,{children:`thorasDashboard.auth.mode`}),`:`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`htpasswd`})}),` (default): a username and password generated in-cluster on\nfirst install by the Thoras config-controller. No external setup is required.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`oidc`})}),`: OIDC login against your identity provider (Okta, Azure Entra ID,\nor any OIDC-compliant provider). You provide the client credentials in a\nSecret you manage.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Only `,_jsx(_components.code,{children:`oidc`}),` gives you per-person attribution in the\n`,_jsx(_components.a,{href:`/guides/audit-trail`,children:`audit trail`}),`; `,_jsx(_components.code,{children:`htpasswd`}),` shares one identity across\neveryone with dashboard access.`]}),`\n`,_jsx(CodeBlock,{numberOfLines:`4`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`4`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`User -> oauth2-proxy sidecar -> Thoras Dashboard (loopback)`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` ^`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` htpasswd file OR OIDC IdP`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`Fields under the unused mode’s block are ignored, so you can switch modes by\nchanging `,_jsx(_components.code,{children:`auth.mode`}),`.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`Credentials and session cookies are only meaningful behind TLS. Terminate TLS\nat your ingress before exposing the dashboard beyond `,_jsx(_components.code,{children:`kubectl port-forward`}),`,\nand set `,_jsx(_components.code,{children:`thorasDashboard.auth.cookieSecure: true`}),`. The chart default is\n`,_jsx(_components.code,{children:`false`}),` so that port-forward works over plain HTTP. This default is suitable\nfor evaluation only. Safari drops Secure cookies over plain HTTP, so you\ncannot enable `,_jsx(_components.code,{children:`cookieSecure`}),` and use `,_jsx(_components.code,{children:`kubectl port-forward`}),` together from a\nMac.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`pick-a-mode`,children:`Pick a mode`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/guides/dashboard-auth/htpasswd`,children:`Default (htpasswd) auth`}),`: log in with the\ngenerated credentials, set your own, or supply your own Secret.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/guides/dashboard-auth/okta`,children:`Okta`}),`: OIDC login against Okta.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.a,{href:`/guides/dashboard-auth/entra`,children:`Azure Entra ID`}),`: OIDC login against Entra.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Other OIDC-compliant providers work with `,_jsx(_components.code,{children:`provider: oidc`}),`. The Okta and Entra\nguides show the general shape.`]}),`\n`,_jsx(Heading,{level:`2`,id:`migrating-from-a-hand-rolled-oauth2-proxy-sidecar`,children:`Migrating from a hand-rolled oauth2-proxy sidecar`}),`\n`,_jsxs(_components.p,{children:[`Before chart 5.0, the documented way to protect the dashboard was to add your\nown oauth2-proxy container under `,_jsx(_components.code,{children:`thorasDashboard.extraContainers`}),` and retarget\nthe Service at port `,_jsx(_components.code,{children:`4180`}),`. The chart now ships that sidecar for you. Migrating\nkeeps your IdP app registration and your existing `,_jsx(_components.code,{children:`oauth2-proxy-secrets`}),` Secret.\nOnly `,_jsx(_components.code,{children:`values.yaml`}),` changes.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Remove`}),` the old wiring, noting the values of your existing oauth2-proxy flags\nfor the next step:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`6`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`6`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`thorasDashboard`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` service`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` targetPort`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`4180`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # remove`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` extraContainers`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# remove the entire oauth2-proxy container`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`name`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`oauth2-proxy`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # ...`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Add`}),` the built-in sidecar configuration, substituting the values you noted:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`10`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`10`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`thorasDashboard`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` auth`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` mode`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`oidc`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` oidc`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` provider`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`oidc`}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:` # or entra-id; reuse your existing --provider value (Okta uses oidc)`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` issuerURL`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\u003cyour --oidc-issuer-url value>`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` redirectURL`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\u003cyour --redirect-url value>`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` emailDomains`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: [`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`yourcompany.com`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`] `}),_jsx(_components.span,{style:{color:`#6E7781`,\"--shiki-dark\":`#6A9955`},children:`# your --email-domain value(s)`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` existingSecret`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` secretName`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`oauth2-proxy-secrets`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`The chart reads `,_jsx(_components.code,{children:`client-id`}),`, `,_jsx(_components.code,{children:`client-secret`}),`, and `,_jsx(_components.code,{children:`cookie-secret`}),` from the\nexisting Secret by default (key names are configurable under\n`,_jsx(_components.code,{children:`auth.oidc.existingSecret`}),`). The Service goes back to targeting the dashboard’s\n`,_jsx(_components.code,{children:`containerPort`}),`, now owned by the chart’s sidecar, so no `,_jsx(_components.code,{children:`service.targetPort`}),`\noverride is needed.`]}),`\n`,_jsx(Heading,{level:`2`,id:`bring-your-own-auth`,children:`Bring your own auth`}),`\n`,_jsxs(_components.p,{children:[`Set `,_jsx(_components.code,{children:`thorasDashboard.auth.enabled: false`}),` if you already gate the dashboard\nsomewhere else, such as an auth plugin at the ingress or gateway, a service-mesh\npolicy, or a custom SSO sidecar wired through `,_jsx(_components.code,{children:`thorasDashboard.extraContainers`}),`\nand `,_jsx(_components.code,{children:`thorasDashboard.service.targetPort`}),`.`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`3`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`3`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`thorasDashboard`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` auth`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` enabled`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#569CD6`},children:`false`})]}),`\n`]})})}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`With built-in auth disabled, the dashboard’s nginx listens on\n`,_jsx(_components.code,{children:`thorasDashboard.containerPort`}),` with nothing in front of it. Any workload in\nthe cluster that can reach the `,_jsx(_components.code,{children:`thoras-dashboard`}),` Service can then use part of\nthe Thoras API without authentication, bypassing the shared `,_jsx(_components.code,{children:`apiClientSecret`}),`.\nOnly disable built-in auth if something external gates every request. The\n`,_jsx(_components.a,{href:`/guides/audit-trail`,children:`audit trail`}),` also can’t attribute actions to a person in\nthis mode; every action is recorded as `,_jsx(_components.code,{children:`unknown`}),`.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`networkpolicy-and-oidc`,children:`NetworkPolicy and OIDC`}),`\n`,_jsxs(_components.p,{children:[`If you enable the chart’s\n`,_jsx(_components.a,{href:`/installation/advanced-setup#networkpolicy`,children:`NetworkPolicy`}),` alongside OIDC mode,\nthe chart opens egress on port `,_jsx(_components.code,{children:`443`}),` from the dashboard pod so the sidecar can\nreach your identity provider. Tighten this with a layered policy scoped to your\nIdP’s hostnames (for example a `,_jsx(_components.code,{children:`CiliumNetworkPolicy`}),` with `,_jsx(_components.code,{children:`toFQDNs`}),`) if your\nsecurity posture requires it.`]}),`\n`,_jsx(Heading,{level:`2`,id:`further-reading`,children:`Further reading`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:_jsx(_components.a,{href:`https://oauth2-proxy.github.io/oauth2-proxy/`,children:`oauth2-proxy documentation home`})}),`\n`,_jsx(_components.li,{children:_jsx(_components.a,{href:`https://oauth2-proxy.github.io/oauth2-proxy/configuration/overview`,children:`Configuration overview / full flag reference`})}),`\n`,_jsx(_components.li,{children:_jsx(_components.a,{href:`https://oauth2-proxy.github.io/oauth2-proxy/configuration/session_storage`,children:`Session storage options`})}),`\n`,_jsx(_components.li,{children:_jsx(_components.a,{href:`https://oauth2-proxy.github.io/oauth2-proxy/configuration/tls`,children:`TLS configuration`})}),`\n`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Dashboard authentication","description":"How the Thoras dashboard authenticates users, and how to configure the two built-in modes: generated htpasswd credentials or OIDC login against your identity provider.","href":"/guides/dashboard-auth"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Overview","slug":"overview","depth":2,"children":[]},{"title":"Pick a mode","slug":"pick-a-mode","depth":2,"children":[]},{"title":"Migrating from a hand-rolled oauth2-proxy sidecar","slug":"migrating-from-a-hand-rolled-oauth2-proxy-sidecar","depth":2,"children":[]},{"title":"Bring your own auth","slug":"bring-your-own-auth","depth":2,"children":[]},{"title":"NetworkPolicy and OIDC","slug":"networkpolicy-and-oidc","depth":2,"children":[]},{"title":"Further reading","slug":"further-reading","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"thoras","siteName":"Thoras Documentation","title":"Thoras Documentation","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-30T18:14:11.215Z","originalFileLocation":"guides/dashboard-auth.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"How the Thoras dashboard authenticates users, and how to configure the two built-in modes: generated htpasswd credentials or OIDC login against your identity provider.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Thoras Documentation\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Thoras Documentation\">\u003cmeta data-mint-head property=\"og:description\" content=\"The autonomous control plane for compute efficiency. Predictive Kubernetes autoscaling, self-hosted.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Thoras Documentation\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.thoras.ai/guides/dashboard-auth\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://thoras.mintlify.app/og-image.png\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Thoras Documentation\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"The autonomous control plane for compute efficiency. Predictive Kubernetes autoscaling, self-hosted.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://thoras.mintlify.app/og-image.png\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/thoras/ZwCsC7wnwzlNbvBX/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Thoras Documentation\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#6b998f\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.thoras.ai/guides/dashboard-auth\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/guides/dashboard-auth.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.thoras.ai/#organization\",\"name\":\"Thoras Documentation\",\"url\":\"https://docs.thoras.ai\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/thoras/d2dju-xNTLxLCVzd/logo/light.png?fit=max\u0026auto=format\u0026n=d2dju-xNTLxLCVzd\u0026q=85\u0026s=657db4d808549261842fe7ed7fd4332c\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.thoras.ai#website\",\"name\":\"Thoras Documentation\",\"url\":\"https://docs.thoras.ai\",\"publisher\":{\"@id\":\"https://docs.thoras.ai/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.thoras.ai/guides/dashboard-auth#webpage\",\"url\":\"https://docs.thoras.ai/guides/dashboard-auth\",\"name\":\"Dashboard authentication\",\"description\":\"How the Thoras dashboard authenticates users, and how to configure the two built-in modes: generated htpasswd credentials or OIDC login against your identity provider.\",\"dateModified\":\"2026-09-30T18:14:11.215Z\",\"isPartOf\":{\"@id\":\"https://docs.thoras.ai#website\"},\"breadcrumb\":{\"@id\":\"https://docs.thoras.ai/guides/dashboard-auth#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.thoras.ai/guides/dashboard-auth#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Guides\",\"item\":\"https://docs.thoras.ai/guides/configuring-asts\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Dashboard authentication\",\"item\":\"https://docs.thoras.ai/guides/dashboard-auth\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.thoras.ai/guides/dashboard-auth#article\",\"headline\":\"Dashboard authentication\",\"name\":\"Dashboard authentication\",\"description\":\"How the Thoras dashboard authenticates users, and how to configure the two built-in modes: generated htpasswd credentials or OIDC login against your identity provider.\",\"url\":\"https://docs.thoras.ai/guides/dashboard-auth\",\"mainEntityOfPage\":{\"@id\":\"https://docs.thoras.ai/guides/dashboard-auth#webpage\"},\"image\":\"https://docs.thoras.ai/og-image.png\",\"dateModified\":\"2026-09-30T18:14:11.215Z\",\"publisher\":{\"@id\":\"https://docs.thoras.ai/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.thoras.ai#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Thoras Documentation\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Thoras Documentation","nav":{"global":null,"tabs":[{"tab":"Documentation","groups":[{"group":"Getting Started","pages":[{"title":"What is Thoras?","href":"/getting-started/what-is-thoras"},{"title":"Quickstart","href":"/getting-started/quickstart"}]},{"group":"Installation","pages":[{"title":"System Requirements","href":"/installation/system-requirements"},{"title":"Advanced Setup","href":"/installation/advanced-setup"},{"title":"Persistent Storage","href":"/installation/persistent-storage"},{"title":"Credentials","href":"/installation/credentials"},{"title":"Private Container Registry","href":"/installation/private-registry"},{"title":"Upgrading","href":"/installation/upgrading"}]},{"group":"Guides","pages":[{"group":"Scaling workloads","pages":[{"title":"Configuring AIScaleTargets","href":"/guides/configuring-asts"},{"title":"Predictive Vertical Pod Rightsizing","href":"/guides/vertical-pod-rightsizing"},{"title":"In-Place Resize Behavior","href":"/guides/in-place-resize"},{"title":"Predictive Horizontal Pod Autoscaling","href":"/guides/hpa"},{"title":"Choosing a Forecast Optimization Strategy","href":"/guides/forecast-optimization-strategies"},{"title":"OOM Remediation","href":"/guides/oom-remediation"},{"title":"JVM Workload Scaling","href":"/guides/jvm-scaling"}]},{"group":"Operating Thoras","pages":[{"title":"Enabling Autonomous Mode","href":"/guides/enabling-autonomous-mode"},{"title":"Pausing Autonomous Scaling","href":"/guides/pause-scaling"},{"title":"Audit Trail","href":"/guides/audit-trail"},{"title":"Stopping Autonomous Scaling","href":"/guides/stopping-autoscaling"},{"title":"ArgoCD","href":"/guides/argo-cd"},{"title":"Tuning for Large Clusters","href":"/guides/large-clusters"},{"title":"Diagnostics Export","href":"/guides/diagnostics"}]},{"title":"Monitoring Thoras","href":"/guides/metrics"},{"title":"Cost Estimates","href":"/guides/cost-estimates"},{"title":"AWS Cost Explorer","description":"Collect your cluster's real billed AWS spend so Thoras reports actual cost instead of estimates.","href":"/guides/aws-cost-explorer"},{"group":"Dashboard authentication","root":{"title":"Dashboard authentication","href":"/guides/dashboard-auth"},"pages":[{"title":"Default (htpasswd) dashboard auth","description":"Log into the Thoras dashboard with the generated username and password, or provide your own.","href":"/guides/dashboard-auth/htpasswd"},{"title":"Okta OIDC dashboard auth","description":"Log into the Thoras dashboard via Okta using the chart's built-in oauth2-proxy sidecar in OIDC mode.","href":"/guides/dashboard-auth/okta"},{"title":"Azure Entra OIDC dashboard auth","description":"Log into the Thoras dashboard via Azure Entra ID (formerly Azure AD) using the chart's built-in oauth2-proxy sidecar in OIDC mode.","href":"/guides/dashboard-auth/entra"}]}]}]},{"tab":"Reference","pages":[{"title":"Reference","href":"/reference/ast-definition"}]},{"tab":"FAQ","pages":[{"title":"FAQ","href":"/faq/introduction"}]},{"tab":"Policies","pages":[{"title":"Policies","href":"/policies/support-policy"}]}]},"title":"Thoras Documentation","buildId":"dpl_cadrRc5H9XQiJQbDFN5mcYRs2PAj","deploymentId":"dpl_cadrRc5H9XQiJQbDFN5mcYRs2PAj","dynamics":[],"chunks":[]}