Skip to content
This repository was archived by the owner on Jun 18, 2025. It is now read-only.

Commit 3fd56ba

Browse files
feat: add support for okta authentication (#188)
This change adds support for using Okta authentication.
1 parent f5d16d6 commit 3fd56ba

File tree

5 files changed

+110
-18
lines changed

5 files changed

+110
-18
lines changed

plugins/cad-backend/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ configAsData:
6060
# Determines how the client will authenticate with the Kubernetes cluster.
6161
authProvider: current-context
6262

63-
# The service account token to be used when using the 'service-account' auth provider.
63+
# Optional. Determines the OIDC token provider to use when using the 'oidc' auth provider.
64+
oidcTokenProvider: okta
65+
66+
# Optional. The service account token to be used when using the 'service-account' auth provider.
6467
serviceAccountToken: ${CAD_SERVICE_ACCOUNT_TOKEN}
6568
```
6669
@@ -83,8 +86,17 @@ Valid values:
8386
| ------ | ----------- |
8487
| current-context | Authenticate to the cluster with the user in the kubeconfig current context |
8588
| google | Authenticate to the cluster using the user's authentication token from the [Google auth plugin](https://backstage.io/docs/auth/) |
89+
| oidc | Authenticate to the cluster using OIDC (OpenID Connect) |
8690
| service-account | Authenticate to the cluster using a Kubernetes service account token |
8791
92+
`clusterLocatorMethod.oidcTokenProvider` determines which configured [Backstage auth provider](https://backstage.io/docs/auth/) to
93+
use to authenticate to the cluster with. This field is required with the `oidc` auth provider.
94+
95+
Valid values:
96+
| Values | Description |
97+
| ------ | ----------- |
98+
| okta | Authenticate to the cluster with the [Okta Backstage auth provider](https://backstage.io/docs/auth/okta/provider) |
99+
88100
`clusterLocatorMethod.serviceAccountToken` defines the service account token to be used with the `service-account` auth provider. You can get the service account token with the following command:
89101
90102
```bash

plugins/cad-backend/src/service/config.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@ export enum ClusterLocatorMethodType {
2424
export enum ClusterLocatorAuthProvider {
2525
CURRENT_CONTEXT = 'current-context',
2626
GOOGLE = 'google',
27+
OIDC = 'oidc',
2728
SERVICE_ACCOUNT = 'service-account',
2829
}
2930

31+
export enum OIDCTokenProvider {
32+
NONE = 'none',
33+
OKTA = 'okta',
34+
}
35+
3036
export const getClusterLocatorMethodType = (
3137
config: Config,
3238
): ClusterLocatorMethodType => {
@@ -53,6 +59,28 @@ export const getClusterLocatorMethodAuthProvider = (
5359
return authProvider as ClusterLocatorAuthProvider;
5460
};
5561

62+
export const getClusterLocatorMethodOIDCTokenProvider = (
63+
config: Config,
64+
): OIDCTokenProvider => {
65+
const authProvider = getClusterLocatorMethodAuthProvider(config);
66+
67+
if (authProvider === ClusterLocatorAuthProvider.OIDC) {
68+
const oidcTokenProvider = config.getString(
69+
'clusterLocatorMethod.oidcTokenProvider',
70+
);
71+
72+
if (!Object.values(OIDCTokenProvider)) {
73+
throw new Error(
74+
`Unknown clusterLocatorMethod.oidcTokenProvider, ${oidcTokenProvider}`,
75+
);
76+
}
77+
78+
return oidcTokenProvider as OIDCTokenProvider;
79+
}
80+
81+
return OIDCTokenProvider.NONE;
82+
};
83+
5684
export const getClusterLocatorMethodServiceAccountToken = (
5785
config: Config,
5886
): string => {

plugins/cad-backend/src/service/router.ts

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import { Logger } from 'winston';
2323
import {
2424
ClusterLocatorAuthProvider,
2525
getClusterLocatorMethodAuthProvider,
26+
getClusterLocatorMethodOIDCTokenProvider,
2627
getClusterLocatorMethodServiceAccountToken,
2728
getClusterLocatorMethodType,
29+
OIDCTokenProvider,
2830
} from './config';
2931
import { getKubernetesConfig } from './lib';
3032

@@ -33,6 +35,36 @@ export interface RouterOptions {
3335
logger: Logger;
3436
}
3537

38+
const getClientAuthentication = (
39+
authProvider: ClusterLocatorAuthProvider,
40+
oidcTokenProvider: OIDCTokenProvider,
41+
): string => {
42+
switch (authProvider) {
43+
case ClusterLocatorAuthProvider.GOOGLE:
44+
return 'google';
45+
46+
case ClusterLocatorAuthProvider.OIDC:
47+
switch (oidcTokenProvider) {
48+
case OIDCTokenProvider.OKTA:
49+
return 'oidc.okta';
50+
51+
default:
52+
throw new Error(
53+
`Client authenticaiton cannot be determined for OIDC token provider ${oidcTokenProvider}`,
54+
);
55+
}
56+
57+
case ClusterLocatorAuthProvider.SERVICE_ACCOUNT:
58+
case ClusterLocatorAuthProvider.CURRENT_CONTEXT:
59+
return 'none';
60+
61+
default:
62+
throw new Error(
63+
`Client authenticaiton cannot be determined for auth provider ${authProvider}`,
64+
);
65+
}
66+
};
67+
3668
export async function createRouter({
3769
config,
3870
logger,
@@ -45,6 +77,7 @@ export async function createRouter({
4577
const clusterLocatorMethodType = getClusterLocatorMethodType(cadConfig);
4678
const clusterLocatorMethodAuthProvider =
4779
getClusterLocatorMethodAuthProvider(cadConfig);
80+
const oidcTokenProvider = getClusterLocatorMethodOIDCTokenProvider(cadConfig);
4881

4982
const kubeConfig = getKubernetesConfig(clusterLocatorMethodType);
5083
const currentCluster = kubeConfig.getCurrentCluster();
@@ -56,6 +89,13 @@ export async function createRouter({
5689
const serviceAccountToken =
5790
getClusterLocatorMethodServiceAccountToken(cadConfig);
5891

92+
const clientAuthentication = getClientAuthentication(
93+
clusterLocatorMethodAuthProvider,
94+
oidcTokenProvider,
95+
);
96+
97+
logger.info(`Using '${clientAuthentication}' for client authentication`);
98+
5999
const k8sApiServerUrl = currentCluster.server;
60100

61101
const healthCheck = (
@@ -69,13 +109,8 @@ export async function createRouter({
69109
_: express.Request,
70110
response: express.Response,
71111
): void => {
72-
const authentication =
73-
clusterLocatorMethodAuthProvider === ClusterLocatorAuthProvider.GOOGLE
74-
? 'google'
75-
: 'none';
76-
77112
response.send({
78-
authentication,
113+
authentication: clientAuthentication,
79114
});
80115
};
81116

@@ -114,10 +149,7 @@ export async function createRouter({
114149

115150
kubeConfig.applyToRequest(requestOptions);
116151

117-
const endUserProviders = [ClusterLocatorAuthProvider.GOOGLE];
118-
const useEndUserAuthz = endUserProviders.includes(
119-
clusterLocatorMethodAuthProvider,
120-
);
152+
const useEndUserAuthz = clientAuthentication !== 'none';
121153
if (useEndUserAuthz) {
122154
requestOptions.headers = requestOptions.headers ?? {};
123155
requestOptions.headers.authorization = request.headers.authorization;

plugins/cad/src/apis/PorchRestApi.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { DiscoveryApi, FetchApi, OAuthApi } from '@backstage/core-plugin-api';
17+
import {
18+
DiscoveryApi,
19+
FetchApi,
20+
OAuthApi,
21+
OpenIdConnectApi,
22+
} from '@backstage/core-plugin-api';
1823
import { ConfigAsDataApi } from '.';
1924
import { ListApiGroups } from '../types/ApiGroup';
2025
import { ListConfigManagements } from '../types/ConfigManagement';
@@ -77,18 +82,31 @@ export class PorchRestAPI implements ConfigAsDataApi {
7782
private discovery: DiscoveryApi,
7883
private fetchApi: FetchApi,
7984
private googleAuthApi: OAuthApi,
85+
private oktaAuthApi: OpenIdConnectApi,
8086
) {}
8187

8288
private async getAuthorizationToken(): Promise<string | undefined> {
83-
if (this.authentication === 'google') {
84-
const accessToken = await this.googleAuthApi.getAccessToken(
89+
const authProvider = this.authentication;
90+
91+
if (authProvider === 'google') {
92+
const googleAccessToken = await this.googleAuthApi.getAccessToken(
8593
'https://www.googleapis.com/auth/cloud-platform.read-only',
8694
);
8795

88-
return `Bearer ${accessToken}`;
96+
return `Bearer ${googleAccessToken}`;
97+
}
98+
99+
if (authProvider === 'oidc.okta') {
100+
const oktaIdToken = await this.oktaAuthApi.getIdToken();
101+
102+
return `Bearer ${oktaIdToken}`;
103+
}
104+
105+
if (authProvider === 'none') {
106+
return undefined;
89107
}
90108

91-
return undefined;
109+
throw new Error(`Authentication provider ${authProvider} not found`);
92110
}
93111

94112
private async cadFetch(path: string, init?: RequestInit): Promise<any> {

plugins/cad/src/plugin.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
discoveryApiRef,
2222
fetchApiRef,
2323
googleAuthApiRef,
24+
oktaAuthApiRef,
2425
} from '@backstage/core-plugin-api';
2526
import { configAsDataApiRef, PorchRestAPI } from './apis';
2627
import { rootRouteRef } from './routes';
@@ -37,9 +38,10 @@ export const cadPlugin = createPlugin({
3738
discoveryApi: discoveryApiRef,
3839
fetchApi: fetchApiRef,
3940
googleAuthApi: googleAuthApiRef,
41+
oktaAuthApi: oktaAuthApiRef,
4042
},
41-
factory: ({ discoveryApi, fetchApi, googleAuthApi }) =>
42-
new PorchRestAPI(discoveryApi, fetchApi, googleAuthApi),
43+
factory: ({ discoveryApi, fetchApi, googleAuthApi, oktaAuthApi }) =>
44+
new PorchRestAPI(discoveryApi, fetchApi, googleAuthApi, oktaAuthApi),
4345
}),
4446
],
4547
});

0 commit comments

Comments
 (0)