nest-keycloak-connect
Advanced tools
Comparing version 1.9.5 to 1.10.0
@@ -19,3 +19,7 @@ import { LogLevel } from '@nestjs/common'; | ||
*/ | ||
realmSecretResolver?: (realm: string) => Promise<string> | string; | ||
realmSecretResolver?: (realm: string, request?: any) => Promise<string> | string; | ||
/** | ||
* The realm auth server url resolver function. | ||
*/ | ||
realmAuthServerUrlResolver?: (realm: string, request?: any) => Promise<string> | string; | ||
} | ||
@@ -22,0 +26,0 @@ /** |
@@ -68,4 +68,4 @@ "use strict"; | ||
provide: constants_1.KEYCLOAK_MULTITENANT_SERVICE, | ||
useClass: keycloak_multitenant_service_1.KeycloakMultiTenantService | ||
} | ||
useClass: keycloak_multitenant_service_1.KeycloakMultiTenantService, | ||
}, | ||
]; | ||
@@ -95,4 +95,4 @@ return { | ||
provide: constants_1.KEYCLOAK_MULTITENANT_SERVICE, | ||
useClass: keycloak_multitenant_service_1.KeycloakMultiTenantService | ||
} | ||
useClass: keycloak_multitenant_service_1.KeycloakMultiTenantService, | ||
}, | ||
]; | ||
@@ -99,0 +99,0 @@ if (options.useExisting || options.useFactory) { |
@@ -17,6 +17,8 @@ import KeycloakConnect from 'keycloak-connect'; | ||
* @param realm the realm to retrieve from | ||
* @param request the request instance, defaults to undefined | ||
* @returns the multi tenant keycloak instance | ||
*/ | ||
get(realm: string): Promise<KeycloakConnect.Keycloak>; | ||
resolveSecret(realm: string): Promise<string>; | ||
get(realm: string, request?: any): Promise<KeycloakConnect.Keycloak>; | ||
resolveAuthServerUrl(realm: string, request?: any): Promise<string>; | ||
resolveSecret(realm: string, request?: any): Promise<string>; | ||
} |
@@ -48,5 +48,6 @@ "use strict"; | ||
* @param realm the realm to retrieve from | ||
* @param request the request instance, defaults to undefined | ||
* @returns the multi tenant keycloak instance | ||
*/ | ||
get(realm) { | ||
get(realm, request = undefined) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -60,6 +61,10 @@ if (typeof this.keycloakOpts === 'string') { | ||
} | ||
const authServerUrl = yield this.resolveAuthServerUrl(realm, request); | ||
const secret = yield this.resolveSecret(realm, request); | ||
// Check if existing | ||
if (this.instances.has(realm)) { | ||
// If resolve always is enabled, resolve everything again | ||
if (this.keycloakOpts.multiTenant.resolveAlways) { | ||
const keycloak = this.instances.get(realm); | ||
const secret = yield this.resolveSecret(realm); | ||
keycloak.config.authServerUrl = authServerUrl; | ||
keycloak.config.secret = secret; | ||
@@ -71,9 +76,10 @@ keycloak.grantManager.secret = secret; | ||
} | ||
// Otherwise return the instance | ||
return this.instances.get(realm); | ||
} | ||
else { | ||
const secret = yield this.resolveSecret(realm); | ||
// TODO: Repeating code from provider, will need to rework this in 2.0 | ||
// Override realm and secret | ||
// Override realm, secret, and authServerUrl | ||
const keycloakOpts = Object.assign(this.keycloakOpts, { | ||
authServerUrl, | ||
realm, | ||
@@ -88,2 +94,3 @@ secret, | ||
}; | ||
// Save instance | ||
this.instances.set(realm, keycloak); | ||
@@ -94,3 +101,3 @@ return keycloak; | ||
} | ||
resolveSecret(realm) { | ||
resolveAuthServerUrl(realm, request = undefined) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -104,4 +111,38 @@ if (typeof this.keycloakOpts === 'string') { | ||
} | ||
// If no realm auth server url resolver is defined, return defaults | ||
if (!this.keycloakOpts.multiTenant.realmAuthServerUrlResolver) { | ||
return (this.keycloakOpts.authServerUrl || | ||
this.keycloakOpts['auth-server-url'] || | ||
this.keycloakOpts.serverUrl || | ||
this.keycloakOpts['server-url']); | ||
} | ||
// Resolve realm authServerUrl | ||
const resolvedAuthServerUrl = this.keycloakOpts.multiTenant.realmAuthServerUrlResolver(realm, request); | ||
const authServerUrl = resolvedAuthServerUrl || resolvedAuthServerUrl instanceof Promise | ||
? yield resolvedAuthServerUrl | ||
: resolvedAuthServerUrl; | ||
// Override auth server url | ||
// Order of priority: resolved realm auth server url > provided auth server url | ||
return (authServerUrl || | ||
this.keycloakOpts.authServerUrl || | ||
this.keycloakOpts['auth-server-url'] || | ||
this.keycloakOpts.serverUrl || | ||
this.keycloakOpts['server-url']); | ||
}); | ||
} | ||
resolveSecret(realm, request = undefined) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (typeof this.keycloakOpts === 'string') { | ||
throw new Error('Keycloak configuration is a configuration path. This should not happen after module load.'); | ||
} | ||
if (this.keycloakOpts.multiTenant === null || | ||
this.keycloakOpts.multiTenant === undefined) { | ||
throw new Error('Multi tenant is not defined yet multi tenant service is being called.'); | ||
} | ||
// If no realm secret resolver is defined, return defaults | ||
if (!this.keycloakOpts.multiTenant.realmSecretResolver) { | ||
return this.keycloakOpts.secret; | ||
} | ||
// Resolve realm secret | ||
const resolvedRealmSecret = this.keycloakOpts.multiTenant.realmSecretResolver(realm); | ||
const resolvedRealmSecret = this.keycloakOpts.multiTenant.realmSecretResolver(realm, request); | ||
const realmSecret = resolvedRealmSecret || resolvedRealmSecret instanceof Promise | ||
@@ -108,0 +149,0 @@ ? yield resolvedRealmSecret |
@@ -19,3 +19,3 @@ "use strict"; | ||
const realm = resolvedRealm instanceof Promise ? yield resolvedRealm : resolvedRealm; | ||
return yield multiTenant.get(realm); | ||
return yield multiTenant.get(realm, request); | ||
} | ||
@@ -25,3 +25,3 @@ else if (!opts.realm) { | ||
const issuerRealm = payload.iss.split('/').pop(); | ||
return yield multiTenant.get(issuerRealm); | ||
return yield multiTenant.get(issuerRealm, request); | ||
} | ||
@@ -28,0 +28,0 @@ return singleTenant; |
{ | ||
"name": "nest-keycloak-connect", | ||
"version": "1.9.5", | ||
"version": "1.10.0", | ||
"description": "keycloak-nodejs-connect module for Nest", | ||
@@ -49,3 +49,3 @@ "author": "John Joshua Ferrer <johnjoshuaferrer@disroot.org>", | ||
"@nestjs/graphql": ">=6", | ||
"keycloak-connect": ">=10.0.0 <24.0.0" | ||
"keycloak-connect": ">=10.0.0 <25.0.0" | ||
}, | ||
@@ -67,3 +67,3 @@ "devDependencies": { | ||
"graphql": "^16.6.0", | ||
"keycloak-connect": "18.0.2", | ||
"keycloak-connect": "24.0.1", | ||
"prettier": "1.19.1", | ||
@@ -70,0 +70,0 @@ "reflect-metadata": "0.1.13", |
@@ -40,2 +40,3 @@ # Nest Keycloak Connect | ||
Registering the module: | ||
```typescript | ||
@@ -46,24 +47,30 @@ KeycloakConnectModule.register({ | ||
clientId: 'my-nestjs-app', | ||
secret: 'secret', | ||
secret: 'secret', | ||
policyEnforcement: PolicyEnforcementMode.PERMISSIVE, // optional | ||
tokenValidation: TokenValidation.ONLINE, // optional | ||
}) | ||
}); | ||
``` | ||
Async registration is also available: | ||
```typescript | ||
KeycloakConnectModule.registerAsync({ | ||
useExisting: KeycloakConfigService, | ||
imports: [ConfigModule] | ||
}) | ||
imports: [ConfigModule], | ||
}); | ||
``` | ||
#### KeycloakConfigService | ||
```typescript | ||
import { Injectable } from '@nestjs/common'; | ||
import { KeycloakConnectOptions, KeycloakConnectOptionsFactory, PolicyEnforcementMode, TokenValidation } from 'nest-keycloak-connect'; | ||
import { | ||
KeycloakConnectOptions, | ||
KeycloakConnectOptionsFactory, | ||
PolicyEnforcementMode, | ||
TokenValidation, | ||
} from 'nest-keycloak-connect'; | ||
@Injectable() | ||
export class KeycloakConfigService implements KeycloakConnectOptionsFactory { | ||
createKeycloakConnectOptions(): KeycloakConnectOptions { | ||
@@ -78,3 +85,3 @@ return { | ||
}; | ||
} | ||
} | ||
} | ||
@@ -89,3 +96,3 @@ ``` | ||
tokenValidation: TokenValidation.ONLINE, | ||
}) | ||
}); | ||
``` | ||
@@ -98,7 +105,9 @@ | ||
#### Global registration using APP_GUARD token | ||
***NOTE: These are in order, see https://docs.nestjs.com/guards#binding-guards for more information.*** | ||
**_NOTE: These are in order, see https://docs.nestjs.com/guards#binding-guards for more information._** | ||
```typescript | ||
providers: [ | ||
{ | ||
provide: APP_GUARD, | ||
provide: APP_GUARD, | ||
useClass: AuthGuard, | ||
@@ -114,5 +123,7 @@ }, | ||
}, | ||
] | ||
]; | ||
``` | ||
#### Scoped registration | ||
```typescript | ||
@@ -127,10 +138,13 @@ @Controller('cats') | ||
### AuthGuard | ||
Adds an authentication guard, you can also have it scoped if you like (using regular `@UseGuards(AuthGuard)` in your controllers). By default, it will throw a 401 unauthorized when it is unable to verify the JWT token or `Bearer` header is missing. | ||
### ResourceGuard | ||
Adds a resource guard, which is permissive by default (can be configured see [options](#nest-keycloak-options)). Only controllers annotated with `@Resource` and methods with `@Scopes` are handled by this guard. | ||
***NOTE: This guard is not necessary if you are using role-based authorization exclusively. You can use role guard exclusively for that.*** | ||
**_NOTE: This guard is not necessary if you are using role-based authorization exclusively. You can use role guard exclusively for that._** | ||
### RoleGuard | ||
Adds a role guard, **can only be used in conjunction with resource guard when enforcement policy is PERMISSIVE**, unless you only use role guard exclusively. | ||
@@ -144,3 +158,9 @@ Permissive by default. Used by controller methods annotated with `@Roles` (matching can be configured) | ||
```typescript | ||
import { Resource, Roles, Scopes, Public, RoleMatchingMode } from 'nest-keycloak-connect'; | ||
import { | ||
Resource, | ||
Roles, | ||
Scopes, | ||
Public, | ||
RoleMatchingMode, | ||
} from 'nest-keycloak-connect'; | ||
import { Controller, Get, Delete, Put, Post, Param } from '@nestjs/common'; | ||
@@ -199,3 +219,3 @@ import { Product } from './product'; | ||
| Decorator | Description | | ||
|--------------------|-----------------------------------------------------------------------------------------------------------| | ||
| ------------------ | --------------------------------------------------------------------------------------------------------- | | ||
| @AuthenticatedUser | Retrieves the current Keycloak logged-in user. (must be per method, unless controller is request scoped.) | | ||
@@ -209,3 +229,5 @@ | @EnforcerOptions | Keycloak enforcer options. | | ||
## Multi tenant configuration | ||
Setting up for multi-tenant is configured as an option in your configuration: | ||
```typescript | ||
@@ -220,5 +242,9 @@ { | ||
}, | ||
realmSecretResolver: (realm) => { | ||
realmSecretResolver: (realm, request) => { | ||
const secrets = { master: 'secret', slave: 'password' }; | ||
return secrets[realm]; | ||
}, | ||
realmAuthServerUrlResolver: (realm, request) => { | ||
const authServerUrls = { master: 'https://master.local/auth', slave: 'https://slave.local/auth' }; | ||
return authServerUrls[realm]; | ||
} | ||
@@ -232,24 +258,28 @@ } | ||
### Keycloak Options | ||
For Keycloak options, refer to the official [keycloak-connect](https://github.com/keycloak/keycloak-nodejs-connect/blob/main/middleware/auth-utils/config.js) library. | ||
### Nest Keycloak Options | ||
| Option | Description | Required | Default | | ||
|-------------------|-------------------------------------------------------------------------------------|----------|--------------| | ||
| cookieKey | Cookie Key | no | KEYCLOAK_JWT | | ||
| logLevels | Built-in logger level (deprecated, will be removed in 2.0) | no | log | | ||
| useNestLogger | Use the nest logger (deprecated, will be removed in 2.0) | no | true | | ||
| policyEnforcement | Sets the policy enforcement mode | no | PERMISSIVE | | ||
| tokenValidation | Sets the token validation method | no | ONLINE | | ||
| multiTenant | Sets the options for [multi-tenant configuration](#multi-tenant-options) | no | - | | ||
| roleMerge | Sets the merge mode for @Role decorator | no | OVERRIDE | | ||
| Option | Description | Required | Default | | ||
| ----------------- | ------------------------------------------------------------------------ | -------- | ------------ | | ||
| cookieKey | Cookie Key | no | KEYCLOAK_JWT | | ||
| logLevels | Built-in logger level (deprecated, will be removed in 2.0) | no | log | | ||
| useNestLogger | Use the nest logger (deprecated, will be removed in 2.0) | no | true | | ||
| policyEnforcement | Sets the policy enforcement mode | no | PERMISSIVE | | ||
| tokenValidation | Sets the token validation method | no | ONLINE | | ||
| multiTenant | Sets the options for [multi-tenant configuration](#multi-tenant-options) | no | - | | ||
| roleMerge | Sets the merge mode for @Role decorator | no | OVERRIDE | | ||
### Multi Tenant Options | ||
| Option | Description | Required | Default | | ||
|---------------------|---------------------------------------------------------------------------------------------------------|----------|--------------| | ||
| resolveAlways | Option to always resolve the realm and secret. Disabled by default. | no | false | | ||
| realmResolver | A function that passes a request (from respective platform i.e express or fastify) and returns a string | yes | - | | ||
| realmSecretResolver | A function that passes the realm string and returns the secret string | yes | - | | ||
| Option | Description | Required | Default | | ||
| -------------------------- | ------------------------------------------------------------------------------------------------------- | -------- | ------- | | ||
| resolveAlways | Option to always resolve the realm and secret. Disabled by default. | no | false | | ||
| realmResolver | A function that passes a request (from respective platform i.e express or fastify) and returns a string | yes | - | | ||
| realmSecretResolver | A function that passes the realm string, and an optional request and returns the secret string | no | - | | ||
| realmAuthServerUrlResolver | A function that passes the realm string, and an optional request and returns the auth server url string | no | - | | ||
## Example app | ||
An [example application](example) is provided in the source code with both Keycloak Realm and Postman requests for you to experiment with. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
86350
39204
1609
274