This is a sample application for REST service development with golang. Libraries used:
- gorm as ORM library
- viper for configuration management
- gin as web framework
- gin-swagger to generate OpenAPI spec from go comments
- resty for REST client implementation (for example to talk to OPA)
- Open Policy Agent (OPA) for authorization decisions
- Google APIs Client Library for Go to validate Google JWT tokens
To build
docker build -t todo .
Docker-compose starts the built container with a database
docker-compose up
Go to Swagger UI http://localhost:8080/swagger/index.html
docker-compose down --volumes
docker-compose up db
opa build authz -o authz/bundle.tar.gz --ignore 'taskservice_authz_test.rego'
docker-compose up bundle_server
docker-compose up opa
To access the database:
$ docker exec -it todo_db_1 /bin/bash
root@187961c81d2e:/# psql -U postgres
psql (14.2 (Debian 14.2-1.pgdg110+1))
Type "help" for help.
go run main.go
Go to Swagger UI http://localhost:8080/swagger/index.html
go test ./...
Run unit tests
opa test authz -v --ignore '*.tar.gz'
Test rules on the server
echo "{\"input\": {\"method\":\"POST\",\"owner\":\"johndoe\",\"path\":[\"tasks\"],\"user\":\"johndoe\"}}" \
| http -v POST http://127.0.0.1:8181/v1/data/authz
swag init
For more see: https://github.com/swaggo/gin-swagger
When authn.notEnforced is true you can simply pass a user ID in the CallerId HTTP header.
If you set Authorizaton HTTP header in your calls, you must use the Bearer schema and include a valid Google OIDC ID token.
You can use Swagger UI to make a test call. To do this you will need to get an ID token issued by Google. (This demo supports only one identity provider, so it must be Google). The steps to get an ID token is described in the next section.
- Go to swagger UI http://localhost:8080/swagger/index.html and click on the "Authorize" button.
- Enter "Bearer [YOUR ID TOKEN]" to the text field and click on "Authorize"
and then click on "Close".
Now Swagger UI will add an Authorization header to all of your operations that have the "lock icon" (all the operations in our case).
- Try some API calls
Try to create a new task, by going to "POST /tasks/" and clicking on "Try out"
You will receive 403 in response, since with the default sample request you were trying to create a task for user "johndoe" and users are only permitted to create tasks for themselves. (See the authorization rules created in the earlier article.)
Now change the user to the email address of the user you signed in to retrieve the token.
This time your task will be created and you receive a 201 in your response.
- First you need to create OAuth client ID credentials. You can find documents online how to do this, for example https://developers.google.com/workspace/guides/create-credentials#oauth-client-id.
Once you are done you should have something similar to this
Make sure you add https://developers.google.com/oauthplayground as authorized redirect URI.
- Generate a token with Google's OAuth Playground
You can find documentation online to see how to do this, for example https://developers.google.com/google-ads/api/docs/oauth/playground, but here is brief summary.
Copy and paste your client ID and client secret from the previous step into the playground
Then enter "openid email" to the scopes and click on "Authorize APIs".
Then on the next page click on "Exchange authorization code for tokens"
On the last page you can find the id_token returned that you can use to authenticate with the service (see above).
Create database and set up IAM based authentication.
aws cloudformation validate-template --template-body file://deploy/cloudformation/database.yaml
aws cloudformation deploy --template-file deploy/cloudformation/database.yaml --stack-name todo-service-database
export PGPASSWORD=$( \
aws secretsmanager get-secret-value \
--secret-id "todo/postgres" \
--query "SecretString" \
--output text \
| jq -r .password)
HOST=$( \
aws secretsmanager get-secret-value \
--secret-id "todo/postgres" \
--query "SecretString" \
--output text \
| jq -r .host)
psql -h ${HOST} -U postgres -c "GRANT rds_iam TO postgres;"
unset PGPASSWORD
Then build and deploy the service.
cd deploy/aws-sam/todo-service
sam build
VPC=$(aws ec2 describe-vpcs --filters "Name=is-default,Values=true" --query "Vpcs[].VpcId" --output text)
SEC_GROUP=$(aws ec2 describe-security-groups --query "SecurityGroups[?VpcId=='${VPC}']".GroupId --output text)
SUBNETS=$(aws ec2 describe-subnets --query "Subnets[?VpcId=='${VPC}'].SubnetId" --output text | sed 's/\t/,/g')
DB_RESOURCE_ID=$(aws rds describe-db-clusters --db-cluster-identifier todo-service-database --query "DBClusters[].DbClusterResourceId" --output text)
sam deploy --stack-name todo-service \
--resolve-image-repos \
--resolve-s3 \
--capabilities CAPABILITY_IAM \
--parameter-overrides VpcSecurityGroupIds=${SEC_GROUP} VpcSubnetIds=${SUBNETS} TodoDbClusterResourceId=${DB_RESOURCE_ID}