diff --git a/packages/ldap-auth-backend/NEW-BACKEND.md b/packages/ldap-auth-backend/NEW-BACKEND.md
new file mode 100644
index 00000000..fd7fedf8
--- /dev/null
+++ b/packages/ldap-auth-backend/NEW-BACKEND.md
@@ -0,0 +1,313 @@
+
+
+
+@immobiliarelabs/backstage-plugin-ldap-auth-backend
+
+![npm (scoped)](https://img.shields.io/npm/v/@immobiliarelabs/backstage-plugin-ldap-auth-backend?style=flat-square)
+[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier?style=flat-square)
+[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release)
+![license](https://img.shields.io/github/license/immobiliare/backstage-plugin-ldap-auth?style=flat-square)
+
+> LDAP Authentication your [Backstage](https://backstage.io/) deployment
+
+This package is the Backend Provider to add LDAP authentication to your Backstage instance!
+
+- Customizable: Authentication request format and marshaling of the response can be injected with custom ones;
+- Works on simple stand-alone process or scaled infrastracture spanning multiple deployments using the shared PostgreSQL instance that Backstage already uses;
+
+This plugin is not meant to be used alone but in pair with:
+
+- The official [@backstage/plugin-catalog-backend-module-ldap](https://www.npmjs.com/package/@backstage/plugin-catalog-backend-module-ldap) which keeps in sync your LDAP users with Backstage user catalogs!
+- Its sibling frontend package [@immobiliarelabs/backstage-plugin-ldap-auth](https://www.npmjs.com/package/@immobiliarelabs/backstage-plugin-ldap-auth)
+
+All the current LTS versions are supported.
+
+## Table of Content
+
+
+
+- [Installation](#installation)
+- [Configurations](#configurations)
+ - [Setup](#setup)
+ - [Connection Configuration](#connection-configuration)
+ - [Add the authentication backend plugin](#add-the-authentication-backend-plugin)
+ - [Custom LDAP Configurations](#custom-ldap-configurations)
+ - [Custom authentication function](#custom-authentication-function)
+ - [Custom check if user exists](#custom-check-if-user-exists)
+ - [Add the login form](#add-the-login-form)
+- [Powered Apps](#powered-apps)
+- [Support & Contribute](#support--contribute)
+- [License](#license)
+
+
+
+## Installation
+
+> These packages are available on npm.
+
+You can install them in your backstage installation using `yarn workspace`
+
+```bash
+# install yarn if you don't have it
+$ npm install -g yarn
+# install backend plugin
+$ yarn workspace backend add @immobiliarelabs/backstage-plugin-ldap-auth-backend
+# install frontend plugin
+$ yarn workspace app add @immobiliarelabs/backstage-plugin-ldap-auth
+```
+
+## Configurations
+
+> This documentation assumes that you have already scaffolded your Backstage instance from the official `@backstage/create-app`, all files that we're going to customize here are the one already created by the CLI!
+
+### Setup
+
+If you didn't have already, you need to configure Backstage's official LDAP plugin, that is needed to import and keep in syncs users your LDAP users.
+
+```sh
+# in your backstage repo
+yarn add @backstage/plugin-catalog-backend-module-ldap
+```
+
+> `packages/backend/src/plugins/catalog.ts`
+
+```ts
+import type { Router } from 'express';
+import type { PluginEnvironment } from '../types';
+
+import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
+import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend';
+import {
+ LdapOrgEntityProvider,
+} from '@backstage/plugin-catalog-backend-module-ldap';
+
+export default async function createPlugin(
+ env: PluginEnvironment,
+): Promise {
+ const builder = await CatalogBuilder.create(env);
+
+ builder.addEntityProvider(
+ LdapOrgEntityProvider.fromConfig(env.config, {
+ id: '',
+ target: 'ldaps://',
+ logger: env.logger,
+ schedule: env.scheduler.createScheduledTaskRunner({
+ frequency: // whatever
+ timeout: // whatever
+ }),
+ }),
+ );
+
+ builder.addProcessor(new ScaffolderEntitiesProcessor());
+ const { processingEngine, router } = await builder.build();
+ await processingEngine.start();
+ return router;
+}
+```
+
+### Connection Configuration
+
+> Adds connection configuration inside your backstage YAML config file, eg: `app-config.yaml`
+
+We use [`ldap-authentication`](https://github.com/shaozi/ldap-authentication) for authentication, you can find all the configurations at this [link], `ldapOpts` fields are options provided to lower level ldap client read more at [`ldapjs` ](https://github.com/ldapjs/node-ldapjs)
+
+> Add in you You backstage configuration file
+
+```yml
+auth:
+ environment: { ENV_NAME } # eg: production|staging|review|develop
+ providers:
+ ldap:
+ # eg: production|staging|review|develop
+ { ENV_NAME }:
+ cookies:
+ secure: false # https cookies or not
+ field: 'backstage-token' # default
+
+ ldapAuthenticationOptions:
+ userSearchBase: 'ou=users,dc=ns,dc=farm' # REQUIRED
+ # what is the user unique key in your ldap instance
+ usernameAttribute: 'uid' # defaults to `uid`
+ # directory where to search user
+ # default search will be `[userSearchBase]=[username],[userSearchBase]`
+
+ # User able to list other users, this is used
+ # to check incoming JWT if user are already part of the LDAP
+ # NOTE: If no admin user/pass provided we'll attempt a credential-less search
+ adminDn: uid={ADMIN_USERNAME},ou=users,dc=ns,dc=farm
+ adminPassword: ''
+
+ ldapOpts:
+ url:
+ - 'ldaps://123.123.123.123'
+ tlsOptions:
+ rejectUnauthorized: false
+```
+
+### Add the authentication backend plugin
+
+This is for a basic usage: - single process - No custom auth or user object marshaling - in-memory sessions
+
+For more uses cases you can see the [example folders](https://github.com/immobiliare/backstage-plugin-ldap-auth/tree/main/examples)
+
+> `packages/backend/src/plugins/auth.ts`
+
+```ts
+const backend = createBackend();
+...
+backend.add(import('@immobiliarelabs/backstage-plugin-ldap-auth-backend'));
+...
+backend.start();
+
+```
+
+If you want to connect to Postgres for the store of the token (default is in memory):
+
+```ts
+// index.ts
+import { tokenValidatorFactory } from '@immobiliarelabs/backstage-plugin-ldap-auth-backend';
+...
+backend.add(import('@immobiliarelabs/backstage-plugin-ldap-auth-backend'));
+backend.add(tokenValidatorFactory({ createTokenValidator }));
+...
+backend.start();
+
+
+// auth.ts
+createTokenValidator(config: Config): TokenValidator {
+ ...
+ const url = `postgresql://${user}:${password}@${host}:${port}/mydb`;
+ return new JWTTokenValidator(
+ new Keyv(url, { table: 'token' })
+ );
+}
+
+```
+
+### Custom LDAP Configurations
+
+If your LDAP server connection options requires more fine tune than we handle here you can inject your custom auth function, take a look at `ldap.create` types at `resolvers.ldapAuthentication`, you can copy the default function and change what you need!
+
+This can be also done for the `resolvers.checkUserExists` function, which runs when controlling a JWT token.
+
+#### Custom authentication function
+
+```ts
+import {
+ coreServices,
+ createBackendModule,
+} from '@backstage/backend-plugin-api';
+import { ldapAuthExtensionPoint } from '@immobiliarelabs/backstage-plugin-ldap-auth-backend';
+
+export default createBackendModule({
+ pluginId: 'auth',
+ moduleId: 'ldap-ext',
+ register(reg) {
+ reg.registerInit({
+ deps: {
+ config: coreServices.rootConfig,
+ ldapAuth: ldapAuthExtensionPoint,
+ },
+ async init({ config, ldapAuth }) {
+ ldapAuth.set({
+ resolvers: {
+ async ldapAuthentication(
+ username,
+ password,
+ ldapOptions,
+ authFunction
+ ): LDAPUser {
+ // modify your ldapOptions and do whatever you need to format it
+ // ...
+ const user = await authFunction(ldapOptions);
+ return { uid: user.uid };
+ },
+ },
+ });
+ },
+ });
+ },
+});
+```
+
+#### Custom check if user exists
+
+```ts
+export default createBackendModule({
+ pluginId: 'auth',
+ moduleId: 'ldap-ext',
+ register(reg) {
+ reg.registerInit({
+ deps: {
+ config: coreServices.rootConfig,
+ ldapAuth: ldapAuthExtensionPoint,
+ },
+ async init({ config, ldapAuth }) {
+ ldapAuth.set({
+ resolvers: {
+ async checkUserExists(
+ ldapAuthOptions,
+ searchFunction
+ ): Promise {
+ const { username } = ldapAuthOptions;
+
+ // Do you custom checks
+ // ....
+
+ return true;
+ },
+ },
+ });
+ },
+ });
+ },
+});
+```
+
+### Add the login form
+
+> More on this in the frontend plugin documentation [here](../ldap-auth/README.md)
+
+We need to replace the existing Backstage demo authentication page with our custom one!
+
+In the `App.tsx` file, change the `createApp` function adding a `components` with our custom `SignInPage`In the `App.tsx` file change the `createApp` function to provide use our custom `SignInPage` in the `components` key.
+
+**Note:** This components isn't only UI, it also brings all the token state management and HTTP API calls to the backstage auth routes we already configured in the backend part.
+
+> `packages/app/src/App.tsx`
+
+```tsx
+import { LdapAuthFrontendPage } from '@immobiliarelabs/backstage-plugin-ldap-auth';
+
+const app = createApp({
+ // ...
+ components: {
+ SignInPage: (props) => (
+
+ ),
+ },
+ // ...
+});
+```
+
+And you're ready to go! If you need more use cases, like having multiple processes and need a shared token store instead of in-memory look at the [example folders](https://github.com/immobiliare/backstage-plugin-ldap-auth/examples/)
+
+## Powered Apps
+
+Backstage Plugin LDAP Auth was created by the amazing Node.js team at [ImmobiliareLabs](http://labs.immobiliare.it/), the Tech dept of [Immobiliare.it](https://www.immobiliare.it), the #1 real estate company in Italy.
+
+We are currently using Backstage Plugin LDAP Auth in our products as well as our internal toolings.
+
+**If you are using Backstage Plugin LDAP Auth in production [drop us a message](mailto:opensource@immobiliare.it)**.
+
+## Support & Contribute
+
+Made with ❤️ by [ImmobiliareLabs](https://github.com/immobiliare) & [Contributors](https://github.com/immobiliare/backstage-plugin-ldap-auth/CONTRIBUTING.md#contributors)
+
+We'd love for you to contribute to Backstage Plugin LDAP Auth!
+If you have any questions on how to use Backstage Plugin LDAP Auth, bugs and enhancement please feel free to reach out by opening a [GitHub Issue](https://github.com/immobiliare/backstage-plugin-ldap-auth).
+
+## License
+
+Backstage Plugin LDAP Auth is licensed under the MIT license.
+See the [LICENSE](https://github.com/immobiliare/backstage-plugin-ldap-auth/LICENSE) file for more information.
diff --git a/packages/ldap-auth-backend/src/alpha.ts b/packages/ldap-auth-backend/src/alpha.ts
index 31cda546..08a07d6f 100644
--- a/packages/ldap-auth-backend/src/alpha.ts
+++ b/packages/ldap-auth-backend/src/alpha.ts
@@ -9,14 +9,19 @@ import {
import { authProvidersExtensionPoint } from '@backstage/plugin-auth-node';
import { JWTTokenValidator, TokenValidator } from './jwt';
import { ldap } from './provider';
-import { LDAPResponse } from './types';
+import {
+ LDAPResponse,
+ ProviderCreateOptions,
+ Resolvers,
+ SignInResolver,
+} from './types';
import { AuthHandler } from '@backstage/plugin-auth-backend';
import Keyv from 'keyv';
-interface AuthHandlerSetter {
- set(handler: AuthHandler>): void;
+interface LdapAuthSetter {
+ set(handler: ProviderCreateOptions): void;
}
-export const ldapAuthExtensionPoint = createExtensionPoint({
+export const ldapAuthExtensionPoint = createExtensionPoint({
id: 'ldap-auth-extension',
});
@@ -57,14 +62,21 @@ export default createBackendModule({
moduleId: 'ldap',
register(reg) {
let authHandler: AuthHandler> | undefined;
- const authHandlerSetter = {
- set(handler: AuthHandler>) {
- authHandler = handler;
+ let resolvers: Resolvers | undefined;
+ let signInResolver: SignInResolver | undefined;
+ let tokenValidatorExt: TokenValidator | undefined;
+
+ const ldapAuthSetter = {
+ set(opt: ProviderCreateOptions) {
+ authHandler = opt.authHandler;
+ resolvers = opt.resolvers;
+ signInResolver = opt.signIn;
+ tokenValidatorExt = opt.tokenValidator;
},
};
- reg.registerExtensionPoint(
+ reg.registerExtensionPoint(
ldapAuthExtensionPoint,
- authHandlerSetter
+ ldapAuthSetter
);
reg.registerInit({
@@ -76,8 +88,10 @@ export default createBackendModule({
providers.registerProvider({
providerId: 'ldap',
factory: ldap.create({
- tokenValidator,
+ tokenValidator: tokenValidatorExt || tokenValidator,
authHandler,
+ resolvers,
+ signIn: signInResolver,
}),
});
},
diff --git a/packages/ldap-auth-backend/src/types.ts b/packages/ldap-auth-backend/src/types.ts
index 60925643..63ef3f46 100644
--- a/packages/ldap-auth-backend/src/types.ts
+++ b/packages/ldap-auth-backend/src/types.ts
@@ -109,19 +109,23 @@ export type BackstageLdapAuthConfiguration = {
ldapAuthenticationOptions: AuthenticationOptions;
};
+export type Resolvers = {
+ checkUserExists?: typeof defaultCheckUserExists;
+ ldapAuthentication?: typeof defaultLDAPAuthentication;
+};
+
+export type SignInResolver = {
+ resolver?: typeof defaultSigninResolver;
+};
+
export type ProviderCreateOptions = {
// Backstage Provider AuthHandler
authHandler?: typeof defaultAuthHandler;
// Backstage Provider SignInResolver
- signIn?: {
- resolver?: typeof defaultSigninResolver;
- };
+ signIn?: SignInResolver;
// Custom resolvers
- resolvers?: {
- checkUserExists?: typeof defaultCheckUserExists;
- ldapAuthentication?: typeof defaultLDAPAuthentication;
- };
+ resolvers?: Resolvers;
// Custom validator function for the JWT token if needed
tokenValidator?: TokenValidator;
};