Authentication Strategy #
Plugins can add new authentication strategies to Kuzzle. For example, our official OAUTH2 Authentication plugin adds OAUTH2 support to Kuzzle.
All authentication strategies supported by Passport.js can be integrated to Kuzzle.
Exposing authenticators #
Passport.js provides a wide range of authentication strategies. Custom authentication strategies can also be implemented by subclassing the abstract Passport Strategy class.
To register strategies to Kuzzle, a authenticators
object property must be exposed by the plugin, for instance:
this.authenticators = {
Local: require('passport-local'),
Oauth2: require('passport-oauth2')
};
Credentials security #
User credentials are very sensitive data, and these must be properly isolated to prevent security vulnerabilities. To do so, Kuzzle guarantees that it never interprets, modifies, or stores credentials information.
Instead, Kuzzle:
- provides a global user unique identifier (referred from now on as the user's kuid, giving the possibility to a user to authenticate with multiple strategies
- entrusts implemented strategies with credentials protection, validation, verification and storage
There are two ways of registering new strategies:
- statically, by exposing a
strategies
object - dynamically, by using the dedicated PluginContextAccessors.strategies methods
Whether strategies are added statically or dynamically, the registered strategy must expose the following properties:
Arguments | Type | Description |
---|---|---|
config | object | Authentication strategy configuration |
methods | object | List of exposed methods |
Statically register strategies #
Plugins can declare a strategies
object which contains the authentication strategies to register.
This object will be interpreted by Kuzzle only once, immediately after this plugin's init function has resolved.
Each key of this object is the name of the strategy to register and the value is the strategy object containing config
and methods
properties.
For example, to register a strategy named local
with the Local
authenticator:
this.authenticators = {
Local: require('passport-local')
};
this.strategies = {
local: {
config: {
authenticator: 'Local'
},
// these methods must be exposed by the plugin
methods: {
create: 'create',
delete: 'delete',
exists: 'exists',
getById: 'getById',
getInfo: 'getInfo',
search: 'search',
update: 'update',
validate: 'validate',
verify: 'verify'
}
}
}
Dynamically register strategies #
Strategies can be register at runtime with the PluginStrategy.add method.
Strategies added dynamically in the plugin's init method are added to the static strategies
object and loaded by Kuzzle after the plugin initialization.
config #
The config
part of the strategies
object can contain the following properties:
Arguments | Type | Description |
---|---|---|
authenticator | string | One of the exposed authenticators name |
authenticateOptions | object | (optional) Additional options to be provided to the Passport's authenticate method |
fields | string[] | (optional) The list of accepted field names by the strategy credentials. The list is informative only, meant to be used by the getAllCredentialFields and the getCredentialFields API methods |
strategyOptions | object | (optional) Options provided to the Passport.js strategy constructor |
Strategy Methods #
The methods
part of the strategies
object can contain the following properties:
Arguments | Type | Description |
---|---|---|
create | string | The name of the exposed create function |
delete | string | The name of the exposed delete function |
exists | string | The name of the exposed exists function |
update | string | The name of the exposed update function |
validate | string | The name of the exposed validate function |
verify | string | The name of the exposed verify function |
afterRegister | string | (optional) The name of the exposed afterRegister function |
getById | string | (optional) The name of the exposed getById function |
getInfo | string | (optional) The name of the exposed getInfo function |
search | string | (optional) The name of the exposed search function |
Even though each strategy must declare its own set of properties, the same strategy method can be used by multiple strategies.
create #
The create
function adds credentials to a user.
For security reasons, plugins are entirely responsible of how credentials are managed, storage included: Kuzzle does not read, modify, or store credentials.
If needed, Kuzzle exposes a secure and isolated storage space for each plugin. It can be accessed using the Repository constructor.
Arguments #
create(request, credentials, kuid, strategy);
Arguments | Type | Description |
---|---|---|
request | KuzzleRequest | API request asking for the credentials creation |
credentials | object | New credentials to create, already validated by this strategy's validate function |
kuid | string | User's kuid |
strategy | string | Authentication strategy used by these credentials |
Returned value #
The create
function must return a promise, resolving to an object. The content of that object depends on this authentication strategy; usually a feedback about the created credentials is expected. That object can be left empty.
The object resolved by the promise is directly forwarded to the originating user. For security reasons, it must only contain non sensitive information.
delete #
The delete
function deletes a user's credentials.
Arguments #
delete (request, kuid, strategy);
Arguments | Type | Description |
---|---|---|
request | KuzzleRequest | API request asking for the credentials deletion |
kuid | string | User's kuid |
strategy | string | Authentication strategy name |
Returned value #
The delete
function must return a promise. The resolved value is not used.
exists #
The exists
function checks whether a user is known to the authentication strategy.
Arguments #
exists(request, kuid, strategy);
Arguments | Type | Description |
---|---|---|
request | KuzzleRequest | Source API request |
kuid | string | User's kuid |
strategy | string | Authentication strategy name |
Returned value #
The exists
function must return a promise, resolving to a boolean representing the result of the user existence check.
update #
The update
function updates a user's credentials.
Arguments #
update(request, credentials, kuid, strategy);
Arguments | Type | Description |
---|---|---|
request | KuzzleRequest | Source API request |
credentials | object | Updated credentials. Those are already validated by this strategy's validate function |
kuid | string | User's kuid |
strategy | string | Authentication strategy name |
Returned value #
The update
function must return a promise, resolving to an object. The content of that object depends on this authentication strategy; usually a feedback about the updated credentials is expected. That object can be left empty.
The object resolved by the promise is directly forwarded to the originating user. For security reasons, it must only contain non sensitive information.
validate #
The validate
function verifies that credentials are well-formed.
Arguments #
validate(request, credentials, kuid, strategy, isUpdate);
Arguments | Type | Description |
---|---|---|
request | KuzzleRequest | Source API request |
credentials | object | Credentials to validate |
kuid | string | User's kuid |
strategy | string | Authentication strategy name |
isUpdate | boolean | Tells whether the request is a credentials update. In the case of an update, the credentials object may only contain changes to be applied, instead of a complete credentials description |
Returned value #
The function validate
must return a promise. The resolved value, if there is one, is ignored.
verify #
The verify function authenticates a user.
The number of arguments taken by the verify
function depends on the authentication strategy. For instance, a local
authentication strategy requires that the verify
function validates both a user name and a password, so these two arguments will have to be provided to the verify
function.
Arguments #
verify(payload, ...)
Arguments | Type | Description |
---|---|---|
payload | object | Login request made to passport |
... | * | Additional arguments; depends on the authentication strategy |
payload #
The payload
object has the following properties:
Properties | Type | Description |
---|---|---|
original | KuzzleRequest | Source API login request |
query | object | Direct link to original.input.args , containing the optional request arguments |
body | object | Direct link to original.input.body , containing the request body content |
Returned value #
The verify
function must return a promise, resolving to an object with the following properties:
Properties | Type | Description |
---|---|---|
kuid | string | If the authentication succeeds, this property must be set to the user's kuid. Otherwise, this must be set to null |
message | string | If kuid is set to null (authentication failed), this optional property can be set with a rejection reason |
A failed authentication is not an error. The returned promise should only be rejected if an actual error occurs.
(optional) afterRegister #
The afterRegister
function is called when the Passport.js strategy is instantiated by Kuzzle.
Arguments #
afterRegister(strategyInstance);
Arguments | Type | Description |
---|---|---|
strategyInstance | object | The Passport.js strategy instance |
(optional) getById #
The getById
function returns credentials information using the authentication strategy's user identifier (which may not be the kuid).
If this function is not implemented, an empty object is returned by Kuzzle instead.
The returned information can be forwarded to users. For security reasons, it must only contain non sensitive information.
Arguments #
getById(request, id, strategy);
Arguments | Type | Description |
---|---|---|
request | KuzzleRequest | The API request asking for credentials information |
id | string | Strategy's user identifier |
strategy | string | Authentication strategy name |
Returned value #
The getById
function must return a promise, resolving to an object containing credentials information. It can be left empty.
(optional) getInfo #
The getInfo
function returns information about a user's credentials.
If this function is not implemented, an empty object is returned by Kuzzle instead.
The returned information can be forwarded to users. For security reasons, it must only contain non sensitive information.
Arguments #
getInfo(request, kuid, strategy);
Arguments | Type | Description |
---|---|---|
request | KuzzleRequest | The API request asking for credentials information |
kuid | string | User's kuid |
strategy | string | Authentication strategy name |
Returned value #
The getInfo
function must return a promise, resolving to an object containing credentials information. It can be left empty.
(optional) search #
Given a credentials related search query, returns matched users' kuid.
If this function is not implemented, a missing_optional_method
error occurs.
Beware of which properties are searchable. It would be unsafe to let the password be a search criteria, isn't it?
Arguments #
search(searchBody, options);
Arguments | Type | Description |
---|---|---|
searchBody | ElasticSearch Query DSL | A query concerning the authentication strategy credentials |
options | { from?: number, size?: number } | Search options useful for paginations |
Returned value #
The search
function must return a promise, resolving to an search result with the following properties:
hits
: Array of matched users. Each hit has the following properties:kuid
: Users unique identifier...
: Non sensitive credentials specific to this authentication strategy
total
: Total of matched users.
The object resolved by the promise is directly forwarded to the originating user. For security reasons, it must only contain non sensitive information.
Example #
module.exports = class AuthenticationPlugin {
constructor() {}
/**
Required plugin initialization function
(see the "Plugin prerequisites" section)
*/
init (customConfig, context) {
this.authenticators = {
StrategyConstructor: require('some-passport-strategy')
};
this.strategies = {
'<strategy name>': {
config: {
// Must be declared in this.authenticators
authenticator: 'StrategyConstructor',
// The list of fields that have to be provided in the credentials
fields: ['login', 'password']
},
methods: {
create: 'create',
delete: 'delete',
exists: 'exists',
update: 'update',
validate: 'validate',
verify: 'verify'
}
}
};
}
/**
* Stores the provided credentials
* Must keep a link between the persisted credentials
* and the kuid
*/
create (request, credentials, kuid) {
// store credentials
return Promise.resolve({/* non sensitive credentials info */});
}
/**
* Removes the user's stored credentials from
* the plugin persistence layer
*/
delete (request, kuid) {
// remove credentials
return Promise.resolve();
}
/**
* Checks if user's credentials exist in the persistence layer
*/
exists (request, kuid) {
// check credentials existence
return Promise.resolve(/* boolean value *);
}
/**
* Updates the user's credentials information in the
* persistence layer
*
* @param {KuzzleRequest} request
* @param {object} credentials
* @param {string} kuid
* @returns {Promise<object>}
*/
update (request, credentials, kuid) {
// update credentials
return Promise.resolve(/* non sensitive credentials info *);
}
/**
* Validates credentials against the strategy rules
* (required fields, password strength, username uniqueness, ...)
*/
validate (request, credentials, kuid, strategy, isUpdate) {
// validate credentials
return Promise.resolve(/* true|false *);
}
/**
* Returns an object with the authenticated user id if successful,
* and a reason if the authentication fails
*/
verify (request, ...credentials) {
const kuid = /* authentification */;
if (kuid) {
return Promise.resolve({kuid});
}
return Promise.resolve({
kuid: null,
message: 'Login failed - You shall not pass! Reason: ...'
});
}
}