Skip to content
Adam Chalkley edited this page Dec 1, 2020 · 2 revisions

Introduction

The project has a private.pem and public.pem file to allow the project to work out of the box with security. Please don't use these keys for your production system. The project has support for generating new private and public key files.

The project also has a bare minimum user system for creating users and generating JSON Web Tokens (JWTs). This document will walk you through the entire process of applying authentication and authorization to the project. All the support for security will be found in the Sales-Admin application.

Routes

Security is applied to routes in the Sales-Api application. This is done through the use of middleware.

app.Handle(http.MethodPost, "/v1/users", u.create, mid.Authenticate(authenticator), mid.HasRole(auth.RoleAdmin))
app.Handle(http.MethodGet, "/v1/users/:id", u.retrieve, mid.Authenticate(authenticator))

In the example above, routes require a JWT that is valid and the /v1/users route requires the Admin role. In the case of the /v1/users/:id route, extra logic is applied in the business layer.

func (u *user) retrieve(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
    claims, ok := ctx.Value(auth.Key).(auth.Claims)
    if !ok {
        return errors.New("claims missing from context")
    }

    params := web.Params(r)
    usr, err := data.Retrieve.User.One(ctx, claims, u.db, params["id"])
}

func (retUser) One(ctx context.Context, claims auth.Claims, db *sqlx.DB, userID string) (*User, error) {
    . . .

    // If you are not an admin and looking to retrieve someone other than yourself.
    if !claims.HasRole(auth.RoleAdmin) && claims.Subject != userID {
        return nil, ErrForbidden
    }

    . . .
}

This logic above restricts users to only retrieving information about themselves unless they are an admin. In this case, the claims are passed to the business layer for more refined security processing.

Generating Keys

Generating a set of public and private keys is the first thing you will want to do unless you plan to use the keys provided by the project.

To generate your own set of private and public key files, use the keygen command.

$ cd $HOME/code/service/cmd/sales-admin
$ go run main.go genkey

OUTPUT:
private and public key files generated

The result of running this command should be two new files in the current directory. Keep the private.pem file secure and out of reach to anyone who should not have it.

For production systems, you DO NOT want to store the private key with the project._

Starting The Database And Migrating The Schema

You must have the database started and the migration run before you can execute any user management functions. For now, use the project's key files until you learn more.

To start the database and run the migration, run these commands.

$ cd $HOME/code/service/
$ make up-local

OUTPUT:
docker run -it -d -p 5432:5432 postgres:11.1-alpine
c4b58ffa0944b74b002e883f85b54ce72287f73bff7f7d98933793acf5d5e76f


$ make migrate

OUTPUT:
migrations complete

Adding Users

Adding a user requires four piece of information: username, email, password, role. The current roles that will be used with the application are: ADMIN, MUTATE, QUERY.

$ cd cmd/sales-admin
$ go run main.go --db-disable-tls=1 useradd bill@ardanlabs.com gopher

OUTPUT:
user id: c2c8b7df-0e99-4f63-a78d-fb24dfc59256

Generating JWT

Generating a JWT requires a user to already exist in the database. This requires three pieces of information: user-email, private-key-file, algorithm. The algorithm choices are: RS256 or HA256. I recommend using RS256.

$ go run main.go --db-disable-tls=1 gentoken c2c8b7df-0e99-4f63-a78d-fb24dfc59256 ../../private.pem RS256

OUTPUT:
-----BEGIN TOKEN-----
eyJhbGciOiJSUzI1NiIsImtpZCI6IjU0YmIyMTY1LTcxZTEtNDFhNi1hZjNlLTdkYTRhMGUxZTJjMSIsInR5cC
I6IkpXVCJ9.eyJyb2xlcyI6WyJBRE1JTiIsIlVTRVIiXSwiZXhwIjoxNjIzNDI2NDg2LCJpYXQiOjE1OTE4OTA
0ODYsImlzcyI6InNlcnZpY2UgcHJvamVjdCIsInN1YiI6ImMyYzhiN2RmLTBlOTktNGY2My1hNzhkLWZiMjRkZ
mM1OTI1NiJ9.SvB_mPQ20IlA61qnYNybZT6EGxBiftGGJel8G0IaXVEaLqm5lhOw-HwMGlx06W7KsukZhX5AKU
OnhUHbtri6SaFjb8mlvoRbcxL8mDdgmu3m9saS0gtnewW37SjqmcHG88121v3iDzxCoWmeJ_jSvLdKtDD6Lq8R
jYeiHfFddtR0SSyzzUwslJxUWu-h8fkXDxJ4NXoIBVD7hi8F_SpxtUJiIYjK70jP2PETSOPYIsuMbofWegh6t7
16XigfXc8kn4-SYAs9bbBZ-Y6IVjMws4K1704hJf0FsZarlY5Q85u1MsGRzIJpWL-cyAD24IRPXPo8oF2any-e
jtr8w2fYlg
-----BEGIN TOKEN-----

To check if the JWT is valid, go to the JWT Website and copy/paste the token in the debugger.