Skip to content

Commit

Permalink
JWT API authentication docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
vtsykun committed Jan 15, 2023
1 parent 7436ac7 commit 874dc30
Show file tree
Hide file tree
Showing 28 changed files with 782 additions and 51 deletions.
13 changes: 12 additions & 1 deletion docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,15 @@

# Introduction
- [Overview](readme.md)
- [Installation](installation.md)

# Setup
- [Installation](installation.md)
- [Using docker](installation-docker.md)

# Usage
- [Administration](usage/README.md)
- [Admin API](usage/api.md)
- [Webhooks](webhook.md)
- [User Authentication](authentication.md)
- [JWT Configuration](authentication-jwt.md)
- [LDAP Configuration](authentication-ldap.md)
105 changes: 105 additions & 0 deletions docs/authentication-jwt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# JWT API Authentication

By default, packeton is storage api tokens in database for each user. But when user
loaded from custom user provider, like LDAP need to enable JWT configuration to use API. So Packeton can be
configured with a non-standard login type to support [JSON Web Tokens](https://en.wikipedia.org/wiki/JSON_Web_Token).

The JSON Web Token integration in Packeton uses the [Firebase library](https://github.com/firebase/php-jwt).
Also, JWT authentication can be enabled only for API.

Add `yaml` configuration file to path `config/packages/`, for example `config/packages/jwt.yaml` to enable it.

```yaml
packeton:
jwt_authentication:
private_key: '%kernel.project_dir%/var/jwt/eddsa-key.pem'
public_key: '%kernel.project_dir%/var/jwt/eddsa-public.pem'
```
Full configurations:
```yaml
# config/packages/config/packages/jwt.yaml
packeton:
jwt_authentication:
private_key: '%kernel.project_dir%/var/jwt/eddsa-key.pem' # required for token sign
public_key: '%kernel.project_dir%/var/jwt/eddsa-public.pem' # required for token verification
passphrase: ~
algo: EdDSA # Sign algo, here libsodium EdDSA
```
## Generate the public/private keys
```
bin/console packagist:jwt:generate-keypair

bin/console packagist:jwt:generate-keypair --overwrite
```

Available options:
* --overwrite will overwrite your keys if they already exist.

If keys already exists, a warning message will be raised to prevent you from overwriting your keys accidentally.

### JWT Token TTL.

JWT Token is never expire. It was done for compatibility with composer basic HTTP authorization.
Each time the api is called, Packeton is checked that the user exists in the database and that
he has the same set of permissions and roles.

### Digital signatures algos.

We support all algos from Firebase lib: HMAC, OpenSSL RSA, OpenSSL

Rsa, HMAC, EdDSA algorithms generate invariant tokens, i.e. the value of the token will be constant for the same user.
It might be convenient as the app does not store the generated tokens.

Example how to change algo:

```yaml
packeton:
jwt_authentication:
...
algo: RS256 # RSA 256
```
### Generating keys using OpenSSL
Example, how to generate an RSA private key, `key.pem` - private key. `public.pem` - public

```
openssl genrsa -out key.pem 2048
openssl rsa -in key.pem -outform PEM -pubout -out public.pem
```
Example, how to generate an ES256 (elliptic curve) key pairs.
```
openssl ecparam -name prime256v1 -genkey -noout -out key.pem
openssl ec -in key.pem -pubout -out public.pem
```
## Obtain the token
You can run command `packagist:user:manager` to show the api token:
```
bin/console packagist:user:manager admin --show-token --token-format=jwt
```
Or you can found api token on your profile page.
[![Keys](img/jwt_keys.png)](img/jwt_keys.png)
### Use the token
Simply use the JWT, like standard API token for composer api.
#### Cache LDAP user loading.
Since 2.0 composer downloads all packages in parallel, it may run more 12 request at the same time.
To prevent calls external LDAP provider each time for JWT token verify, the obtained LDAP user object
placed to cache with 60 sec TTL.
125 changes: 125 additions & 0 deletions docs/authentication-ldap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Authenticating against an LDAP server

You can enable LDAP authenticating only on configuration level.

Packeton has pre-installed Symfony LDAP component. Add the file `config/packages/ldap.yaml` to enable LDAP
with following content. See LDAP in [Symfony Docs](https://symfony.com/doc/current/security/ldap.html)

```yaml
parameters:
default_login_provider: 'form_login_ldap'
default_login_options:
provider: all_users
login_path: /login
use_forward: false
check_path: /login
failure_path: null
service: Symfony\Component\Ldap\Ldap
dn_string: 'uid={username},dc=example,dc=com'

services:
Symfony\Component\Ldap\Ldap:
arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
tags:
- ldap

Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: ldap.forumsys.com
port: 389

security:
providers:
users_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: dc=example,dc=com
search_dn: "cn=read-only-admin,dc=example,dc=com"
search_password: password
default_roles: ROLE_MAINTAINER
uid_key: uid

all_users:
chain:
providers: ['packagist', 'users_ldap']
```
Here is working example where used test `ldap.forumsys.com` server https://www.forumsys.com/2022/05/10/online-ldap-test-server/

Using LDAP integration does not prevent you from creating user manually from CLI and assign more accessible roles.
At the same LDAP password validation will be done on LDAP server side, because `CheckLdapCredentialsListener` has higher priority
loading than default check listener. Therefore, if user is not enable in LDAP - it will not able login to packeton.

## User providers priority.

Packeton use Symfony [Chain User Provider](https://symfony.com/doc/current/security/user_providers.html#chain-user-provider)
to lookup users.

If you want to use customer user restriction by vendors and versions, `packagist` user provider must load before ldap.

```yaml
security:
providers:
users_ldap:
ldap:
...
all_users:
chain:
providers: ['packagist', 'users_ldap'] # Load user/roles form default packagist and if not found - use ldap user
providers: ['users_ldap', 'packagist'] # packagist users will be ignore
```

## Load different roles from LDAP.

You can use more 1 user providers:

```yaml
security:
providers:
users_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: dc=example,dc=com
search_dn: "cn=read-only-admin,dc=example,dc=com"
filter: "(&(objectclass=groupOfUniqueNames)(ou=scientists)(uniqueMember=uid={username},dc=example,dc=com))"
search_password: password
default_roles: ROLE_MAINTAINER
uid_key: uid
users_ldap_admin:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: dc=example,dc=com
search_dn: "cn=read-only-admin,dc=example,dc=com"
filter: "(&(objectclass=groupOfUniqueNames)(ou=mathematicians)(uniqueMember=uid={username},dc=example,dc=com))"
search_password: password
default_roles: ROLE_ADMIN
uid_key: uid
all_users:
chain:
providers: ['packagist', 'users_ldap', 'users_ldap_admin']
```

Here test example where exists two Groups (ou) that include:

* ou=mathematicians,dc=example,dc=com - assign role ROLE_ADMIN
* ou=scientists,dc=example,dc=com - assign role ROLE_MAINTAINER

## API authentication with LDAP users.

By default, packeton is storage api token in database for each user.
But if the user was loaded by custom external users' provider, but from the database, you will need enable JWT configuration.
See [JWT Configuration](authentication-jwt.md)

## Enable LDAP for docker runtime.

You can use docker volume to share own configuration to application.

```
...
volumes:
- .docker:/data
- ${PWD}/ldap.yaml:/var/www/packagist/config/packages/ldap.yaml
```
53 changes: 53 additions & 0 deletions docs/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# User Authentication

Packeton may support multiple methods of authenticating users. It can additionally be extended to support
custom authentication schemes.

## Web User authentication
Included in packeton is support for authenticating users via:

* A username and password.
* An email address and password.

But possible to enable LDAP only via configuration, see [ldap authentication](./authentication-ldap.md)

## Composer API authentication

Packeton is support API authentication only with api token. Password usage is not allowed.
You can see api token in thr user profile menu.

Support for authenticating users via:
* HTTP Basic Authentication (username and api token)
* Short query param `token` = `username:apiToken`
* Default packagist hook API (query params: `username` = username, `apiToken` = apiToken)

Your customer needs to authenticate to access their Composer repository:
The simplest way to provide your credentials is providing your set of credentials inline with the repository specification such as:

```json
{
"repositories": [
{
"type": "composer",
"url": "https://<username>:<api_token>@example.org"
}
]
}
```

When you don't want to hard code your credentials into your composer.json, you can set up it global.

```
composer config --global --auth http-basic.example.org username api_token
```

Example API call.

```
curl https://example.com/packages/list.json
-u "username:apiToken"
```

```
curl https://example.com/packages/list.json?token=username:apiToken
```
Binary file added docs/img/intro1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/intro2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/intro3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/intro4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/intro5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/intro6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/intro7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/intro8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/jwt_keys.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 874dc30

Please sign in to comment.