The service implemented in the authorization-server
subfolder together with the Nginx instance defined in the nginx
subfolder of this repository makes possible to integrate Energy Web DID
solution (https://energy-web-foundation.gitbook.io/energy-web/foundational-concepts/self-sovereign-identity#decentralized-identifiers-dids)
into any REST service that requires this kind of user authentication to be added without changing its source code.
sequenceDiagram
participant client
participant nginx
participant auth proxy
participant PDA as passport-did-auth
client->>nginx: /auth/login
nginx->>auth proxy: /auth/login
auth proxy ->> PDA: identity token
PDA-->>auth proxy: access token
auth proxy->>auth proxy: generate new tokens pair
auth proxy-->> nginx: tokens pair
nginx-->>client: tokens pair
client->>nginx: /any/backend/path
nginx->>auth proxy: /auth/token-introspection
auth proxy-->>nginx: OK
nginx->>backend: /any/backend/path
backend-->>nginx: response
nginx-->>client: response
client->>client: continue working with the backend API
client->>client: detect access token expired
client->>nginx: /auth/refresh-token
nginx->>auth proxy: /auth/refresh-token
auth proxy->>auth proxy: generate new tokens pair
auth proxy-->> nginx: tokens pair
nginx-->>client: tokens pair
client->>client: continue working with the backend API
- nodejs
- yarn
- docker
- docker compose
- jq (https://stedolan.github.io/jq/download/) (for development and exploring)
yarn install
docker-compose -f docker-compose.dev.yaml up --build
cd authorization-server
Copy .env.example
file to .env
file
Edit .env
file and:
- set
JWT_SECRET
to contain your secret phrase used to generate and validate tokens. - set
ACCEPTED_ROLES
to contain roles that DIDs are required to be enrolled to
For the detailed env variables description check this document.
Execute yarn start:dev
Put a private key into the PRIVATE_KEY
env variable:
export PRIVATE_KEY=<your private key here>
Generate an identity token and store it in the IDENTITY_TOKEN
env variable:
export IDENTITY_TOKEN=$(node generate-identity-cli/index.js -p $PRIVATE_KEY -b 999999999999)
Now, you can request access and refresh tokens pair:
curl "http://localhost:8080/auth/login" \
-Ssf \
-X POST --header 'Content-Type: application/json' \
-d "{\"identityToken\": \"$IDENTITY_TOKEN\"}" \
| jq
You will see the following output:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImUxZGI1Y2EyLTNlNTUtNDc5NC04N2U4LWNmMDM2YTNjYjBjMCIsImRpZCI6ImRpZDpldGhyOjB4ODJGY0IzMTM4NUVhQmUyNjFFNGU2MDAzYjlGMkNiMmFmMzRlMjY1NCIsInJvbGVzIjpbInJvbGUxLnJvbGVzLmFwcC10ZXN0Mi5hcHBzLmFydHVyLmlhbS5ld2MiXSwiaWF0IjoxNjQ0OTIzMjk5LCJleHAiOjE2NDQ5MjMzMDl9.XFR4V76W_6Ox8-ocVNDSGBNTLpdBNdo5kU1gvpnovOs",
"type": "Bearer",
"expires_in": 9,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjFkMTQ0M2JkLWFkOTktNGZhZC04ZTYyLTVmOGVlMzI2MWQ5YiIsImRpZCI6ImRpZDpldGhyOjB4ODJGY0IzMTM4NUVhQmUyNjFFNGU2MDAzYjlGMkNiMmFmMzRlMjY1NCIsInJvbGVzIjpbInJvbGUxLnJvbGVzLmFwcC10ZXN0Mi5hcHBzLmFydHVyLmlhbS5ld2MiXSwiaWF0IjoxNjQ0OTIzMjk5LCJleHAiOjE2NDQ5MjM4OTl9.1n8TiG1cPSZEfJdj209TQylWqKyU2BDXHUX4loGyggU"
}
You can store generated access_token in the ACCESS_TOKEN
env variable to be used in requests to the actual REST API:
export ACCESS_TOKEN=$(curl "http://localhost:8080/auth/login" \
-Ssf \
-X POST --header 'Content-Type: application/json' \
-d "{\"identityToken\": \"$IDENTITY_TOKEN\"}" \
| jq -r .access_token)
Request an endpoint with the valid access token:
curl http://127.0.0.1:8080/any-path -H "Authorization: Bearer $ACCESS_TOKEN"
You will see the following response created by the backend service:
{"message":"backend response","timestamp":"2022-02-15T11:15:24.904Z"}
Request an endpoint with invalid token:
curl -v http://127.0.0.1:8080/ -H "Authorization: Bearer invalid-token"
You will see the following response generated by the NginX and 401
http response status code:
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>
After your access token expires, you need to regenerate it by providing your refresh token in the following request:
curl -X POST 'http://localhost:8080/auth/refresh-token' \
-H 'Content-Type: application/json' \
--data-raw '{
"refreshToken": "{{your refresh token here}}"
}'
You will get the same response as in case of /auth/login
endpoint if your refresh token has not expired yet.
To run this solution on production, you need to build docker images:
build:docker
Adjust docker-compose.yaml or create any other orchestrator configuration you use to:
- replace
backend
service with your REST API service. Probably, you will need to also adjustnginx/nginx.conf
file before building the images to contain hostname of your service if it is not defined by your orchestration solution - contain correct JWT_SECRET value
- contain correct ACCEPTED_ROLES value
- finetune any other env variables for
auth-server
service to meet your needs