A lightweight authentication service written in go
Authi is a lightweight authentication service written in go. It covers the basic use cases of creating and deleting users as well as refresh tokens and update passwords.
For a fast setup you can use the following guide:
.
├── docker-compose.yml
└── myTokenFolder
version: '3.7'
services:
postgres:
image: postgres:latest
container_name: postgres
restart: always
environment:
- POSTGRES_PASSWORD=myDatabasePassword
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
authi:
image: "beancodede/authi:latest"
container_name: authi
restart: always
environment:
- POSTGRES_PASSWORD=myDatabasePassword
ports:
- 1203:1203
volumes:
- ./myTokenFolder:/token
depends_on:
postgres:
condition: service_healthy
links:
- postgres:postgres
ssh-keygen -t rsa -b 4096 -m PEM -f ./myTokenFolder/jwtRS256.key
openssl rsa -in ./myTokenFolder/jwtRS256.key -pubout -outform PEM -out ./myTokenFolder/jwtRS256.key.pub
Note You need the cli programmes
ssh-keygen
andopenssl
in order to execute these commands
docker compose up
Note You need
docker
installed in order to execute this command
The application can be started with different environment variables to configure specific behavior.
Name of environment variable | Description | Mandatory | Default |
---|---|---|---|
LOG_LEVEL | Log level of console output. You can choose between debug, info, warn | ❌ | info |
ADDRESS | Server address on that Authi runs | ❌ | 0.0.0.0 |
PORT | Server port on that Authi runs | ❌ | 1203 |
PRIVATE_KEY_PATH | Path to the RSA private key file for signing jwt tokens | ❌ | /token/jwtRS256.key |
PUBLIC_KEY_PATH | Path to the RSA public key to validate jwt tokens | ❌ | /token/jwtRS256.key.pub |
DATABASE | Used database to store user data | ❌ | postgresql |
POSTGRES_USER | User of postgres database | ❌ | postgres |
POSTGRES_PASSWORD | Password of postgres database | ✔️ | - |
POSTGRES_DB | Database name that should be used in postgres | ❌ | postgres |
POSTGRES_HOST | Server address of Postgres database | ❌ | postgres |
POSTGRES_PORT | Server port oft Postgres database | ❌ | 5432 |
POSTGRES_OPTIONS | Connection options of Postgres database | ❌ | sslmode=disable |
ACCESS_TOKEN_EXPIRE_TIME | Time in minutes till an access token is no longer valid | ❌ | 5 |
REFRESH_TOKEN_EXPIRE_TIME | Time in minutes till an refresh token is no longer valid | ❌ | 10 |
INIT_USER_FILE | Path to the file with initial user | ❌ | authi.conf |
The offered api interfaces can be find in this Swagger UI or in the folder /docs.
To access the authi service from other go echo application, you can use the methods within the adapter package. Therefore two methods are provided:
GetToken(userId string, password string) (*TokenResponseDTO, error)
to get an access and refresh token with your userId and password.
RefreshToken(userId string, token string, refreshToken string) (*TokenResponseDTO, error)
to refresh your token for a logged in user without passing the password again.
Note You can only refresh tokens as long as your token and refresh token are valid.
To initial an authi adapter you have to use the method NewAuthiAdapter()
within the adapter package.
The whole code could look like this:
func AdapterExample() {
//User id that were previously created over REST
userId := "693227c8-4178-4e72-b3b7-a8b8bae36f1b"
//Generate UUID to trace your calls in the logs
correlationId := uuid.NewString()
//Initialize authi adapter
authiAdapter := adapter.NewAuthiAdapter(correlationId)
//Logging in previously created user with password `mySecretUserPassword`
token, err := authiAdapter.GetToken(userId, "mySecretUserPassword")
//Checking if an error occurred while loading user token
if err != nil {
panic(err)
}
//Printing access token for example
fmt.Println(token.AccessToken)
//Printing refresh token for example
fmt.Println(token.RefreshToken)
//Refreshing tokens to avoid outdated tokens
refreshedToken, err := authiAdapter.RefreshToken(userId, token.AccessToken, token.RefreshToken)
//Checking if an error occurred while loading refreshed token
if err != nil {
panic(err)
}
//Printing refreshed access token for example
fmt.Println(refreshedToken.AccessToken)
//Printing refreshed refresh token for example
fmt.Println(refreshedToken.RefreshToken)
}
If you write an echo go application, you also have the possibility to use the already attached middleware. Therefore you can orientate on the following example code:
func MiddlewareExample() {
//Initialize parser to validate Tokens
tokenParser, err := parser.NewJWTParser()
//Checking if an error occurred while loading jwt parser
if err != nil {
panic(err)
}
//Initialize middleware
echoMiddleware := middleware.NewEchoMiddleware(tokenParser)
//Initialize echo
e := echo.New()
//Secure endpoint with method `echoMiddleware.CheckToken`
e.GET(
"/someEndpoint",
func(c echo.Context) error { return c.NoContent(201) },
echoMiddleware.CheckToken,
)
}
Note In order for the code to work properly, the environment variable
PUBLIC_KEY_PATH
must point to the appropriate public key of the Authi server
While using the middleware the following errors could occur:
Error | Description | Instruction |
---|---|---|
ErrTokenNotFound | Token was not found in request | Does the passed token starts with "Bearer "; Is the token set in header Authorization inside the request |
ErrClaimCouldNotBeParsed | Token is valid but Claim format is unexpected | Are you using compatible version? Otherwise please create an issue |
ErrTokenNotValid | Token is not valid | A different key file may be used here for checking than is used in the Authi service. Otherwise, the token may have expired. |
ErrWhileReadingKey | Key file couldn't be read | Maybe the file is not on the correct position. Check your environment variable PUBLIC_KEY_PATH |
ErrWhileParsingKey | Key file couldn't be parsed | Maybe the file has not the correct format. Use the commands from Execute the following two commands to generate key's to generate key files |
To create users that are available immediately after starting the application, an init user config can be created. The default name of the file is authi.conf
and must be right next to the application. If a different file path is desired, this can be adjusted via the environment variable INIT_USER_FILE
.
The startup process deletes all initial users and then creates the users from the file. Thus only the users with the most recent data from the file should remain as initial users.
The configuration file can be written in JSON or YAML. Mandatory fields are the ID of the user, which must be in uuid V4 format, and the user's password. Below are two example configurations in YAML and JSON:
{
"users":[
{
"id": "c5ffc340-507e-4c66-a6ce-a7d98842f9ba",
"password":"someSecretPassword"
},
{
"id":"5cc3621d-e5ac-4d81-93df-462b27e0cc2b",
"password":"someOtherPassword"
}
]
}
users:
-
id: c5ffc340-507e-4c66-a6ce-a7d98842f9ba
password: someSecretPassword
-
id: 5cc3621d-e5ac-4d81-93df-462b27e0cc2b
password: someOtherPassword