Setting Up JWT Authorization

This guide shows how Giftless could be set up to accept JWT tokens as a mechanism for both authentication and authorization, and shows some examples of manually generating JWT tokens and sending them to Giftless.

JWT tokens are useful because they allow an external service to verify the user’s identity and grant it specific access permissions to objects stored by Giftless. The external service generates a token which can be verified by Giftless, but Giftless does not need to have any awareness of the granting party’s authentication mechanism or authorization logic.

It is recommended that you read the Authentication and Authorization overview section in the documentation to get yourself familiar with some basic concepts.

This tutorial assumes you have at least completed the Getting Started guide, and have Giftless configured to store objects either locally or in Google Cloud Storage. The code samples assume you are running in the same directory and virtual environment in which Giftless was installed in previous tutorials.

Generate an RSA key pair

JWT tokens can be signed and encrypted using different algorithms, but one common and often useful pattern is to use an RSA public / private key pair. This allows distributing the public key, used for token verification, to multiple (potentially untrusted) services while token generation is done using a secret key.

For this tutorial we will generate an RSA key pair:

# Generate an RSA private key in PEM encoded format
ssh-keygen -t rsa -b 4096 -m PEM -f jwt-rs256.key

# Extract the public key to a PEM file
openssl rsa -in jwt-rs256.key -pubout -outform PEM -out jwt-rs256.key.pub

Do not enter any passphrase when generating the private key.

This will create two files in the current directory: jwt-rsa256.key which is the private key, and jwt-rsa256.key.pub which is the public key.

Configure Giftless to use JWT

To configure Giftless to require JWT tokens in requests, modify the AUTH_PROVIDERS section in your Giftless config file (giftless.conf.yaml) t0:

AUTH_PROVIDERS:
  - factory: giftless.auth.jwt:factory
    options: 
      algorithm: RS256
      public_key_file: jwt-rs256.key.pub

Then, set the path to the configuration file, and start the local development server:

export GIFTLESS_CONFIG_FILE=giftless.conf.yaml
flask run

Generating a JWT Token

We now need to generate a JWT token that both authenticates us with Giftless, and carries some authorization information in the form of granted scopes. These tell Giftless what access level (e.g. read or write) the user has to which namespaces or objects.

In a production setting, JWT tokens will be generated by a special-purpose authorization service. For the purpose of this tutorial, we will manually generate tokens using pyjwt - a command line tool that comes with the PyJWT Python library:

Note

you can also use the debugging tool in https://jwt.io to generate tokens

pip install pyjwt  # this should already be installed in your Giftless virtual environment
SCOPES="obj:my-organization/*"
JWT_TOKEN=$(pyjwt --alg RS256 --key "$(cat jwt-rs256.key)" encode exp=+3600 sub=mr-robot scopes="$SCOPES")
echo $JWT_TOKEN

This generates a JWT token identifying the user as mr-robot. The token is valid for 1 hour, and will grant both read and write access to every object under the my-organization namespace.

Using JWT tokens with Git LFS clients

Authenticating from custom Python code

giftless-client is a Python implementation of a Git LFS client with some Giftless specific extras such as support for the multipart-basic transfer mode.

To use the JWT token we have just generated with giftless-client, let’s install it into our tutorial virtual environment:

pip install giftless-client

And create the following Python script in lfs-client.py:

import os
import sys
from giftless_client import LfsClient


def main(file):
    token = os.getenv('JWT_TOKEN')  # assuming we set the env var above
    organization = 'my-organization'
    repo = 'my-repo'
    
    client = LfsClient(
        lfs_server_url='http://127.0.0.1:5000', # Git LFS server URL
        auth_token=token                        # JWT token
    )
    
    result = client.upload(file, organization, repo)
    print(f"Upload complete: Object ID {result['oid']}, {result['size']} bytes")

if __name__ == '__main__':
    with open(sys.argv[1], 'rb') as f:
        main(f)

Assuming you have set the JWT_TOKEN environment variable as described above, run it using:

export JWT_TOKEN
dd if=/dev/urandom of=random-file.bin bs=1024 count=1024 # Generate a random 1mb file
python lfs-client.py random-file.bin  # Upload it to storage via Git LFS

The output should be something like:

Upload complete: Object ID 7e3dd874a5475c946e441e17181cfcd8fac7760bb373d38b93aee9f9a93dce2e, 1048576 bytes

(with a different object ID value)

Note

this will fail if an hour or more has passed since we generated our token. In this case, simply regenerate the token and run again

Authenticating from Web-based Clients

One advantage of using JWT tokens for authentication and authorization is that they can be securely handed to Web clients, which in turn can use them to upload and download files securely from LFS-managed storage, directly from a Web application.

While we will not be demonstrating using Giftless to upload and download files from a browser, you can do this using JWT tokens to authenticate. giftless-client-js is a JavaScript client library which implements the Git LFS protocol with some Giftless specific “extras”. You can look at the documentation of this library to see some examples of how to do this.

Important

To communicate with Giftless from a browser, you will most likely need to enable CORS support in Giftless, or deploy Giftless on the same scheme / host / port which serves your Web application.

Authenticating command line git lfs

Let’s see if we can now use this token to push an object using command line git-lfs. Assuming you have followed the Getting Started tutorial, you should have a local repository checked out under ./local-repo. We’ll add another large file to this repo:

cd local-repo
dd if=/dev/zero of=512kb-blob.bin bs=1024 count=512
git add 512kb-blob.bin
git commit -m "Added a 512 kb file"
git push

At this stage you will be asked for a username for your Giftless server.

Giftless allows passing a JWT token using the Basic HTTP authentication scheme. This is designed specifically to support clients, such as git, that do not always support sending custom authentication tokens, and piggyback on the Basic scheme which is widely supported.

To use this functionality with command line git, simply use _jwt as your username, and the JWT token as your password.

While we will not cover this as part of this tutorial, this functionality can be automated by configuring a git credentials helper for your Giftless base URL.

Summary

In this guide we have demonstrated one of Giftless’ built-in authentication and authorization modes - JWT tokens. We have covered how to configure Giftless to accept them, and demonstrated generating and using them from different client environments.

Next, you can: