auth-server

module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2023 License: MIT

README ΒΆ

auth-server

Go Reference Go Version Go Report Card

auth-server is a microservice that provides authentication and authorization using access and refresh tokens.

▢️ Features

  • High security β€” two types of tokens, asymmetric signing, fingerprint verification, password hashing and much more
  • Easy installation and startup β€” use Docker or Docker Compose
  • Flexible and clear configuration β€” environment variables or .env file (see Configuration)
  • Simple interaction β€” RESTful API to interact with other services (see Endpoints)

▢️ Installation and Startup

In production, the database must be running and migrations must be applied.

Migrations are located in the migrations directory.

πŸ–₯️ Locally

Create configuration file and specify its path instead of <config_file> in the following commands.

POSTGRES_URI must be set to URI of running PostgreSQL database. KEY_PRIVATE and KEY_PUBLIC must be set to generated keys paths.

go mod download
go build ./cmd/main.go
main -c <config_file>
🐳 Docker

Create configuration file and specify its path instead of <config_file> in the following commands.

Private and public keys are generated using the ECDSA algorithm when the image is built. There is no effect of changing KEY_PRIVATE, KEY_PUBLIC or AT_ALG.

POSTGRES_URI must be set to URI of running PostgreSQL database.

docker build -t auth-server .
docker run -p <host_port>:<app_port> --env-file <config_file> --name auth-web auth-server

You can copy generated keys to host in the following way:

$ docker ps
CONTAINER ID   IMAGE         COMMAND    CREATED         STATUS         PORTS                  NAMES
80fb44dc1638   auth-server   "./main"   4 seconds ago   Up 2 seconds   0.0.0.0:3000->80/tcp   auth-web

$ docker cp auth-web:/keys ./
Successfully copied 3.58kB

$ ls ./keys
ecdsa  ecdsa.pub
πŸ™ Docker Compose

As when running using Docker, private and public keys are generated when the image is built.

For development

Compose uses configs/docker.dev.env to configure web server for development. It also runs database server and applies migrations.

There is no effect of changing POSTGRES_URI.

docker compose -f docker-compose.dev.yaml up --build
For production

Compose uses configs/docker.prod.env to configure web server for production.

POSTGRES_URI must be changed to URI of running PostgreSQL database.

docker compose -f docker-compose.prod.yaml up --build

▢️ Configuration

Π‘onfiguration is performed using environment variables or .env file.

List of environment variables:

Variable Default Constraint Description
APP_NAME auth Application name used in the "iss" JWT claim
APP_ENV development Application environment
KEY_PUBLIC Path to public key encoded in PEM format
KEY_PRIVATE Path to private key encoded in PEM format
HTTP_HOST 0.0.0.0 Host for the server to listen on
HTTP_PORT 3000 Port for the server to listen on
HTTP_ORIGINS * Separated by comma List of origins a cross-domain request can be executed from
POSTGRES_URI PostgreSQL connection URI Database connection string in URI format
AT_ALG HS256 RFC7518, RFC8037 Algorithm used to sign the JWT
AT_AGE 15 1 β€” 60 Number of minutes until the access token expires
RT_CAP 10 > 1 Max number of refresh tokens per user until overwriting
RT_AGE 30 1 β€” 365 Number of days until the refresh token expires
BCRYPT_COST 4 4 β€” 31 Cost parameter of bcrypt algorithm used for password hashing

.env file example:

APP_NAME=auth
APP_ENV=development
KEY_PUBLIC=/secrets/ecdsa.pub
KEY_PRIVATE=/secrets/ecdsa
HTTP_HOST=0.0.0.0
HTTP_PORT=3000
HTTP_ORIGINS=https://*.example1.com,http://example2.com
POSTGRES_URI=postgres://postgres:test@localhost:5432/postgres?search_path=auth
AT_ALG=ES384
AT_AGE=15
RT_CAP=10
RT_AGE=30
BCRYPT_COST=10

▢️ Tokens

πŸ” Access token

Access token is a JSON Web Token (JWT) signed using one of the algorithms: HMAC SHA, RSA, ECDSA or EdDSA. Token contains a payload with two custom claims: fingerprint and roles.

Payload example:

{
  "fingerprint": "fb57b63a63bb4923031a191fa0abd37db24d8c56c6ba33d26ca34529a505eeab",
  "roles": ["admin"],
  "iss": "auth",
  "sub": "522198cc-42d9-4b47-b20e-1def58dc2709",
  "exp": 1687173288
}

Access token is created by auth-server and used by other microservices to authorize requests. Recipient microservice must parse token, check token issuer (iss) and expiration date (exp), compare fingerprint from payload with user fingerprint and optionally check roles.

Subject claim (sub) contains user ID.

Fingerprint is created from HTTP headers Sec-CH-UA, User-Agent, Accept-Language, Upgrade-Insecure-Requests and hashed using SHA-256 in the following way:

SHA256(Sec-CH-UA + ":" + User-Agent + ":" + Accept-Language + ":" + Upgrade-Insecure-Requests)

This repository also contains package jwt that provides Parser interface to parse access token:

import (
	"fmt"
 	
	"github.com/qsoulior/auth-server/pkg/jwt"
)

parser, err := jwt.NewParser(jwt.Params{issuer, alg, publicKey})
if err != nil {
	return err
}
claims, err := parser.Parse(token)
if err != nil {
	return err
}
fmt.Println(claims.Subject) // "522198cc-42d9-4b47-b20e-1def58dc2709"
♻️ Refresh token

Refresh token is stored in database and used to refresh the access token.

Refresh token entity:

type RefreshToken struct {
	ID          uuid.UUID `json:"id"`
	ExpiresAt   time.Time `json:"expires_at"`
	Fingerprint []byte    `json:"fingerprint"`
	Session     bool      `json:"session"`
	UserID      uuid.UUID `json:"-"`
}

This token is issued by the server upon successful authentication and is refreshed along with refresh of the access token. Client receives a cookie in response:

Set-Cookie: refresh_token=da5067f7-0235-4ca2-ab38-a650e44d7bbc; Path=/v1/token; Expires=Sat, 22 Jul 2023 16:35:36 GMT; HttpOnly; Secure; SameSite=None

▢️ Endpoints

πŸ’ Create user

POST /user

Request:

{
  "name": "test",
  "password": "Test123$"
}

Response:

201 Created
πŸ’ Get user

GET /user

Request:

Authorization: Bearer <access_token>

Response:

200 OK
{
  "id": "522198cc-42d9-4b47-b20e-1def58dc2709",
  "username": "test"
}
πŸ’ Update user password

PUT /user/password

Request:

Authorization: Bearer <access_token>
{
  "current_password": "Ttest123$",
  "new_password": "Ttest123$"
}

Response:

204 No Content
πŸ’ Delete user

DELETE /user

Request:

Authorization: Bearer <access_token>
{
  "password": "Ttest123$"
}

Response:

204 No Content
πŸ”‘ Create token

POST /token

Request:

{
  "name": "test",
  "password": "Test123$",
  "session": false
}

Response:

201 Created
Set-Cookie: refresh_token=da5067f7-0235-4ca2-ab38-a650e44d7bbc; Path=/v1/token; Expires=Sat, 22 Jul 2023 16:35:36 GMT; HttpOnly; Secure; SameSite=None
{
  "access_token": "<access_token>"
}
πŸ”‘ Refresh token

POST /token/refresh

Request:

Cookie: refresh_token=d337672c-d6e9-4058-b838-a634bbc5bddc

Response:

201 Created
Set-Cookie: refresh_token=da5067f7-0235-4ca2-ab38-a650e44d7bbc; Path=/v1/token; Expires=Sat, 22 Jul 2023 16:35:36 GMT; HttpOnly; Secure; SameSite=None
{
  "access_token": "<access_token>"
}
πŸ”‘ Revoke token

POST /token/revoke

Request:

Cookie: refresh_token=d337672c-d6e9-4058-b838-a634bbc5bddc

Response:

204 No Content
πŸ”‘ Revoke all tokens

POST /token/revoke-all

Request:

Cookie: refresh_token=d337672c-d6e9-4058-b838-a634bbc5bddc

Response:

204 No Content

Directories ΒΆ

Path Synopsis
Package main provides main function.
Package main provides main function.
internal
app
Package app provides structures and functions to configure and run application.
Package app provides structures and functions to configure and run application.
controller/http
Package http provides common structures and functions for HTTP controllers.
Package http provides common structures and functions for HTTP controllers.
controller/http/v1
Package v1 provides structures and functions to implement HTTP controllers.
Package v1 provides structures and functions to implement HTTP controllers.
entity
Package entity provides entity structures for use cases, repositories and controllers.
Package entity provides entity structures for use cases, repositories and controllers.
pkg/fingerprint
Package fingerprint provides structure to hash and verify fingerprint.
Package fingerprint provides structure to hash and verify fingerprint.
pkg/hash
Package hash provides structure to work with hex representation of hash.
Package hash provides structure to work with hex representation of hash.
repo
Package repo provides interfaces and structures to interact with database.
Package repo provides interfaces and structures to interact with database.
usecase
Package usecase provides interfaces and structures to encapsulate business logic.
Package usecase provides interfaces and structures to encapsulate business logic.
pkg
config
Package config provides functions to read env variables and set structure fields to variables.
Package config provides functions to read env variables and set structure fields to variables.
db
Package db provides structure to implement database connections.
Package db provides structure to implement database connections.
jwt
Package jwt provides structures and functions to build/parse JWT, read/parse public and private keys.
Package jwt provides structures and functions to build/parse JWT, read/parse public and private keys.
log
Package log provides structures for logging.
Package log provides structures for logging.
uuid
Package uuid provides structures to generate and inspect UUIDs.
Package uuid provides structures to generate and inspect UUIDs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL