Skip to content

Commit

Permalink
fix(keycloak): remove code duplication
Browse files Browse the repository at this point in the history
Signed-off-by: Oleksandr Andriienko <[email protected]>
  • Loading branch information
AndrienkoAleksandr committed Dec 22, 2024
1 parent e584fb5 commit 4f44a5c
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import KeycloakAdminClient from '@keycloak/keycloak-admin-client';
import { KeycloakProviderConfig } from './config';
import { Credentials } from '@keycloak/keycloak-admin-client/lib/utils/auth';
import { InputError } from '@backstage/errors';

// update token with refresh token.
export async function ensureTokenValid(
kcAdminClient: KeycloakAdminClient,
provider: KeycloakProviderConfig,
) {
if (!kcAdminClient.accessToken) {
await authenticate(kcAdminClient, provider);
} else {
const tokenData = JSON.parse(
Buffer.from(kcAdminClient.accessToken.split('.')[1], 'base64').toString(),
);

const tokenExpiry = tokenData.exp * 1000; // Convert to milliseconds
const now = Date.now();

// update token with refresh token.
if (now > tokenExpiry - 30000) {
await authenticate(kcAdminClient, provider);
}
}
}

export async function authenticate(
kcAdminClient: KeycloakAdminClient,
provider: KeycloakProviderConfig,
) {
try {
let credentials: Credentials;
if (provider.username && provider.password) {
credentials = {
grantType: 'password',
clientId: provider.clientId ?? 'admin-cli',
username: provider.username,
password: provider.password,
};
} else if (provider.clientId && provider.clientSecret) {
credentials = {
grantType: 'client_credentials',
clientId: provider.clientId,
clientSecret: provider.clientSecret,
};
} else {
throw new InputError(
`username and password or clientId and clientSecret must be provided.`,
);
}
await kcAdminClient.auth(credentials);
console.log('Authenticated successfully');
} catch (error) {
console.error('Failed to authenticate', error);
throw error;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ import {
UserRepresentationWithEntity,
UserTransformer,
} from './types';
import { Credentials } from '@keycloak/keycloak-admin-client/lib/utils/auth';
import { InputError } from '@backstage/errors';
import { ensureTokenValid } from './authenticate';

export const parseGroup = async (
keycloakGroup: GroupRepresentationWithParent,
Expand Down Expand Up @@ -223,60 +222,6 @@ export function* traverseGroups(
}
}

// update token with refresh token.
async function ensureTokenValid(
kcAdminClient: KeycloakAdminClient,
provider: KeycloakProviderConfig,
) {
if (!kcAdminClient.accessToken) {
await authenticate(kcAdminClient, provider);
} else {
const tokenData = JSON.parse(
Buffer.from(kcAdminClient.accessToken.split('.')[1], 'base64').toString(),
);

const tokenExpiry = tokenData.exp * 1000; // Convert to milliseconds
const now = Date.now();

// update token with refresh token.
if (now > tokenExpiry - 30000) {
await authenticate(kcAdminClient, provider);
}
}
}

async function authenticate(
kcAdminClient: KeycloakAdminClient,
provider: KeycloakProviderConfig,
) {
try {
let credentials: Credentials;
if (provider.username && provider.password) {
credentials = {
grantType: 'password',
clientId: provider.clientId ?? 'admin-cli',
username: provider.username,
password: provider.password,
};
} else if (provider.clientId && provider.clientSecret) {
credentials = {
grantType: 'client_credentials',
clientId: provider.clientId,
clientSecret: provider.clientSecret,
};
} else {
throw new InputError(
`username and password or clientId and clientSecret must be provided.`,
);
}
await kcAdminClient.auth(credentials);
console.log('Authenticated successfully');
} catch (error) {
console.error('Failed to authenticate', error);
throw error;
}
}

export const readKeycloakRealm = async (
client: KeycloakAdminClient,
config: KeycloakProviderConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import type {
EntityProviderConnection,
} from '@backstage/plugin-catalog-node';

import type { Credentials } from '@keycloak/keycloak-admin-client/lib/utils/auth';
// @ts-ignore
import inclusion from 'inclusion';
import { merge } from 'lodash';
Expand All @@ -46,6 +45,7 @@ import {
} from '../lib';
import { readProviderConfigs } from '../lib/config';
import { readKeycloakRealm } from '../lib/read';
import { authenticate } from '../lib/authenticate';

/**
* Options for {@link KeycloakOrgEntityProvider}.
Expand Down Expand Up @@ -222,29 +222,7 @@ export class KeycloakOrgEntityProvider implements EntityProvider {
baseUrl: provider.baseUrl,
realmName: provider.loginRealm,
});

let credentials: Credentials;

if (provider.username && provider.password) {
credentials = {
grantType: 'password',
clientId: provider.clientId ?? 'admin-cli',
username: provider.username,
password: provider.password,
};
} else if (provider.clientId && provider.clientSecret) {
credentials = {
grantType: 'client_credentials',
clientId: provider.clientId,
clientSecret: provider.clientSecret,
};
} else {
throw new InputError(
`username and password or clientId and clientSecret must be provided.`,
);
}

await kcAdminClient.auth(credentials);
await authenticate(kcAdminClient, provider);

const pLimitCJSModule = await inclusion('p-limit');
const limitFunc = pLimitCJSModule.default;
Expand Down

0 comments on commit 4f44a5c

Please sign in to comment.