> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gcore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure OIDC authentication

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    <p>OIDC authentication enables Single Sign-On (SSO) for Kubernetes API access through an external identity provider — Okta, Azure AD, Keycloak, or any OIDC-compatible service.</p>

    <Steps>
      <Step title="Navigate to the Advanced settings tab">
        <Frame>
          <img src="https://mintcdn.com/gcore/c2W3cwtGjSbFG_V1/images/docs/edge-ai/managed-kubernetes/configure-oidc-authentication/configure-oidc-authentication-image1.png?fit=max&auto=format&n=c2W3cwtGjSbFG_V1&q=85&s=4c46ea21bcbacceae7ebad611b824003" alt="Advanced settings tab showing OIDC authentication and Cluster Autoscaler sections" width="1024" height="438" data-path="images/docs/edge-ai/managed-kubernetes/configure-oidc-authentication/configure-oidc-authentication-image1.png" />
        </Frame>
      </Step>

      <Step title="Expand OIDC authentication" />

      <Step title="Configure the identity provider settings and click Save changes">
        <Frame>
          <img src="https://mintcdn.com/gcore/c2W3cwtGjSbFG_V1/images/docs/edge-ai/managed-kubernetes/configure-oidc-authentication/configure-oidc-authentication-image2.png?fit=max&auto=format&n=c2W3cwtGjSbFG_V1&q=85&s=c5a77d558c0f30e4bd986120d998455d" alt="OIDC authentication form showing Issuer URL, Client ID, groups claim, signing algorithms, and username fields" width="1024" height="668" data-path="images/docs/edge-ai/managed-kubernetes/configure-oidc-authentication/configure-oidc-authentication-image2.png" />
        </Frame>
      </Step>
    </Steps>

    | Field               | Description                                                 |
    | ------------------- | ----------------------------------------------------------- |
    | Issuer URL          | The OIDC issuer endpoint (e.g., `https://example.com`)      |
    | Client ID           | Application client ID from the identity provider            |
    | Groups claim        | JWT claim containing group membership (optional)            |
    | Groups prefix       | Prefix added to group names (default: `oidc`)               |
    | Set required claims | Require specific claims in tokens                           |
    | Signing algorithms  | JWT signing algorithms to accept (default: RS256, optional) |
    | Username claim      | JWT claim used as the username (default: `sub`)             |
    | Username prefix     | Prefix added to usernames (default: `oidc`)                 |
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>OIDC authentication delegates Kubernetes API access to an external identity provider. Once configured, users authenticate with the provider and present the resulting JWT token to `kubectl` — Kubernetes validates the token against the configured issuer without contacting the provider again.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required, along with a
      [project ID](/api-reference/cloud/projects/list-projects)
      and a [region ID](/api-reference/cloud/regions/list-regions).
    </Info>

    <p>Set the following environment variables before running the examples:</p>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export GCORE_CLOUD_PROJECT_ID="{YOUR_PROJECT_ID}"
    export GCORE_CLOUD_REGION_ID="{YOUR_REGION_ID}"
    export CLUSTER_NAME="{YOUR_CLUSTER_NAME}"
    ```

    ## Configure OIDC

    <p>Pass the identity provider settings under `authentication.oidc`. Only `issuer_url` and `client_id` are typically required — all other fields are optional.</p>

    | Field             | Description                                                                                                  |
    | ----------------- | ------------------------------------------------------------------------------------------------------------ |
    | `issuer_url`      | OIDC discovery endpoint of the identity provider                                                             |
    | `client_id`       | Application client ID registered with the provider                                                           |
    | `username_claim`  | JWT claim used as the Kubernetes username (default: `sub`)                                                   |
    | `username_prefix` | Prefix prepended to usernames to avoid conflicts (default: `oidc:`)                                          |
    | `groups_claim`    | JWT claim containing group membership                                                                        |
    | `groups_prefix`   | Prefix prepended to group names (default: `oidc:`)                                                           |
    | `signing_algs`    | Accepted signing algorithms: `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`, `PS256`, `PS384`, `PS512` |
    | `required_claims` | Key-value pairs that must be present in the token                                                            |

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        import time
        import gcore

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])
        project_id = int(os.environ["GCORE_CLOUD_PROJECT_ID"])
        region_id = int(os.environ["GCORE_CLOUD_REGION_ID"])
        cluster_name = os.environ["CLUSTER_NAME"]

        result = client.cloud.k8s.clusters.update(
            cluster_name=cluster_name,
            project_id=project_id,
            region_id=region_id,
            authentication={
                "oidc": {
                    "issuer_url": "https://accounts.provider.example",
                    "client_id": "kubernetes",
                    "username_claim": "sub",
                    "username_prefix": "oidc:",
                    "groups_claim": "groups",
                    "groups_prefix": "oidc:",
                }
            },
        )

        task_id = result.tasks[0]
        while True:
            task = client.cloud.tasks.get(task_id)
            if task.state in ("FINISHED", "ERROR"):
                break
            time.sleep(5)

        print("OIDC configured:", task.state)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"
            "strconv"
            "time"

            gcore "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/cloud"
            "github.com/G-Core/gcore-go/option"
        )

        func main() {
            projectID, _ := strconv.ParseInt(os.Getenv("GCORE_CLOUD_PROJECT_ID"), 10, 64)
            regionID, _ := strconv.ParseInt(os.Getenv("GCORE_CLOUD_REGION_ID"), 10, 64)
            clusterName := os.Getenv("CLUSTER_NAME")

            client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))
            ctx := context.TODO()

            result, err := client.Cloud.K8S.Clusters.Update(ctx, clusterName, cloud.K8SClusterUpdateParams{
                ProjectID: gcore.Int(projectID),
                RegionID:  gcore.Int(regionID),
                Authentication: cloud.K8SClusterUpdateParamsAuthentication{
                    Oidc: cloud.K8SClusterUpdateParamsAuthenticationOidc{
                        IssuerURL:      gcore.String("https://accounts.provider.example"),
                        ClientID:       gcore.String("kubernetes"),
                        UsernameClaim:  gcore.String("sub"),
                        UsernamePrefix: gcore.String("oidc:"),
                        GroupsClaim:    gcore.String("groups"),
                        GroupsPrefix:   gcore.String("oidc:"),
                    },
                },
            })
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }

            taskID := result.Tasks[0]
            for {
                task, _ := client.Cloud.Tasks.Get(ctx, taskID)
                if task.State == "FINISHED" || task.State == "ERROR" {
                    fmt.Println("OIDC configured:", task.State)
                    break
                }
                time.Sleep(5 * time.Second)
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X PATCH "https://api.gcore.com/cloud/v2/k8s/clusters/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$CLUSTER_NAME" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "authentication": {
              "oidc": {
                "issuer_url": "https://accounts.provider.example",
                "client_id": "kubernetes",
                "username_claim": "sub",
                "username_prefix": "oidc:",
                "groups_claim": "groups",
                "groups_prefix": "oidc:"
              }
            }
          }'
        ```

        Response:

        ```json theme={null}
        {"tasks": ["fe5b3025-a9ce-4848-a94a-b6cbd6c0eae2"]}
        ```
      </Tab>
    </Tabs>

    <p>The API returns a task ID. Poll <code>GET /cloud/v1/tasks/{task_id}</code> every five seconds until `state` is `FINISHED`.</p>

    ## Remove OIDC

    <p>Set `authentication.oidc` to `null` to remove the OIDC configuration and return to kubeconfig-based authentication.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        import time
        import gcore

        client = gcore.Gcore(api_key=os.environ["GCORE_API_KEY"])
        project_id = int(os.environ["GCORE_CLOUD_PROJECT_ID"])
        region_id = int(os.environ["GCORE_CLOUD_REGION_ID"])
        cluster_name = os.environ["CLUSTER_NAME"]

        result = client.cloud.k8s.clusters.update(
            cluster_name=cluster_name,
            project_id=project_id,
            region_id=region_id,
            authentication={"oidc": None},
        )

        task_id = result.tasks[0]
        while True:
            task = client.cloud.tasks.get(task_id)
            if task.state in ("FINISHED", "ERROR"):
                break
            time.sleep(5)

        print("OIDC removed:", task.state)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "bytes"
            "encoding/json"
            "fmt"
            "net/http"
            "os"
            "time"
        )

        func main() {
            projectID := os.Getenv("GCORE_CLOUD_PROJECT_ID")
            regionID := os.Getenv("GCORE_CLOUD_REGION_ID")
            clusterName := os.Getenv("CLUSTER_NAME")
            apiKey := os.Getenv("GCORE_API_KEY")

            // The Go SDK typed API omits null object fields; use net/http to send oidc: null.
            url := fmt.Sprintf("https://api.gcore.com/cloud/v2/k8s/clusters/%s/%s/%s", projectID, regionID, clusterName)
            req, _ := http.NewRequest(http.MethodPatch, url, bytes.NewReader([]byte(`{"authentication": {"oidc": null}}`)))
            req.Header.Set("Authorization", "APIKey "+apiKey)
            req.Header.Set("Content-Type", "application/json")
            resp, err := http.DefaultClient.Do(req)
            if err != nil {
                fmt.Fprintln(os.Stderr, err)
                os.Exit(1)
            }
            defer resp.Body.Close()

            var result struct {
                Tasks []string `json:"tasks"`
            }
            json.NewDecoder(resp.Body).Decode(&result)
            taskID := result.Tasks[0]
            fmt.Println("Task ID:", taskID)

            // Poll the task
            for {
                r, _ := http.NewRequest(http.MethodGet, "https://api.gcore.com/cloud/v1/tasks/"+taskID, nil)
                r.Header.Set("Authorization", "APIKey "+apiKey)
                res, _ := http.DefaultClient.Do(r)
                var t struct{ State string `json:"state"` }
                json.NewDecoder(res.Body).Decode(&t)
                res.Body.Close()
                if t.State == "FINISHED" || t.State == "ERROR" {
                    fmt.Println("OIDC removed:", t.State)
                    break
                }
                time.Sleep(5 * time.Second)
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X PATCH "https://api.gcore.com/cloud/v2/k8s/clusters/$GCORE_CLOUD_PROJECT_ID/$GCORE_CLOUD_REGION_ID/$CLUSTER_NAME" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"authentication": {"oidc": null}}'
        ```

        Response:

        ```json theme={null}
        {"tasks": ["task-uuid"]}
        ```
      </Tab>
    </Tabs>

    <p>The API returns a task ID. Poll <code>GET /cloud/v1/tasks/{task_id}</code> every five seconds until `state` is `FINISHED`.</p>
  </MethodSection>
</MethodSwitch>
