
## bottleJwtAuth - JWT Authentication for Bottle

**bottleJwtAuth** provides JWT Bearer token authentication and authorization for [Bottle web Framework](https://bottlepy.org) web apps.


## Installation

```bash
# pip install bottleJwtAuth
```

### Using bottleJwtAuth
```python
from bottle import Bottle
from JwtAuth import JwtAuth
from config import issuer, audience


app = Bottle()

# Create a restriction on any route with a prefix of '/api/'
aud=JwtAuth(jwt_config={issuer=issuer, audience=audience, url_prefix='/api/')

app.install(aud)

# Basic JWT authentication and validation required to access this view:
@app.route('/api/whoami')
def whoami():
    return ...

# Specific claim requirements to access this view:
@app.route('/api/adduser/<user>', claim={'role':['appadmin', 'usradmin']})
def add_user(user=None):
    ....

# No JWT or any other feature is required to access this route
@app.route('/info)
def info():
    return '....'

if __name__ == '__main__:
    app.run()

```
#### Signature and Parameters

```
auth = JwtAuth(jwt_config=jwt_conf, issuer=issuer, audience=audience, url_prefix='/')
```

**`jwt_config`** A python `dict` used to configure a JWT Authorizor.  Discussed below in more detail. Default: a symmetric HS256 key is generated.

**`issuer`** The issuer URN (generally a URL) used for the JWT `iss` on encode and used to verify on decode.  Default is `None`

**`audience`** The audience URN used for verification of the JWT `aud` on decode.  You will need to add this to the payload on encode operations.

**`url_prefix`** The prefix path component used to match route rules where JwtAuth will impose a JWT requirement and verification.  The default is '/' - the whole app.

### Prefix-based JwtAuth

Any bottle routes that matches the **`url_prefix`** of a JwtAuth plugin will have middleware added to retrieve and verify the JWT bearer token form the HTTP `Authentication` header.
The plugin verifies:
* The request has a valid authentication header with a bearer token.
* The token signature is correct
* The iss field matches the expected issuer
* The aud field matches the expected audience
* The token has not expired.

This applies to all routes and subordinate routes defined, unless the route itself disables the JwtAuth plugin.  This is done with the `skip` config in the bottle route statement.

```python
@app.route('/dontcheckjwt', skip=['/api/'])
```
Note the plugin `name` is identical to the `url_prefix`, in the example, `'/api/'`

Multiple JwtAuth plugins can be added to the same app as long as the `url_prefix` of each plugin do not overlap. 

### Specific route restrictions

Additional requirements can be placed on specific routes:
* you can define a **`scope`** (or **`scp`**) restriction on a given view in the route definition.
* you can define **`claim`** restrictions on a given view in the route definition.
* this only applies to routes with the url_prefix applied.
* **`scope`** and **`claim`** requirements can be combined.

Consider an example with a scope requirement:
```python
auth = JwtAuth(config, issuer='urn:xyz', audience='api:testapi', url_prefix='/api')

# Verify JWT and scope
@app.route('/api/test/3', scp='email')
def test3():
    return 'test3 - JWT verified, and scope of email verified'
```
* if more than one scope is specified, the token must contain all of these.  e.g.
```python
@app.route('/api/test/3/, scp=['email', 'User.write'])
def viewx():
    ...
```

With a claim requirement, multiple claims can be required, and the token much be able to satisfy each claim. 

However, only one matching value per claim is required. For example, for a request to access viewy, the token must contain a claim call `name` that contains either 'bob' or 'alice', and must have the `role` of `appadmin` as well.

```python
@app.route('/api/test/4/', claim={'name': ['bob', 'alice'], 'role':'appadmin'})
def viewy():
    ...
```

## *jwt_config* configuration details

The `jwt_config` option is a dict that can take multiple forms, depending on how **JwtAuth** is deployed. This `dict` is provided to an underlying class **JwtEncoder** that itself uses the **PyJWT** module to implement both encoding and decoding of JWT's - though **bottleJwtAuth** is only concerned with decoding of JWTs.

Details on configuring **jwt_config** are [available in the readme.](https://github.com/Glocktober/JwtEncoder)
**JwtEncoder** provides a configuration interface to support a few different JST encryption methods, depending on what your needs are.  These include:
* shared key
* providing a public and private key
* providing an X509 RSA certificate
* JWKS url
