public async Task TestOidcAuth() { var clientId = "CLIENT_ID"; var clientSecret = "CLIENT_SECRET"; var idpIssuerUrl = "https://idp.issuer.url"; var unexpiredIdToken = "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjAsImV4cCI6MjAwMDAwMDAwMH0.8Ata5uKlrqYfeIaMwS91xVgVFHu7ntHx1sGN95i2Zho"; var expiredIdToken = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjB9.f37LFpIw_XIS5TZt3wdtEjjyCNshYy03lOWpyDViRM0"; var refreshToken = "REFRESH_TOKEN"; // use unexpired id token as bearer, do not attempt to refresh var auth = new OidcTokenProvider(clientId, clientSecret, idpIssuerUrl, unexpiredIdToken, refreshToken); var result = await auth.GetAuthenticationHeaderAsync(CancellationToken.None).ConfigureAwait(false); result.Scheme.Should().Be("Bearer"); result.Parameter.Should().Be(unexpiredIdToken); try { // attempt to refresh id token when expired auth = new OidcTokenProvider(clientId, clientSecret, idpIssuerUrl, expiredIdToken, refreshToken); result = await auth.GetAuthenticationHeaderAsync(CancellationToken.None).ConfigureAwait(false); result.Scheme.Should().Be("Bearer"); result.Parameter.Should().Be(expiredIdToken); Assert.True(false, "should not be here"); } catch (KubernetesClientException e) { Assert.StartsWith("Unable to refresh OIDC token.", e.Message); } try { // attempt to refresh id token when null auth = new OidcTokenProvider(clientId, clientSecret, idpIssuerUrl, null, refreshToken); result = await auth.GetAuthenticationHeaderAsync(CancellationToken.None).ConfigureAwait(false); result.Scheme.Should().Be("Bearer"); result.Parameter.Should().Be(expiredIdToken); Assert.True(false, "should not be here"); } catch (KubernetesClientException e) { Assert.StartsWith("Unable to refresh OIDC token.", e.Message); } }
private void SetUserDetails(K8SConfiguration k8SConfig, Context activeContext) { if (string.IsNullOrWhiteSpace(activeContext.ContextDetails.User)) { return; } var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals( activeContext.ContextDetails.User, StringComparison.OrdinalIgnoreCase)); if (userDetails == null) { throw new KubeConfigException($"User not found for context {activeContext.Name} in kubeconfig"); } if (userDetails.UserCredentials == null) { throw new KubeConfigException($"User credentials not found for user: {userDetails.Name} in kubeconfig"); } var userCredentialsFound = false; // Basic and bearer tokens are mutually exclusive if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Token)) { AccessToken = userDetails.UserCredentials.Token; userCredentialsFound = true; } else if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.UserName) && !string.IsNullOrWhiteSpace(userDetails.UserCredentials.Password)) { Username = userDetails.UserCredentials.UserName; Password = userDetails.UserCredentials.Password; userCredentialsFound = true; } // Token and cert based auth can co-exist if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificateData) && !string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKeyData)) { ClientCertificateData = userDetails.UserCredentials.ClientCertificateData; ClientCertificateKeyData = userDetails.UserCredentials.ClientKeyData; userCredentialsFound = true; } if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificate) && !string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKey)) { ClientCertificateFilePath = GetFullPath(k8SConfig, userDetails.UserCredentials.ClientCertificate); ClientKeyFilePath = GetFullPath(k8SConfig, userDetails.UserCredentials.ClientKey); userCredentialsFound = true; } if (userDetails.UserCredentials.AuthProvider != null) { if (userDetails.UserCredentials.AuthProvider.Config != null && (userDetails.UserCredentials.AuthProvider.Config.ContainsKey("access-token") || userDetails.UserCredentials.AuthProvider.Config.ContainsKey("id-token"))) { switch (userDetails.UserCredentials.AuthProvider.Name) { case "azure": { var config = userDetails.UserCredentials.AuthProvider.Config; if (config.ContainsKey("expires-on")) { var expiresOn = int.Parse(config["expires-on"]); DateTimeOffset expires; expires = DateTimeOffset.FromUnixTimeSeconds(expiresOn); if (DateTimeOffset.Compare( expires, DateTimeOffset.Now) <= 0) { var tenantId = config["tenant-id"]; var clientId = config["client-id"]; var apiServerId = config["apiserver-id"]; var refresh = config["refresh-token"]; var newToken = RenewAzureToken( tenantId, clientId, apiServerId, refresh); config["access-token"] = newToken; } } AccessToken = config["access-token"]; userCredentialsFound = true; break; } case "gcp": { // config var config = userDetails.UserCredentials.AuthProvider.Config; TokenProvider = new GcpTokenProvider(config["cmd-path"]); userCredentialsFound = true; break; } case "oidc": { var config = userDetails.UserCredentials.AuthProvider.Config; AccessToken = config["id-token"]; if (config.ContainsKey("client-id") && config.ContainsKey("idp-issuer-url") && config.ContainsKey("id-token") && config.ContainsKey("refresh-token")) { string clientId = config["client-id"]; string clientSecret = config.ContainsKey("client-secret") ? config["client-secret"] : null; string idpIssuerUrl = config["idp-issuer-url"]; string idToken = config["id-token"]; string refreshToken = config["refresh-token"]; TokenProvider = new OidcTokenProvider(clientId, clientSecret, idpIssuerUrl, idToken, refreshToken); userCredentialsFound = true; } break; } } } } if (userDetails.UserCredentials.ExternalExecution != null) { if (string.IsNullOrWhiteSpace(userDetails.UserCredentials.ExternalExecution.Command)) { throw new KubeConfigException( "External command execution to receive user credentials must include a command to execute"); } if (string.IsNullOrWhiteSpace(userDetails.UserCredentials.ExternalExecution.ApiVersion)) { throw new KubeConfigException("External command execution missing ApiVersion key"); } var(accessToken, clientCertificateData, clientCertificateKeyData) = ExecuteExternalCommand(userDetails.UserCredentials.ExternalExecution); AccessToken = accessToken; // When reading ClientCertificateData from a config file it will be base64 encoded, and code later in the system (see CertUtils.GeneratePfx) // expects ClientCertificateData and ClientCertificateKeyData to be base64 encoded because of this. However the string returned by external // auth providers is the raw certificate and key PEM text, so we need to take that and base64 encoded it here so it can be decoded later. ClientCertificateData = clientCertificateData == null ? null : Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(clientCertificateData)); ClientCertificateKeyData = clientCertificateKeyData == null ? null : Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(clientCertificateKeyData)); userCredentialsFound = true; } if (!userCredentialsFound) { throw new KubeConfigException( $"User: {userDetails.Name} does not have appropriate auth credentials in kubeconfig"); } }