mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
3d158a195a
* chore: plugin search ui chore: progress chore: format code chore: progress chore: @verdaccio-ui/copy-clipboard chore: search finish * chore: ui-components * Update ToolList.tsx * xss protection * Update static-data.yml * Update AddonCard.tsx
304 lines
11 KiB
Markdown
304 lines
11 KiB
Markdown
---
|
||
id: plugin-auth
|
||
title: 'Authentication Plugin'
|
||
---
|
||
|
||
## What's an Authentication Plugin? {#whats-an-authentication-plugin}
|
||
|
||
Is a sort plugin that allows to handle who access or publish to a specific package. By default the `htpasswd` is built-in, but can
|
||
easily be replaced by your own.
|
||
|
||
## Getting Started
|
||
|
||
The authentication plugins are defined in the `auth:` section, as follows:
|
||
|
||
```yaml
|
||
auth:
|
||
htpasswd:
|
||
file: ./htpasswd
|
||
```
|
||
|
||
also multiple plugins can be chained:
|
||
|
||
```yaml
|
||
auth:
|
||
htpasswd:
|
||
file: ./htpasswd
|
||
anotherAuth:
|
||
foo: bar
|
||
bar: foo
|
||
lastPlugin:
|
||
foo: bar
|
||
bar: foo
|
||
```
|
||
|
||
> If one of the plugin in the chain is able to resolve the request, the next ones will be ignored.
|
||
|
||
## How do the authentication plugin works? {#how-do-the-authentication-plugin-works}
|
||
|
||
Basically we have to return an object with a single method called `authenticate` that will receive 3 arguments (`user, password, callback`).
|
||
|
||
On each request, `authenticate` will be triggered and the plugin should return the credentials, if the `authenticate` fails, it will fallback to the `$anonymous` role by default.
|
||
|
||
### API {#api}
|
||
|
||
```typescript
|
||
interface IPluginAuth<T> extends IPlugin<T> {
|
||
authenticate(user: string, password: string, cb: AuthCallback): void;
|
||
adduser?(user: string, password: string, cb: AuthCallback): void;
|
||
changePassword?(user: string, password: string, newPassword: string, cb: AuthCallback): void;
|
||
allow_publish?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
|
||
allow_access?(user: RemoteUser, pkg: AllowAccess & PackageAccess, cb: AuthAccessCallback): void;
|
||
allow_unpublish?(
|
||
user: RemoteUser,
|
||
pkg: AllowAccess & PackageAccess,
|
||
cb: AuthAccessCallback
|
||
): void;
|
||
apiJWTmiddleware?(helpers: any): Function;
|
||
}
|
||
```
|
||
|
||
> Only `adduser`, `allow_access`, `apiJWTmiddleware`, `allow_publish` and `allow_unpublish` are optional, verdaccio provide a fallback in all those cases.
|
||
|
||
#### `apiJWTmiddleware` method {#apijwtmiddleware-method}
|
||
|
||
Since `v4.0.0`
|
||
|
||
`apiJWTmiddleware` was introduced on [PR#1227](https://github.com/verdaccio/verdaccio/pull/1227) in order to have full control of the token handler, overriding this method will disable `login/adduser` support. We recommend don't implement this method unless is totally necessary. See a full example [here](https://github.com/verdaccio/verdaccio/pull/1227#issuecomment-463235068).
|
||
|
||
## What should I return in each of the methods? {#what-should-i-return-in-each-of-the-methods}
|
||
|
||
Verdaccio relies on `callback` functions at time of this writing. Each method should call the method and what you return is important, let's review how to do it.
|
||
|
||
### `authentication` callback {#authentication-callback}
|
||
|
||
Once the authentication has been executed there is 2 options to give a response to `verdaccio`.
|
||
|
||
##### If the authentication fails {#if-the-authentication-fails}
|
||
|
||
If the auth was unsuccessful, return `false` as the second argument.
|
||
|
||
```typescript
|
||
callback(null, false);
|
||
```
|
||
|
||
##### If the authentication success {#if-the-authentication-success}
|
||
|
||
The auth was successful.
|
||
|
||
`groups` is an array of strings where the user is part of.
|
||
|
||
```
|
||
callback(null, groups);
|
||
```
|
||
|
||
##### If the authentication produce an error {#if-the-authentication-produce-an-error}
|
||
|
||
The authentication service might fails, and you might want to reflect that in the user response, eg: service is unavailable.
|
||
|
||
```
|
||
import { getInternalError } from '@verdaccio/commons-api';
|
||
|
||
callback(getInternalError('something bad message), null);
|
||
```
|
||
|
||
> A failure on login is not the same as service error, if you want to notify user the credentials are wrong, just return `false` instead string of groups. The behaviour mostly depends of you.
|
||
|
||
### `adduser` callback {#adduser-callback}
|
||
|
||
##### If adduser success {#if-adduser-success}
|
||
|
||
If the service is able to create an user, return `true` as the second argument.
|
||
|
||
```typescript
|
||
callback(null, true);
|
||
```
|
||
|
||
##### If adduser fails {#if-adduser-fails}
|
||
|
||
Any other action different than success must return an error.
|
||
|
||
```typescript
|
||
import { getConflict } from '@verdaccio/commons-api';
|
||
|
||
const err = getConflict('maximum amount of users reached');
|
||
|
||
callback(err);
|
||
```
|
||
|
||
### `changePassword` callback {#changepassword-callback}
|
||
|
||
##### If the request is successful {#if-the-request-is-successful}
|
||
|
||
If the service is able to create an user, return `true` as the second argument.
|
||
|
||
```typescript
|
||
const user = serviceUpdatePassword(user, password, newPassword);
|
||
|
||
callback(null, user);
|
||
```
|
||
|
||
##### If the request fails {#if-the-request-fails}
|
||
|
||
Any other action different than success must return an error.
|
||
|
||
```typescript
|
||
import { getNotFound } from '@verdaccio/commons-api';
|
||
|
||
const err = getNotFound('user not found');
|
||
|
||
callback(err);
|
||
```
|
||
|
||
### `allow_access`, `allow_publish`, or `allow_unpublish` callback {#allow_access-allow_publish-or-allow_unpublish-callback}
|
||
|
||
These methods aims to allow or deny trigger some actions.
|
||
|
||
##### If the request success {#if-the-request-success}
|
||
|
||
If the service is able to create an user, return a `true` as the second argument.
|
||
|
||
```typescript
|
||
|
||
allow_access(user: RemoteUser, pkg: PackageAccess, cb: Callback): void {
|
||
const isAllowed: boolean = checkAction(user, pkg);
|
||
|
||
callback(null, isAllowed)
|
||
}
|
||
```
|
||
|
||
##### If the request fails {#if-the-request-fails-1}
|
||
|
||
Any other action different than success must return an error.
|
||
|
||
```typescript
|
||
import { getNotFound } from '@verdaccio/commons-api';
|
||
|
||
const err = getForbidden('not allowed to access package');
|
||
|
||
callback(err);
|
||
```
|
||
|
||
## Generate an authentication plugin {#generate-an-authentication-plugin}
|
||
|
||
For detailed info check our [plugin generator page](plugin-generator). Run the `yo` command in your terminal and follow the steps.
|
||
|
||
```
|
||
➜ yo verdaccio-plugin
|
||
|
||
Just found a `.yo-rc.json` in a parent directory.
|
||
Setting the project root at: /Users/user/verdaccio_yo_generator
|
||
|
||
_-----_ ╭──────────────────────────╮
|
||
| | │ Welcome to │
|
||
|--(o)--| │ generator-verdaccio-plug │
|
||
`---------´ │ in plugin generator! │
|
||
( _´U`_ ) ╰──────────────────────────╯
|
||
/___A___\ /
|
||
| ~ |
|
||
__'.___.'__
|
||
´ ` |° ´ Y `
|
||
|
||
? What is the name of your plugin? service-name
|
||
? Select Language typescript
|
||
? What kind of plugin you want to create? auth
|
||
? Please, describe your plugin awesome auth plugin
|
||
? GitHub username or organization myusername
|
||
? Author's Name Juan Picado
|
||
? Author's Email jotadeveloper@gmail.com
|
||
? Key your keywords (comma to split) verdaccio,plugin,auth,awesome,verdaccio-plugin
|
||
create verdaccio-plugin-authservice-name/package.json
|
||
create verdaccio-plugin-authservice-name/.gitignore
|
||
create verdaccio-plugin-authservice-name/.npmignore
|
||
create verdaccio-plugin-authservice-name/jest.config.js
|
||
create verdaccio-plugin-authservice-name/.babelrc
|
||
create verdaccio-plugin-authservice-name/.travis.yml
|
||
create verdaccio-plugin-authservice-name/README.md
|
||
create verdaccio-plugin-authservice-name/.eslintrc
|
||
create verdaccio-plugin-authservice-name/.eslintignore
|
||
create verdaccio-plugin-authservice-name/src/index.ts
|
||
create verdaccio-plugin-authservice-name/index.ts
|
||
create verdaccio-plugin-authservice-name/tsconfig.json
|
||
create verdaccio-plugin-authservice-name/types/index.ts
|
||
create verdaccio-plugin-authservice-name/.editorconfig
|
||
|
||
I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.
|
||
|
||
|
||
⸨ ░░░░░░░░░░░░░░░░░⸩ ⠋ fetchMetadata: sill pacote range manifest for @babel/plugin-syntax-jsx@^7.7.4 fetc
|
||
```
|
||
|
||
After the install finish, access to your project scalfold.
|
||
|
||
```
|
||
➜ cd verdaccio-plugin-service-name
|
||
➜ cat package.json
|
||
|
||
{
|
||
"name": "verdaccio-plugin-service-name",
|
||
"version": "0.0.1",
|
||
"description": "awesome auth plugin",
|
||
...
|
||
```
|
||
|
||
## Full implementation ES5 example {#full-implementation-es5-example}
|
||
|
||
```javascript
|
||
function Auth(config, stuff) {
|
||
var self = Object.create(Auth.prototype);
|
||
self._users = {};
|
||
|
||
// config for this module
|
||
self._config = config;
|
||
|
||
// verdaccio logger
|
||
self._logger = stuff.logger;
|
||
|
||
// pass verdaccio logger to ldapauth
|
||
self._config.client_options.log = stuff.logger;
|
||
|
||
return self;
|
||
}
|
||
|
||
Auth.prototype.authenticate = function (user, password, callback) {
|
||
var LdapClient = new LdapAuth(self._config.client_options);
|
||
....
|
||
LdapClient.authenticate(user, password, function (err, ldapUser) {
|
||
...
|
||
var groups;
|
||
...
|
||
callback(null, groups);
|
||
});
|
||
};
|
||
|
||
module.exports = Auth;
|
||
```
|
||
|
||
And the configuration will looks like:
|
||
|
||
```yaml
|
||
auth:
|
||
htpasswd:
|
||
file: ./htpasswd
|
||
```
|
||
|
||
Where `htpasswd` is the sufix of the plugin name. eg: `verdaccio-htpasswd` and the rest of the body would be the plugin configuration params.
|
||
|
||
### List Community Authentication Plugins {#list-community-authentication-plugins}
|
||
|
||
- [verdaccio-bitbucket](https://github.com/idangozlan/verdaccio-bitbucket): Bitbucket authentication plugin for verdaccio.
|
||
- [verdaccio-bitbucket-server](https://github.com/oeph/verdaccio-bitbucket-server): Bitbucket Server authentication plugin for verdaccio.
|
||
- [verdaccio-ldap](https://www.npmjs.com/package/verdaccio-ldap): LDAP auth plugin for verdaccio.
|
||
- [verdaccio-active-directory](https://github.com/nowhammies/verdaccio-activedirectory): Active Directory authentication plugin for verdaccio
|
||
- [verdaccio-gitlab](https://github.com/bufferoverflow/verdaccio-gitlab): use GitLab Personal Access Token to authenticate
|
||
- [verdaccio-gitlab-ci](https://github.com/lab360-ch/verdaccio-gitlab-ci): Enable GitLab CI to authenticate against verdaccio.
|
||
- [verdaccio-htpasswd](https://github.com/verdaccio/verdaccio-htpasswd): Auth based on htpasswd file plugin (built-in) for verdaccio
|
||
- [verdaccio-github-oauth](https://github.com/aroundus-inc/verdaccio-github-oauth): Github oauth authentication plugin for verdaccio.
|
||
- [verdaccio-github-oauth-ui](https://github.com/n4bb12/verdaccio-github-oauth-ui): GitHub OAuth plugin for the verdaccio login button.
|
||
- [verdaccio-groupnames](https://github.com/deinstapel/verdaccio-groupnames): Plugin to handle dynamic group associations utilizing `$group` syntax. Works best with the ldap plugin.
|
||
- [verdaccio-sqlite](https://github.com/bchanudet/verdaccio-sqlite): SQLite Authentication plugin for Verdaccio
|
||
- [verdaccio-okta-auth](https://github.com/hogarthww-labs/verdaccio-okta-auth) Verdaccio Okta Auth
|
||
- [verdaccio-azure-ad-login](https://github.com/IhToN/verdaccio-azure-ad-login) Let your users authenticate into Verdaccio via Azure AD OAuth 2.0 API
|
||
- [verdaccio-auth-gitlab](https://github.com/pfdgithub/verdaccio-auth-gitlab) Verdaccio authentication plugin by gitlab personal access tokens.
|
||
|
||
**Have you developed a new plugin? Add it here !**
|